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;