Author: Jan Christoph Ebersbach URL: http://dwm.suckless.org/patches/single_tagset This patch addresses the multi-monitor setup. Instead of having separate tags for every monitor there is just one list of tags for all monitors. Instead of moving windows from one monitor to the other, the desired tag from the other monitor can just be selected and all windows will be drawn on the current monitor. Several deep changes needed to be made: 1. Macro ISVISIBLE expects a second parameter, the monitor 2. Monitor->clients and Monitor->stack were moved to the global variable Clientlist cl. All monitors refer to this one list. 3. A new method attachclients was added. When changing between tags this function ensures that all clients are pointing to the right monitor. Please be aware that this patch probably breaks any other patch! diff -r ec4baab78314 dwm.c --- a/dwm.c Mon Dec 19 15:38:30 2011 +0100 +++ b/dwm.c Fri Apr 06 08:23:13 2012 +0200 @@ -45,7 +45,7 @@ #define CLEANMASK(mask) (mask & ~(numlockmask|LockMask) & (ShiftMask|ControlMask|Mod1Mask|Mod2Mask|Mod3Mask|Mod4Mask|Mod5Mask)) #define INTERSECT(x,y,w,h,m) (MAX(0, MIN((x)+(w),(m)->wx+(m)->ww) - MAX((x),(m)->wx)) \ * MAX(0, MIN((y)+(h),(m)->wy+(m)->wh) - MAX((y),(m)->wy))) -#define ISVISIBLE(C) ((C->tags & C->mon->tagset[C->mon->seltags])) +#define ISVISIBLE(C, M) ((C->tags & M->tagset[M->seltags])) #define LENGTH(X) (sizeof X / sizeof X[0]) #define MAX(A, B) ((A) > (B) ? (A) : (B)) #define MIN(A, B) ((A) < (B) ? (A) : (B)) @@ -124,6 +124,7 @@ void (*arrange)(Monitor *); } Layout; +typedef struct Clientlist Clientlist; struct Monitor { char ltsymbol[16]; float mfact; @@ -137,9 +138,8 @@ unsigned int tagset[2]; Bool showbar; Bool topbar; - Client *clients; + Clientlist *cl; Client *sel; - Client *stack; Monitor *next; Window barwin; const Layout *lt[2]; @@ -154,12 +154,18 @@ int monitor; } Rule; +struct Clientlist { + Client *clients; + Client *stack; +}; + /* function declarations */ static void applyrules(Client *c); static Bool applysizehints(Client *c, int *x, int *y, int *w, int *h, Bool interact); static void arrange(Monitor *m); static void arrangemon(Monitor *m); static void attach(Client *c); +static void attachclients(Monitor *m); static void attachstack(Client *c); static void buttonpress(XEvent *e); static void checkotherwm(void); @@ -202,7 +208,7 @@ static void monocle(Monitor *m); static void motionnotify(XEvent *e); static void movemouse(const Arg *arg); -static Client *nexttiled(Client *c); +static Client *nexttiled(Client *c, Monitor *m); static void pop(Client *); static void propertynotify(XEvent *e); static void quit(const Arg *arg); @@ -283,6 +289,7 @@ static DC dc; static Monitor *mons = NULL, *selmon = NULL; static Window root; +static Clientlist *cl; /* configuration, allows nested code to access above variables */ #include "config.h" @@ -313,7 +320,7 @@ { c->isfloating = r->isfloating; c->tags |= r->tags; - for(m = mons; m && m->num != r->monitor; m = m->next); + for(m = mons; m && (m->tagset[m->seltags] & c->tags) == 0; m = m->next) ; if(m) c->mon = m; } @@ -394,9 +401,9 @@ void arrange(Monitor *m) { if(m) - showhide(m->stack); + showhide(m->cl->stack); else for(m = mons; m; m = m->next) - showhide(m->stack); + showhide(m->cl->stack); if(m) arrangemon(m); else for(m = mons; m; m = m->next) @@ -413,14 +420,47 @@ void attach(Client *c) { - c->next = c->mon->clients; - c->mon->clients = c; + c->next = c->mon->cl->clients; + c->mon->cl->clients = c; +} + +void +attachclients(Monitor *m) { + /* attach clients to the specified monitor */ + Monitor *tm; + Client *c; + unsigned int utags = 0; + Bool rmons = False; + if(!m) + return; + + /* collect information about the tags in use */ + for(tm = mons; tm; tm = tm->next) + if(tm != m) + utags |= m->tagset[m->seltags]; + + for(c = m->cl->clients; c; c = c->next) + if(ISVISIBLE(c, m)) { + /* if client is also visible on other tags that are displayed on + * other monitors, remove these tags */ + if(c->tags & utags) { + c->tags = c->tags & m->tagset[m->seltags]; + rmons = True; + } + unfocus(c, True); + c->mon = m; + } + + if(rmons) + for(tm = mons; tm; tm = tm->next) + if(tm != m) + arrange(tm); } void attachstack(Client *c) { - c->snext = c->mon->stack; - c->mon->stack = c; + c->snext = c->mon->cl->stack; + c->mon->cl->stack = c; } void @@ -483,8 +523,8 @@ view(&a); selmon->lt[selmon->sellt] = &foo; for(m = mons; m; m = m->next) - while(m->stack) - unmanage(m->stack, False); + while(m->cl->stack) + unmanage(m->cl->stack, False); if(dc.font.set) XFreeFontSet(dpy, dc.font.set); else @@ -541,7 +581,7 @@ || (cme->data.l[0] == 2 /* _NET_WM_STATE_TOGGLE */ && !c->isfullscreen))); } else if(cme->message_type == netatom[NetActiveWindow]) { - if(!ISVISIBLE(c)) { + if(!ISVISIBLE(c, c->mon)) { c->mon->seltags ^= 1; c->mon->tagset[c->mon->seltags] = c->tags; } @@ -624,7 +664,7 @@ c->y = m->my + (m->mh / 2 - HEIGHT(c) / 2); /* center in y direction */ if((ev->value_mask & (CWX|CWY)) && !(ev->value_mask & (CWWidth|CWHeight))) configure(c); - if(ISVISIBLE(c)) + if(ISVISIBLE(c, m)) XMoveResizeWindow(dpy, c->win, c->x, c->y, c->w, c->h); } else @@ -645,11 +685,18 @@ Monitor * createmon(void) { - Monitor *m; + Monitor *m, *tm; + unsigned int i; if(!(m = (Monitor *)calloc(1, sizeof(Monitor)))) die("fatal: could not malloc() %u bytes\n", sizeof(Monitor)); - m->tagset[0] = m->tagset[1] = 1; + m->cl = cl; + /* reassing tags when creating a new monitor */ + for(i=1, tm = mons; tm; tm = tm->next, i++) { + tm->seltags ^= 1; + tm->tagset[tm->seltags] = i; + } + m->tagset[0] = m->tagset[1] = i; m->mfact = mfact; m->nmaster = nmaster; m->showbar = showbar; @@ -673,7 +720,7 @@ detach(Client *c) { Client **tc; - for(tc = &c->mon->clients; *tc && *tc != c; tc = &(*tc)->next); + for(tc = &c->mon->cl->clients; *tc && *tc != c; tc = &(*tc)->next); *tc = c->next; } @@ -681,11 +728,11 @@ detachstack(Client *c) { Client **tc, *t; - for(tc = &c->mon->stack; *tc && *tc != c; tc = &(*tc)->snext); + for(tc = &c->mon->cl->stack; *tc && *tc != c; tc = &(*tc)->snext); *tc = c->snext; if(c == c->mon->sel) { - for(t = c->mon->stack; t && !ISVISIBLE(t); t = t->snext); + for(t = c->mon->cl->stack; t && !ISVISIBLE(t, c->mon); t = t->snext); c->mon->sel = t; } } @@ -722,7 +769,7 @@ unsigned long *col; Client *c; - for(c = m->clients; c; c = c->next) { + for(c = m->cl->clients; c; c = c->next) { occ |= c->tags; if(c->isurgent) urg |= c->tags; @@ -842,8 +889,8 @@ void focus(Client *c) { - if(!c || !ISVISIBLE(c)) - for(c = selmon->stack; c && !ISVISIBLE(c); c = c->snext); + if(!c || !ISVISIBLE(c, selmon)) + for(c = selmon->cl->stack; c && !ISVISIBLE(c, selmon); c = c->snext); /* was if(selmon->sel) */ if(selmon->sel && selmon->sel != c) unfocus(selmon->sel, False); @@ -892,17 +939,17 @@ if(!selmon->sel) return; if(arg->i > 0) { - for(c = selmon->sel->next; c && !ISVISIBLE(c); c = c->next); + for(c = selmon->sel->next; c && !ISVISIBLE(c, selmon); c = c->next); if(!c) - for(c = selmon->clients; c && !ISVISIBLE(c); c = c->next); + for(c = selmon->cl->clients; c && !ISVISIBLE(c, selmon); c = c->next); } else { - for(i = selmon->clients; i != selmon->sel; i = i->next) - if(ISVISIBLE(i)) + for(i = selmon->cl->clients; i != selmon->sel; i = i->next) + if(ISVISIBLE(i, selmon)) c = i; if(!c) for(; i; i = i->next) - if(ISVISIBLE(i)) + if(ISVISIBLE(i, selmon)) c = i; } if(c) { @@ -1194,12 +1241,12 @@ unsigned int n = 0; Client *c; - for(c = m->clients; c; c = c->next) - if(ISVISIBLE(c)) + for(c = m->cl->clients; c; c = c->next) + if(ISVISIBLE(c, m)) 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)) + for(c = nexttiled(m->cl->clients, m); c; c = nexttiled(c->next, m)) resize(c, m->wx, m->wy, m->ww - 2 * c->bw, m->wh - 2 * c->bw, False); } @@ -1274,8 +1321,8 @@ } Client * -nexttiled(Client *c) { - for(; c && (c->isfloating || !ISVISIBLE(c)); c = c->next); +nexttiled(Client *c, Monitor *m) { + for(; c && (c->isfloating || !ISVISIBLE(c, m)); c = c->next); return c; } @@ -1425,8 +1472,8 @@ if(m->lt[m->sellt]->arrange) { wc.stack_mode = Below; wc.sibling = m->barwin; - for(c = m->stack; c; c = c->snext) - if(!c->isfloating && ISVISIBLE(c)) { + for(c = m->cl->stack; c; c = c->snext) + if(!c->isfloating && ISVISIBLE(c, m)) { XConfigureWindow(dpy, c->win, CWSibling|CWStackMode, &wc); wc.sibling = c->win; } @@ -1476,11 +1523,9 @@ if(c->mon == m) return; unfocus(c, True); - detach(c); detachstack(c); c->mon = m; c->tags = m->tagset[m->seltags]; /* assign tags of target monitor */ - attach(c); attachstack(c); focus(NULL); arrange(NULL); @@ -1594,6 +1639,8 @@ sw = DisplayWidth(dpy, screen); sh = DisplayHeight(dpy, screen); bh = dc.h = dc.font.height + 2; + if(!(cl = (Clientlist *)calloc(1, sizeof(Clientlist)))) + die("fatal: could not malloc() %u bytes\n", sizeof(Clientlist)); updategeom(); /* init atoms */ wmatom[WMProtocols] = XInternAtom(dpy, "WM_PROTOCOLS", False); @@ -1642,7 +1689,7 @@ showhide(Client *c) { if(!c) return; - if(ISVISIBLE(c)) { /* show clients top down */ + if(ISVISIBLE(c, c->mon)) { /* show clients top down */ XMoveWindow(dpy, c->win, c->x, c->y); if((!c->mon->lt[c->mon->sellt]->arrange || c->isfloating) && !c->isfullscreen) resize(c, c->x, c->y, c->w, c->h, False); @@ -1676,7 +1723,22 @@ void tag(const Arg *arg) { + Monitor *m; + unsigned int newtags; if(selmon->sel && arg->ui & TAGMASK) { + newtags = arg->ui & TAGMASK; + for(m = mons; m; m = m->next) + /* if tag is visible on another monitor, move client to the new monitor */ + if(m != selmon && m->tagset[m->seltags] & newtags) { + /* prevent moving client to all tags (MODKEY-Shift-0) when multiple monitors are connected */ + if(newtags & selmon->tagset[selmon->seltags]) + return; + selmon->sel->tags = newtags; + selmon->sel->mon = m; + arrange(m); + break; + } + /* workaround in case just one monitor is connected */ selmon->sel->tags = arg->ui & TAGMASK; focus(NULL); arrange(selmon); @@ -1706,7 +1768,7 @@ unsigned int i, n, h, mw, my, ty; Client *c; - for(n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++); + for(n = 0, c = nexttiled(m->cl->clients, m); c; c = nexttiled(c->next, m), n++); if(n == 0) return; @@ -1714,7 +1776,7 @@ 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++) + for(i = my = ty = 0, c = nexttiled(m->cl->clients, m); c; c = nexttiled(c->next, m), 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); @@ -1748,12 +1810,17 @@ void toggletag(const Arg *arg) { + Monitor *m; unsigned int newtags; if(!selmon->sel) return; newtags = selmon->sel->tags ^ (arg->ui & TAGMASK); if(newtags) { + /* prevent adding tags that are in use on other monitors */ + for(m = mons; m; m = m->next) + if(m != selmon && newtags & m->tagset[m->seltags]) + return; selmon->sel->tags = newtags; focus(NULL); arrange(selmon); @@ -1762,10 +1829,16 @@ void toggleview(const Arg *arg) { + Monitor *m; unsigned int newtagset = selmon->tagset[selmon->seltags] ^ (arg->ui & TAGMASK); if(newtagset) { + /* prevent displaying the same tags on multiple monitors */ + for(m = mons; m; m = m->next) + if(m != selmon && newtagset & m->tagset[m->seltags]) + return; selmon->tagset[selmon->seltags] = newtagset; + attachclients(selmon); focus(NULL); arrange(selmon); } @@ -1872,8 +1945,10 @@ if(n <= nn) { for(i = 0; i < (nn - n); i++) { /* new monitors available */ for(m = mons; m && m->next; m = m->next); - if(m) + if(m) { m->next = createmon(); + attachclients(m->next); + } else mons = createmon(); } @@ -1894,17 +1969,13 @@ else { /* less monitors available nn < n */ for(i = nn; i < n; i++) { for(m = mons; m && m->next; m = m->next); - while(m->clients) { - dirty = True; - c = m->clients; - m->clients = c->next; - detachstack(c); - c->mon = mons; - attach(c); - attachstack(c); - } if(m == selmon) selmon = mons; + for(c = m->cl->clients; c; c = c->next) { + dirty = True; + if(c->mon == m) + c->mon = selmon; + } cleanupmon(m); } } @@ -2043,11 +2114,31 @@ void view(const Arg *arg) { + Monitor *m; + unsigned int newtagset = selmon->tagset[selmon->seltags ^ 1]; if((arg->ui & TAGMASK) == selmon->tagset[selmon->seltags]) return; + + /* swap tags when trying to display a tag from another monitor */ + if(arg->ui & TAGMASK) + newtagset = arg->ui & TAGMASK; + for(m = mons; m; m = m->next) + if(m != selmon && newtagset & m->tagset[m->seltags]) { + /* prevent displaying all tags (MODKEY-0) when multiple monitors + * are connected */ + if(newtagset & selmon->tagset[selmon->seltags]) + return; + m->seltags ^= 1; + m->tagset[m->seltags] = selmon->tagset[selmon->seltags]; + attachclients(m); + arrange(m); + break; + } + selmon->seltags ^= 1; /* toggle sel tagset */ if(arg->ui & TAGMASK) selmon->tagset[selmon->seltags] = arg->ui & TAGMASK; + attachclients(selmon); focus(NULL); arrange(selmon); } @@ -2058,7 +2149,7 @@ Monitor *m; for(m = mons; m; m = m->next) - for(c = m->clients; c; c = c->next) + for(c = m->cl->clients; c; c = c->next) if(c->win == w) return c; return NULL; @@ -2120,8 +2211,8 @@ if(!selmon->lt[selmon->sellt]->arrange || (selmon->sel && selmon->sel->isfloating)) return; - if(c == nexttiled(selmon->clients)) - if(!c || !(c = nexttiled(c->next))) + if(c == nexttiled(selmon->cl->clients, selmon)) + if(!c || !(c = nexttiled(c->next, selmon))) return; pop(c); }