Hello,

After seeing lolilolicon's (canti on irc) master slave layout patch, I
had to have a go myself.  My patch is more free form, allowing the
user to set the master and slave layouts independent of each other on
the fly.  Traditional layouts are still possible by binding multiple
commands to the same key (as is shown in config.def.h).  There are a
few problems still with the patch.  I handle floating in a fairly ugly
fashion, if the master layout is floating, then the entire thing is
floating.  I don't show number of clients in a monocle layout yet.

I've included monocle, vertical stack, horizontal stack, and a simple
grid (it has holes).  Enjoy!

-Evan
diff -r 1228e3d45d25 config.def.h
--- a/config.def.h	Wed Nov 02 12:01:28 2011 +0000
+++ b/config.def.h	Sun Nov 06 09:28:12 2011 -0800
@@ -23,15 +23,18 @@
 };
 
 /* layout(s) */
+static const Bool vsplit      = True; /* True means vertical split between master/slave to start */
 static const float mfact      = 0.55; /* factor of master area size [0.05..0.95] */
 static const int nmaster      = 1;    /* number of clients in master area */
 static const Bool resizehints = True; /* True means respect size hints in tiled resizals */
 
 static const Layout layouts[] = {
 	/* symbol     arrange function */
-	{ "[]=",      tile },    /* first entry is default */
+	{ "==",       lt_stack_vert },    /* first entry is default */
+	{ "||",       lt_stack_horz },
+	{ "++",       lt_grid },
+	{ "[]",       lt_monocle },
 	{ "><>",      NULL },    /* no layout function means floating behavior */
-	{ "[M]",      monocle },
 };
 
 /* key definitions */
@@ -63,11 +66,32 @@
 	{ MODKEY,                       XK_Return, zoom,           {0} },
 	{ MODKEY,                       XK_Tab,    view,           {0} },
 	{ MODKEY|ShiftMask,             XK_c,      killclient,     {0} },
-	{ MODKEY,                       XK_t,      setlayout,      {.v = &layouts[0]} },
-	{ MODKEY,                       XK_f,      setlayout,      {.v = &layouts[1]} },
-	{ MODKEY,                       XK_m,      setlayout,      {.v = &layouts[2]} },
-	{ MODKEY,                       XK_space,  setlayout,      {0} },
-	{ MODKEY|ShiftMask,             XK_space,  togglefloating, {0} },
+	{ MODKEY|ShiftMask,             XK_t,      setmlayout,     {.v = &layouts[0]} },
+	{ MODKEY|ShiftMask,             XK_s,      setmlayout,     {.v = &layouts[1]} },
+	{ MODKEY|ShiftMask,             XK_g,      setmlayout,     {.v = &layouts[2]} },
+	{ MODKEY|ShiftMask,             XK_m,      setmlayout,     {.v = &layouts[3]} },
+	{ MODKEY|ShiftMask,             XK_space,  setmlayout,     {0} },
+	{ MODKEY|ControlMask,           XK_t,      setslayout,     {.v = &layouts[0]} },
+	{ MODKEY|ControlMask,           XK_s,      setslayout,     {.v = &layouts[1]} },
+	{ MODKEY|ControlMask,           XK_g,      setslayout,     {.v = &layouts[2]} },
+	{ MODKEY|ControlMask,           XK_m,      setslayout,     {.v = &layouts[3]} },
+	{ MODKEY|ControlMask,           XK_space,  setslayout,     {0} },
+	{ MODKEY,                       XK_v,      setsplit,       {.i = -1} },
+	{ MODKEY|ControlMask|ShiftMask, XK_space,  togglefloating, {0} },
+	/* traditional tile */
+	{ MODKEY,                       XK_t,      setmlayout,     {.v = &layouts[0]} },
+	{ MODKEY,                       XK_t,      setslayout,     {.v = &layouts[0]} },
+	{ MODKEY,                       XK_t,      setsplit,       {.i = 1} },
+	{ MODKEY,                       XK_t,      setnmaster,     {.ui = 1} },
+	/* traditional monocle */
+	{ MODKEY,                       XK_m,      setslayout,     {.v = &layouts[3]} },
+	{ MODKEY,                       XK_m,      setnmaster,     {.i = 0} },
+	/* floating */
+	{ MODKEY|ShiftMask,             XK_f,      setmlayout,     {.v = &layouts[4]} },
+	/* traditional "last layout" */
+	{ MODKEY,                       XK_space,  setmlayout,     {0} },
+	{ MODKEY,                       XK_space,  setslayout,     {0} },
+
 	{ MODKEY,                       XK_0,      view,           {.ui = ~0 } },
 	{ MODKEY|ShiftMask,             XK_0,      tag,            {.ui = ~0 } },
 	{ MODKEY,                       XK_comma,  focusmon,       {.i = -1 } },
@@ -90,8 +114,8 @@
 /* click can be ClkLtSymbol, ClkStatusText, ClkWinTitle, ClkClientWin, or ClkRootWin */
 static Button buttons[] = {
 	/* click                event mask      button          function        argument */
-	{ ClkLtSymbol,          0,              Button1,        setlayout,      {0} },
-	{ ClkLtSymbol,          0,              Button3,        setlayout,      {.v = &layouts[2]} },
+	{ ClkLtSymbol,          0,              Button1,        setmlayout,     {0} },
+	{ ClkLtSymbol,          0,              Button3,        setslayout,     {0} },
 	{ ClkWinTitle,          0,              Button2,        zoom,           {0} },
 	{ ClkStatusText,        0,              Button2,        spawn,          {.v = termcmd } },
 	{ ClkClientWin,         MODKEY,         Button1,        movemouse,      {0} },
diff -r 1228e3d45d25 dwm.c
--- a/dwm.c	Wed Nov 02 12:01:28 2011 +0000
+++ b/dwm.c	Sun Nov 06 09:28:12 2011 -0800
@@ -72,6 +72,10 @@
 } Arg;
 
 typedef struct {
+	unsigned int x, y, w, h;
+} Area;
+
+typedef struct {
 	unsigned int click;
 	unsigned int mask;
 	unsigned int button;
@@ -120,19 +124,20 @@
 
 typedef struct {
 	const char *symbol;
-	void (*arrange)(Monitor *);
+	void (*arrange)(Client **, unsigned int, Area *);
 } Layout;
 
 struct Monitor {
 	char ltsymbol[16];
 	float mfact;
 	int nmaster;
+	Bool vsplit; /* vertical separation between master/slave */
 	int num;
 	int by;               /* bar geometry */
 	int mx, my, mw, mh;   /* screen size */
 	int wx, wy, ww, wh;   /* window area  */
 	unsigned int seltags;
-	unsigned int sellt;
+	unsigned int selltm, sellts; /* sel lt master/slave */
 	unsigned int tagset[2];
 	Bool showbar;
 	Bool topbar;
@@ -141,7 +146,8 @@
 	Client *stack;
 	Monitor *next;
 	Window barwin;
-	const Layout *lt[2];
+	const Layout *ltm[2]; /* master */
+	const Layout *lts[2]; /* slave */
 };
 
 typedef struct {
@@ -195,10 +201,13 @@
 static void initfont(const char *fontstr);
 static void keypress(XEvent *e);
 static void killclient(const Arg *arg);
+static void lt_grid(Client **beg, unsigned int num, Area *a);
+static void lt_stack_vert(Client **beg, unsigned int num, Area *a);
+static void lt_stack_horz(Client **beg, unsigned int num, Area *a);
+static void lt_monocle(Client **beg, unsigned int num, Area *a);
 static void manage(Window w, XWindowAttributes *wa);
 static void mappingnotify(XEvent *e);
 static void maprequest(XEvent *e);
-static void monocle(Monitor *m);
 static void movemouse(const Arg *arg);
 static Client *nexttiled(Client *c);
 static void pop(Client *);
@@ -214,8 +223,11 @@
 static Bool sendevent(Client *c, Atom proto);
 static void sendmon(Client *c, Monitor *m);
 static void setclientstate(Client *c, long state);
+static void setnmaster(const Arg *arg);
 static void setfocus(Client *c);
-static void setlayout(const Arg *arg);
+static void setmlayout(const Arg *arg);
+static void setslayout(const Arg *arg);
+static void setsplit(const Arg *arg);
 static void setmfact(const Arg *arg);
 static void setup(void);
 static void showhide(Client *c);
@@ -224,7 +236,6 @@
 static void tag(const Arg *arg);
 static void tagmon(const Arg *arg);
 static int textnw(const char *text, unsigned int len);
-static void tile(Monitor *);
 static void togglebar(const Arg *arg);
 static void togglefloating(const Arg *arg);
 static void toggletag(const Arg *arg);
@@ -353,7 +364,7 @@
 		*h = bh;
 	if(*w < bh)
 		*w = bh;
-	if(resizehints || c->isfloating || !c->mon->lt[c->mon->sellt]->arrange) {
+	if(resizehints || c->isfloating || !c->mon->ltm[c->mon->selltm]->arrange) {
 		/* see last two sentences in ICCCM 4.1.2.3 */
 		baseismin = c->basew == c->minw && c->baseh == c->minh;
 		if(!baseismin) { /* temporarily remove base dimensions */
@@ -401,9 +412,32 @@
 
 void
 arrangemon(Monitor *m) {
-	strncpy(m->ltsymbol, m->lt[m->sellt]->symbol, sizeof m->ltsymbol);
-	if(m->lt[m->sellt]->arrange)
-		m->lt[m->sellt]->arrange(m);
+	unsigned int n;
+	Area a;
+	Client *c;
+
+	sprintf(m->ltsymbol, "%s%s%s", m->ltm[m->selltm]->symbol, m->vsplit ? "|" : "/", m->lts[m->sellts]->symbol);
+
+	for(n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++);
+	if(n == 0 || !m->ltm[m->selltm]->arrange)
+		return;
+
+	c = m->clients;
+
+	a.x = m->wx;
+	a.y = m->wy;
+	a.w = (n > m->nmaster) ? ( m->vsplit ? m->ww * m->mfact : m->ww) : m->ww;
+	a.h = (n > m->nmaster) ? (!m->vsplit ? m->wh * m->mfact : m->wh) : m->wh;
+
+	m->ltm[m->selltm]->arrange(&c, m->nmaster, &a); /* master */
+
+	a.x = (m->nmaster) ? ( m->vsplit ? a.x   + a.w : a.x) : m->wx;
+	a.y = (m->nmaster) ? (!m->vsplit ? a.y   + a.h : a.y) : m->wy;
+	a.w = (m->nmaster) ? ( m->vsplit ? m->ww - a.w : a.w) : m->ww;
+	a.h = (m->nmaster) ? (!m->vsplit ? m->wh - a.h : a.h) : m->wh;
+
+	m->lts[m->sellts]->arrange(&c, -1, &a); /* slave */
+
 	restack(m);
 }
 
@@ -477,7 +511,8 @@
 	Monitor *m;
 
 	view(&a);
-	selmon->lt[selmon->sellt] = &foo;
+	selmon->ltm[selmon->selltm] = &foo;
+	selmon->lts[selmon->sellts] = &foo;
 	for(m = mons; m; m = m->next)
 		while(m->stack)
 			unmanage(m->stack, False);
@@ -617,7 +652,7 @@
 	if((c = wintoclient(ev->window))) {
 		if(ev->value_mask & CWBorderWidth)
 			c->bw = ev->border_width;
-		else if(c->isfloating || !selmon->lt[selmon->sellt]->arrange) {
+		else if(c->isfloating || !selmon->ltm[selmon->selltm]->arrange) {
 			m = c->mon;
 			if(ev->value_mask & CWX) {
 				c->oldx = c->x;
@@ -668,12 +703,15 @@
 		die("fatal: could not malloc() %u bytes\n", sizeof(Monitor));
 	m->tagset[0] = m->tagset[1] = 1;
 	m->mfact = mfact;
+	m->vsplit = vsplit;
 	m->nmaster = nmaster;
 	m->showbar = showbar;
 	m->topbar = topbar;
-	m->lt[0] = &layouts[0];
-	m->lt[1] = &layouts[1 % LENGTH(layouts)];
-	strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol);
+	m->ltm[0] = &layouts[0];
+	m->lts[0] = &layouts[0];
+	m->ltm[1] = &layouts[1 % LENGTH(layouts)];
+	m->lts[1] = &layouts[1 % LENGTH(layouts)];
+	sprintf(m->ltsymbol, "%s%s%s", layouts[0].symbol, vsplit ? "|" : "/", layouts[0].symbol);
 	return m;
 }
 
@@ -1198,20 +1236,6 @@
 }
 
 void
-monocle(Monitor *m) {
-	unsigned int n = 0;
-	Client *c;
-
-	for(c = m->clients; c; c = c->next)
-		if(ISVISIBLE(c))
-			n++;
-	if(n > 0) /* override layout symbol */
-		snprintf(m->ltsymbol, sizeof m->ltsymbol, "[%d]", n);
-	for(c = nexttiled(m->clients); c; c = nexttiled(c->next))
-		resize(c, m->wx, m->wy, m->ww - 2 * c->bw, m->wh - 2 * c->bw, False);
-}
-
-void
 movemouse(const Arg *arg) {
 	int x, y, ocx, ocy, nx, ny;
 	Client *c;
@@ -1249,11 +1273,11 @@
 					ny = selmon->wy;
 				else if(abs((selmon->wy + selmon->wh) - (ny + HEIGHT(c))) < snap)
 					ny = selmon->wy + selmon->wh - HEIGHT(c);
-				if(!c->isfloating && selmon->lt[selmon->sellt]->arrange
+				if(!c->isfloating && selmon->ltm[selmon->selltm]->arrange
 				&& (abs(nx - c->x) > snap || abs(ny - c->y) > snap))
 					togglefloating(NULL);
 			}
-			if(!selmon->lt[selmon->sellt]->arrange || c->isfloating)
+			if(!selmon->ltm[selmon->selltm]->arrange || c->isfloating)
 				resize(c, nx, ny, c->w, c->h, True);
 			break;
 		}
@@ -1382,11 +1406,11 @@
 			if(c->mon->wx + nw >= selmon->wx && c->mon->wx + nw <= selmon->wx + selmon->ww
 			&& c->mon->wy + nh >= selmon->wy && c->mon->wy + nh <= selmon->wy + selmon->wh)
 			{
-				if(!c->isfloating && selmon->lt[selmon->sellt]->arrange
+				if(!c->isfloating && selmon->ltm[selmon->selltm]->arrange
 				&& (abs(nw - c->w) > snap || abs(nh - c->h) > snap))
 					togglefloating(NULL);
 			}
-			if(!selmon->lt[selmon->sellt]->arrange || c->isfloating)
+			if(!selmon->ltm[selmon->selltm]->arrange || c->isfloating)
 				resize(c, c->x, c->y, nw, nh, True);
 			break;
 		}
@@ -1410,9 +1434,9 @@
 	drawbar(m);
 	if(!m->sel)
 		return;
-	if(m->sel->isfloating || !m->lt[m->sellt]->arrange)
+	if(m->sel->isfloating || !m->ltm[m->selltm]->arrange)
 		XRaiseWindow(dpy, m->sel->win);
-	if(m->lt[m->sellt]->arrange) {
+	if(m->ltm[m->selltm]->arrange) {
 		wc.stack_mode = Below;
 		wc.sibling = m->barwin;
 		for(c = m->stack; c; c = c->snext)
@@ -1484,6 +1508,12 @@
 			PropModeReplace, (unsigned char *)data, 2);
 }
 
+void
+setnmaster(const Arg *arg) {
+	selmon->nmaster = MAX(arg->ui, 0);
+	arrange(selmon);
+}
+
 Bool
 sendevent(Client *c, Atom proto) {
 	int n;
@@ -1516,24 +1546,47 @@
 }
 
 void
-setlayout(const Arg *arg) {
-	if(!arg || !arg->v || arg->v != selmon->lt[selmon->sellt])
-		selmon->sellt ^= 1;
+setmlayout(const Arg *arg) {
+	if(!arg || !arg->v || arg->v != selmon->ltm[selmon->selltm])
+		selmon->selltm ^= 1;
 	if(arg && arg->v)
-		selmon->lt[selmon->sellt] = (Layout *)arg->v;
-	strncpy(selmon->ltsymbol, selmon->lt[selmon->sellt]->symbol, sizeof selmon->ltsymbol);
+		selmon->ltm[selmon->selltm] = (Layout *)arg->v;
+	sprintf(selmon->ltsymbol, "%s%s%s", selmon->ltm[selmon->selltm]->symbol, selmon->vsplit ? "|" : "/", selmon->lts[selmon->sellts]->symbol);
 	if(selmon->sel)
 		arrange(selmon);
 	else
 		drawbar(selmon);
 }
 
+void
+setslayout(const Arg *arg) {
+	if(!arg || !arg->v || arg->v != selmon->lts[selmon->sellts])
+		selmon->sellts ^= 1;
+	if(arg && arg->v)
+		selmon->lts[selmon->sellts] = (Layout *)arg->v;
+	sprintf(selmon->ltsymbol, "%s%s%s", selmon->ltm[selmon->selltm]->symbol, selmon->vsplit ? "|" : "/", selmon->lts[selmon->sellts]->symbol);
+	if(selmon->sel)
+		arrange(selmon);
+	else
+		drawbar(selmon);
+}
+
+void
+setsplit(const Arg *arg) {
+	switch (arg->i) {
+		case 0: selmon->vsplit = False; break;
+		case 1: selmon->vsplit = True; break;
+		case -1: selmon->vsplit = !selmon->vsplit; break;
+	}
+	arrange(selmon);
+}
+
 /* arg > 1.0 will set mfact absolutly */
 void
 setmfact(const Arg *arg) {
 	float f;
 
-	if(!arg || !selmon->lt[selmon->sellt]->arrange)
+	if(!arg || !selmon->ltm[selmon->selltm]->arrange)
 		return;
 	f = arg->f < 1.0 ? arg->f + selmon->mfact : arg->f - 1.0;
 	if(f < 0.1 || f > 0.9)
@@ -1607,7 +1660,7 @@
 		return;
 	if(ISVISIBLE(c)) { /* show clients top down */
 		XMoveWindow(dpy, c->win, c->x, c->y);
-		if((!c->mon->lt[c->mon->sellt]->arrange || c->isfloating) && !c->isfullscreen)
+		if((!c->mon->ltm[c->mon->selltm]->arrange || c->isfloating) && !c->isfullscreen)
 			resize(c, c->x, c->y, c->w, c->h, False);
 		showhide(c->snext);
 	}
@@ -1665,29 +1718,80 @@
 }
 
 void
-tile(Monitor *m) {
-	unsigned int i, n, h, mw, my, ty;
+lt_grid(Client **beg, unsigned int num, Area *a) {
+	unsigned int i, n, x, y, w, h, rows, cols;
 	Client *c;
 
-	for(n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++);
+	for(n = 0, c = nexttiled(*beg); c; c = nexttiled(c->next), n++);
 	if(n == 0)
 		return;
 
-	if(n > m->nmaster)
-		mw = m->nmaster ? m->ww * m->mfact : 0;
-	else
-		mw = m->ww;
-	for(i = my = ty = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++)
-		if(i < m->nmaster) {
-			h = (m->wh - my) / (MIN(n, m->nmaster) - i);
-			resize(c, m->wx, m->wy + my, mw - (2*c->bw), h - (2*c->bw), False);
-			my += HEIGHT(c);
-		}
-		else {
-			h = (m->wh - ty) / (n - i);
-			resize(c, m->wx + mw, m->wy + ty, m->ww - mw - (2*c->bw), h - (2*c->bw), False);
-			ty += HEIGHT(c);
-		}
+	n = (num >= 0) ? MIN(n, num) : n;
+
+	for(cols = 0; cols * cols < n; cols++);
+	rows = n / cols + !!(n % cols);
+
+	w = a->w / cols;
+	h = a->h / rows;
+
+	for(i = 0, c = nexttiled(*beg); c && i != num; c = nexttiled(c->next), i++) {
+		x = a->x + i / rows * w;
+		y = a->y + i % rows * h;
+		resize(c, x, y, w - (2*c->bw), h - (2*c->bw), False);
+	}
+
+	*beg = c;
+}
+
+void
+lt_stack_vert(Client **beg, unsigned int num, Area *a) {
+	unsigned int i, n, y, h;
+	Client *c;
+
+	for(n = 0, c = nexttiled(*beg); c; c = nexttiled(c->next), n++);
+	if(n == 0)
+		return;
+
+	n = (num >= 0) ? MIN(n, num) : n;
+
+	for(i = y = 0, c = nexttiled(*beg); c && i != num; c = nexttiled(c->next), i++) {
+		h = (a->h - y) / (n - i);
+		resize(c, a->x, a->y + y, a->w - (2*c->bw), h - (2*c->bw), False);
+		y += HEIGHT(c);
+	}
+
+	*beg = c;
+}
+
+void
+lt_stack_horz(Client **beg, unsigned int num, Area *a) {
+	unsigned int i, n, x, w;
+	Client *c;
+
+	for(n = 0, c = nexttiled(*beg); c; c = nexttiled(c->next), n++);
+	if(n == 0)
+		return;
+
+	n = (num >= 0) ? MIN(n, num) : n;
+
+	for(i = x = 0, c = nexttiled(*beg); c && i != num; c = nexttiled(c->next), i++) {
+		w = (a->w - x) / (n - i);
+		resize(c, a->x + x, a->y, w - (2*c->bw), a->h - (2*c->bw), False);
+		x += WIDTH(c);
+	}
+
+	*beg = c;
+}
+
+void
+lt_monocle(Client **beg, unsigned int num, Area *a) {
+	unsigned int i;
+	Client *c;
+
+	for(i = 0, c = nexttiled(*beg); c && i != num; c = nexttiled(c->next), i++)
+		resize(c, a->x, a->y, a->w - (2*c->bw), a->h - (2*c->bw), False);
+
+	*beg = c;
 }
 
 void
@@ -2087,7 +2191,7 @@
 zoom(const Arg *arg) {
 	Client *c = selmon->sel;
 
-	if(!selmon->lt[selmon->sellt]->arrange
+	if(!selmon->ltm[selmon->selltm]->arrange
 	|| (selmon->sel && selmon->sel->isfloating))
 		return;
 	if(c == nexttiled(selmon->clients))

Reply via email to