Here's a first attempt at working Xinerama for dwm. It has rough edges
(and probably sharp corners), but perhaps someone else will find it
interesting and provide feedback.

The current code seems very confused about the difference between
Zaphod and Xinerama. I cleaned this up only where absolutely necessary
so far, but this has probably made Zaphod mode support more difficult
than it was.

Tags are per-display rather than per-monitor. This fit more naturally
with the way I work (tested by playing with awesome and xmonad),
though it is somewhat arbitrary. If two monitors display the same tag
then the client appears on the lowest numbered monitor (again
arbitrary).

There are changes to config.def.h - don't forget to merge them into
your config.h as appropriate.

I'll do more work on this, but wanted to get early feedback.

Comments?

dme.
diff --git a/config.def.h b/config.def.h
--- a/config.def.h
+++ b/config.def.h
@@ -15,11 +15,11 @@ const char tags[][MAXTAGLEN] = { "1", "2
 const char tags[][MAXTAGLEN] = { "1", "2", "3", "4", "5", "6", "7", "8", "www" };
 Bool initags[LENGTH(tags)] = {[0] = True};
 Rule rules[] = {
-	/* class:instance:title regex	tags regex	isfloating */	/* monitor */
-	{ "Firefox",			"www",		False,		-1 },
-	{ "Gimp",			NULL,		True,		-1 },
-	{ "MPlayer",			NULL,		True,		-1 },
-	{ "Acroread",			NULL,		True,		-1 },
+	/* class:instance:title regex	tags regex	isfloating */
+	{ "Firefox",			"www",		False },
+	{ "Gimp",			NULL,		True },
+	{ "MPlayer",			NULL,		True },
+	{ "Acroread",			NULL,		True },
 };
 
 /* layout(s) */
diff --git a/config.mk b/config.mk
--- a/config.mk
+++ b/config.mk
@@ -15,10 +15,10 @@ LIBS = -L/usr/lib -lc -L${X11LIB} -lX11 
 LIBS = -L/usr/lib -lc -L${X11LIB} -lX11 -lXinerama
 
 # flags
-CFLAGS = -Os ${INCS} -DVERSION=\"${VERSION}\"
-LDFLAGS = -s ${LIBS}
-#CFLAGS = -g -std=c99 -pedantic -Wall -O2 ${INCS} -DVERSION=\"${VERSION}\"
-#LDFLAGS = -g ${LIBS}
+#CFLAGS = -Os ${INCS} -DVERSION=\"${VERSION}\"
+#LDFLAGS = -s ${LIBS}
+CFLAGS = -g -std=c99 -pedantic -Wall -O2 ${INCS} -DVERSION=\"${VERSION}\"
+LDFLAGS = -g ${LIBS}
 
 # Solaris
 #CFLAGS = -fast ${INCS} -DVERSION=\"${VERSION}\"
diff --git a/dwm.c b/dwm.c
--- a/dwm.c
+++ b/dwm.c
@@ -75,6 +75,7 @@ struct Client {
 	Client *snext;
 	Window win;
 	int monitor;
+	int generation;
 };
 
 typedef struct {
@@ -99,16 +100,16 @@ typedef struct {
 	const char *arg;
 } Key;
 
+typedef struct Monitor Monitor;
 typedef struct {
 	const char *symbol;
-	void (*arrange)(void);
+	void (*arrange)(Monitor *m);
 } Layout;
 
 typedef struct {
 	const char *prop;
 	const char *tags;
 	Bool isfloating;
-	int monitor;
 } Rule;
 
 typedef struct {
@@ -116,7 +117,8 @@ typedef struct {
 	regex_t *tagregex;
 } Regs;
 
-typedef struct {
+struct Monitor {
+	int nth;
 	int screen;
 	Window root;
 	Window barwin;
@@ -126,7 +128,7 @@ typedef struct {
 	Bool *prevtags;
 	Layout *layout;
 	double mwfact;
-} Monitor;
+};
 
 /* function declarations */
 void applyrules(Client *c);
@@ -151,7 +153,7 @@ void enternotify(XEvent *e);
 void enternotify(XEvent *e);
 void eprint(const char *errstr, ...);
 void expose(XEvent *e);
-void floating(void); /* default floating layout */
+void floating(Monitor*); /* default floating layout */
 void focus(Client *c);
 void focusin(XEvent *e);
 void focusnext(const char *arg);
@@ -164,7 +166,7 @@ void grabkeys(void);
 void grabkeys(void);
 unsigned int idxoftag(const char *tag);
 void initfont(Monitor*, const char *fontstr);
-Bool isoccupied(Monitor *m, unsigned int t);
+Bool isoccupied(unsigned int t);
 Bool isprotodel(Client *c);
 Bool isvisible(Client *c, Monitor *m);
 void keypress(XEvent *e);
@@ -190,7 +192,7 @@ void tag(const char *arg);
 void tag(const char *arg);
 unsigned int textnw(Monitor*, const char *text, unsigned int len);
 unsigned int textw(Monitor*, const char *text);
-void tile(void);
+void tile(Monitor*);
 void togglebar(const char *arg);
 void togglefloating(const char *arg);
 void toggletag(const char *arg);
@@ -253,6 +255,17 @@ int selmonitor = 0;
 
 //Bool prevtags[LENGTH(tags)];
 
+void
+dump_tagset(char *s, Bool *t)
+{
+	unsigned int i;
+
+	printf("%s: ", s);
+	for(i = 0; i < LENGTH(tags); i++)
+		printf("%s ", t[i] ? "t" : "f");
+	printf("\n");
+}
+
 /* function implementations */
 void
 applyrules(Client *c) {
@@ -260,7 +273,6 @@ applyrules(Client *c) {
 	unsigned int i, j;
 	regmatch_t tmp;
 	Bool matched_tag = False;
-	Bool matched_monitor = False;
 	XClassHint ch = { 0 };
 
 	/* rule matching */
@@ -270,11 +282,6 @@ applyrules(Client *c) {
 			ch.res_name ? ch.res_name : "", c->name);
 	for(i = 0; i < LENGTH(rules); i++)
 		if(regs[i].propregex && !regexec(regs[i].propregex, buf, 1, &tmp, 0)) {
-			if (rules[i].monitor >= 0 && rules[i].monitor < mcount) {
-				matched_monitor = True;
-				c->monitor = rules[i].monitor;
-			}
-
 			c->isfloating = rules[i].isfloating;
 			for(j = 0; regs[i].tagregex && j < LENGTH(tags); j++) {
 				if(!regexec(regs[i].tagregex, tags[j], 1, &tmp, 0)) {
@@ -289,21 +296,36 @@ applyrules(Client *c) {
 		XFree(ch.res_name);
 	if(!matched_tag)
 		memcpy(c->tags, monitors[monitorat(-1, -1)].seltags, sizeof initags);
-	if (!matched_monitor)
-		c->monitor = monitorat(-1, -1);
 }
 
 void
 arrange(void) {
+	static int generation = 0;
+	int m;
 	Client *c;
-
-	for(c = clients; c; c = c->next)
-		if(isvisible(c, &monitors[c->monitor]))
-			unban(c);
-		else
-			ban(c);
-
-	monitors[selmonitor].layout->arrange();
+	Monitor *mon;
+
+	generation++;
+
+	for(m = 0, mon = &monitors[0]; m < mcount; m++, mon++) {
+
+		for(c = clients; c; c = c->next) {
+			if (c->generation != generation) {
+				/* This client not already placed. */
+				if (isvisible(c, mon)) {
+					c->generation = generation;
+					c->monitor = m;
+					unban(c);
+				}
+				else {
+					c->monitor = -1; /* XXX dme: risky? */
+					ban(c);
+				}
+			}
+		}
+		mon->layout->arrange(mon);
+	}
+
 	focus(NULL);
 	restack();
 }
@@ -414,6 +436,7 @@ cleanup(void) {
 	}
 	for(i = 0; i < mcount; i++) {
 		Monitor *m = &monitors[i];
+
 		if(m->dc.font.set)
 			XFreeFontSet(dpy, m->dc.font.set);
 		else
@@ -422,12 +445,12 @@ cleanup(void) {
 		XFreePixmap(dpy, m->dc.drawable);
 		XFreeGC(dpy, m->dc.gc);
 		XDestroyWindow(dpy, m->barwin);
-		XFreeCursor(dpy, cursor[CurNormal]);
-		XFreeCursor(dpy, cursor[CurResize]);
-		XFreeCursor(dpy, cursor[CurMove]);
-		XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime);
-		XSync(dpy, False);
-	}
+	}
+	XFreeCursor(dpy, cursor[CurNormal]);
+	XFreeCursor(dpy, cursor[CurResize]);
+	XFreeCursor(dpy, cursor[CurMove]);
+	XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime);
+	XSync(dpy, False);
 }
 
 void
@@ -571,14 +594,16 @@ drawbar(void) {
 		Monitor *m = &monitors[i];
 		m->dc.x = 0;
 		for(j = 0; j < LENGTH(tags); j++) {
+			Bool occupied = isoccupied(j);
+
 			m->dc.w = textw(m, tags[j]);
 			if(m->seltags[j]) {
 				drawtext(m, tags[j], m->dc.sel);
-				drawsquare(m, sel && sel->tags[j] && sel->monitor == selmonitor, isoccupied(m, j), m->dc.sel);
+				drawsquare(m, sel && sel->tags[j] && sel->monitor == m->nth, occupied, m->dc.sel);
 			}
 			else {
 				drawtext(m, tags[j], m->dc.norm);
-				drawsquare(m, sel && sel->tags[j] && sel->monitor == selmonitor, isoccupied(m, j), m->dc.norm);
+				drawsquare(m, sel && sel->tags[j] && sel->monitor == m->nth, occupied, m->dc.norm);
 			}
 			m->dc.x += m->dc.w;
 		}
@@ -594,13 +619,13 @@ drawbar(void) {
 		drawtext(m, stext, m->dc.norm);
 		if((m->dc.w = m->dc.x - x) > bh) {
 			m->dc.x = x;
-			if(sel && sel->monitor == selmonitor) {
+			if(sel && sel->monitor == m->nth) {
 				drawtext(m, sel->name, m->dc.sel);
 				drawsquare(m, False, sel->isfloating, m->dc.sel);
 			}
 			else
-				drawtext(m, NULL, m->dc.norm);
-		}
+				drawtext(m, NULL, m->dc.norm);	
+	}
 		XCopyArea(dpy, m->dc.drawable, m->barwin, m->dc.gc, 0, 0, m->sw, bh, 0, 0);
 		XSync(dpy, False);
 	}
@@ -717,12 +742,12 @@ expose(XEvent *e) {
 }
 
 void
-floating(void) { /* default floating layout */
+floating(Monitor *m) { /* default floating layout */
 	Client *c;
 
 	domwfact = dozoom = False;
 	for(c = clients; c; c = c->next)
-		if(isvisible(c, &monitors[selmonitor]))
+		if(isvisible(c, m))
 			resize(c, c->x, c->y, c->w, c->h, True);
 }
 
@@ -976,11 +1001,11 @@ initfont(Monitor *m, const char *fontstr
 }
 
 Bool
-isoccupied(Monitor *m, unsigned int t) {
+isoccupied(unsigned int t) {
 	Client *c;
 
 	for(c = clients; c; c = c->next)
-		if(c->tags[t] && c->monitor == selmonitor)
+		if(c->tags[t])
 			return True;
 	return False;
 }
@@ -1005,7 +1030,7 @@ isvisible(Client *c, Monitor *m) {
 	unsigned int i;
 
 	for(i = 0; i < LENGTH(tags); i++)
-		if(c->tags[i] && monitors[c->monitor].seltags[i] && c->monitor == selmonitor)
+		if(c->tags[i] && m->seltags[i])
 			return True;
 	return False;
 }
@@ -1049,7 +1074,7 @@ void
 void
 manage(Window w, XWindowAttributes *wa) {
 	Client *c, *t = NULL;
-	Monitor *m = &monitors[selmonitor];
+	Monitor *m;
 	Status rettrans;
 	Window trans;
 	XWindowChanges wc;
@@ -1057,6 +1082,9 @@ manage(Window w, XWindowAttributes *wa) 
 	c = emallocz(sizeof(Client));
 	c->tags = emallocz(sizeof initags);
 	c->win = w;
+
+	c->monitor = selmonitor; /* XXX dme: should use current position rather than selected monitor? */
+	m = &monitors[c->monitor];
 
 	applyrules(c);
 
@@ -1178,7 +1206,7 @@ movemouse(Client *c) {
 
 Client *
 nexttiled(Client *c, Monitor *m) {
-	for(; c && (c->isfloating || !isvisible(c, m)); c = c->next);
+	for(; c && (c->isfloating || (c->monitor != m->nth)); c = c->next);
 	return c;
 }
 
@@ -1428,32 +1456,30 @@ run(void) {
 
 void
 scan(void) {
-	unsigned int i, j, num;
+	unsigned int j, num;
 	Window *wins, d1, d2;
 	XWindowAttributes wa;
-
-	for(i = 0; i < mcount; i++) {
-		Monitor *m = &monitors[i];
-		wins = NULL;
-		if(XQueryTree(dpy, m->root, &d1, &d2, &wins, &num)) {
-			for(j = 0; j < num; j++) {
-				if(!XGetWindowAttributes(dpy, wins[j], &wa)
-				|| wa.override_redirect || XGetTransientForHint(dpy, wins[j], &d1))
-					continue;
-				if(wa.map_state == IsViewable || getstate(wins[j]) == IconicState)
-					manage(wins[j], &wa);
-			}
-			for(j = 0; j < num; j++) { /* now the transients */
-				if(!XGetWindowAttributes(dpy, wins[j], &wa))
-					continue;
-				if(XGetTransientForHint(dpy, wins[j], &d1)
-				&& (wa.map_state == IsViewable || getstate(wins[j]) == IconicState))
-					manage(wins[j], &wa);
-			}
-		}
-		if(wins)
-			XFree(wins);
-	}
+	Monitor *m = &monitors[0];
+
+	wins = NULL;
+	if(XQueryTree(dpy, m->root, &d1, &d2, &wins, &num)) {
+		for(j = 0; j < num; j++) {
+			if(!XGetWindowAttributes(dpy, wins[j], &wa)
+			   || wa.override_redirect || XGetTransientForHint(dpy, wins[j], &d1))
+				continue;
+			if(wa.map_state == IsViewable || getstate(wins[j]) == IconicState)
+				manage(wins[j], &wa);
+		}
+		for(j = 0; j < num; j++) { /* now the transients */
+			if(!XGetWindowAttributes(dpy, wins[j], &wa))
+				continue;
+			if(XGetTransientForHint(dpy, wins[j], &d1)
+			   && (wa.map_state == IsViewable || getstate(wins[j]) == IconicState))
+				manage(wins[j], &wa);
+		}
+	}
+	if(wins)
+		XFree(wins);
 }
 
 void
@@ -1536,17 +1562,18 @@ setup(void) {
 	if (XineramaIsActive(dpy)) {
 		info = XineramaQueryScreens(dpy, &mcount);
 	}
-	mcount = 1;
 	monitors = emallocz(mcount * sizeof(Monitor));
 
 	for(i = 0; i < mcount; i++) {
 		/* init geometry */
 		m = &monitors[i];
-
-		m->screen = i;
-		m->root = RootWindow(dpy, i);
-
-		if (mcount != 1) { // TODO: Xinerama
+		m->nth = i;
+
+		m->screen = DefaultScreen(dpy);
+		m->root = RootWindow(dpy, m->screen);
+
+		if (info) {
+			/* Xinerama */
 			m->sx = info[i].x_org;
 			m->sy = info[i].y_org;
 			m->sw = info[i].width;
@@ -1566,12 +1593,12 @@ setup(void) {
 		memcpy(m->prevtags, initags, sizeof initags);
 
 		/* init appearance */
-		m->dc.norm[ColBorder] = getcolor(NORMBORDERCOLOR, i);
-		m->dc.norm[ColBG] = getcolor(NORMBGCOLOR, i);
-		m->dc.norm[ColFG] = getcolor(NORMFGCOLOR, i);
-		m->dc.sel[ColBorder] = getcolor(SELBORDERCOLOR, i);
-		m->dc.sel[ColBG] = getcolor(SELBGCOLOR, i);
-		m->dc.sel[ColFG] = getcolor(SELFGCOLOR, i);
+		m->dc.norm[ColBorder] = getcolor(NORMBORDERCOLOR, m->screen);
+		m->dc.norm[ColBG] = getcolor(NORMBGCOLOR, m->screen);
+		m->dc.norm[ColFG] = getcolor(NORMFGCOLOR, m->screen);
+		m->dc.sel[ColBorder] = getcolor(SELBORDERCOLOR, m->screen);
+		m->dc.sel[ColBG] = getcolor(SELBGCOLOR, m->screen);
+		m->dc.sel[ColFG] = getcolor(SELFGCOLOR, m->screen);
 		initfont(m, FONT);
 		m->dc.h = bh = m->dc.font.height + 2;
 
@@ -1675,54 +1702,51 @@ textw(Monitor *m, const char *text) {
 }
 
 void
-tile(void) {
-	unsigned int i, j, n, nx, ny, nw, nh, mw, th;
+tile(Monitor *m) {
+	unsigned int j, n, nx, ny, nw, nh, mw, th;
 	Client *c, *mc;
 
 	domwfact = dozoom = True;
 
-	nw = 0; /* gcc stupidity requires this */
-
-	for (i = 0; i < mcount; i++) {
-		Monitor *m = &monitors[i];
-
-		for(n = 0, c = nexttiled(clients, m); c; c = nexttiled(c->next, m))
-			n++;
-
-		for(j = 0, c = mc = nexttiled(clients, m); c; c = nexttiled(c->next, m)) {
-			/* window geoms */
-			mw = (n == 1) ? m->waw : m->mwfact * m->waw;
-			th = (n > 1) ? m->wah / (n - 1) : 0;
-			if(n > 1 && th < bh)
-				th = m->wah;
-			if(j == 0) { /* master */
-				nx = m->wax;
+	nw = nx = ny = 0; /* gcc stupidity requires this */
+
+	for(n = 0, c = nexttiled(clients, m); c; c = nexttiled(c->next, m))
+		n++;
+
+	for(j = 0, c = mc = nexttiled(clients, m); c; c = nexttiled(c->next, m)) {
+		/* window geoms */
+		mw = (n == 1) ? m->waw : m->mwfact * m->waw;
+		th = (n > 1) ? m->wah / (n - 1) : 0;
+		if(n > 1 && th < bh)
+			th = m->wah;
+		if(j == 0) { /* master */
+			nx = m->wax;
+			ny = m->way;
+			nw = mw - 2 * c->border;
+			nh = m->wah - 2 * c->border;
+		}
+		else {  /* tile window */
+			if(j == 1) {
 				ny = m->way;
-				nw = mw - 2 * c->border;
-				nh = m->wah - 2 * c->border;
+				nx += mc->w + 2 * mc->border;
+				nw = m->waw - mw - 2 * c->border;
 			}
-			else {  /* tile window */
-				if(j == 1) {
-					ny = m->way;
-					nx += mc->w + 2 * mc->border;
-					nw = m->waw - mw - 2 * c->border;
-				}
-				if(j + 1 == n) /* remainder */
-					nh = (m->way + m->wah) - ny - 2 * c->border;
-				else
-					nh = th - 2 * c->border;
-			}
-			resize(c, nx, ny, nw, nh, RESIZEHINTS);
-			if((RESIZEHINTS) && ((c->h < bh) || (c->h > nh) || (c->w < bh) || (c->w > nw)))
-				/* client doesn't accept size constraints */
-				resize(c, nx, ny, nw, nh, False);
-			if(n > 1 && th != m->wah)
-				ny = c->y + c->h + 2 * c->border;
-
-			j++;
-		}
-	}
-}
+			if(j + 1 == n) /* remainder */
+				nh = (m->way + m->wah) - ny - 2 * c->border;
+			else
+				nh = th - 2 * c->border;
+		}
+		resize(c, nx, ny, nw, nh, RESIZEHINTS);
+		if((RESIZEHINTS) && ((c->h < bh) || (c->h > nh) || (c->w < bh) || (c->w > nw)))
+			/* client doesn't accept size constraints */
+			resize(c, nx, ny, nw, nh, False);
+		if(n > 1 && th != m->wah)
+			ny = c->y + c->h + 2 * c->border;
+
+		j++;
+	}
+}
+
 void
 togglebar(const char *arg) {
 	if(bpos == BarOff)
@@ -1976,7 +2000,6 @@ monitorat(int x, int y) {
 monitorat(int x, int y) {
 	int i;
 
-	return 0;
 	if(!XineramaIsActive(dpy))
 		return 0;
 

Reply via email to