From 7c6ad58007d403c0f45043259227179a01c3dca4 Mon Sep 17 00:00:00 2001 From: BrunoCooper17 Date: Sun, 14 Nov 2021 13:13:12 -0600 Subject: [PATCH] single tagset for dwm source a786211 20211015 Description 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: Macro ISVISIBLE expects a second parameter, the monitor Monitor->clients and Monitor->stack were moved to the global variable Clientlist cl. All monitors refer to this one list. A new method attachclients was added. When changing between tags this function ensures that all clients are pointing to the right monitor. This patch also add _NET_WM_DESKTOP attribute Authors Jan Christoph Ebersbach - jceb@e-jc.de Mohammad Zeinali - mzeinali@tutanota.com Jesus Mastache Caballero - --- dwm.c | 243 ++++++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 187 insertions(+), 56 deletions(-) diff --git a/dwm.c b/dwm.c index 5e4d494..b93b071 100644 --- a/dwm.c +++ b/dwm.c @@ -20,6 +20,7 @@ * * To understand everything else, start reading main(). */ +#include #include #include #include @@ -49,7 +50,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 MOUSEMASK (BUTTONMASK|PointerMotionMask) #define WIDTH(X) ((X)->w + 2 * (X)->bw) @@ -62,7 +63,7 @@ enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */ enum { SchemeNorm, SchemeSel }; /* color schemes */ enum { NetSupported, NetWMName, NetWMState, NetWMCheck, NetWMFullscreen, NetActiveWindow, NetWMWindowType, - NetWMWindowTypeDialog, NetClientList, NetLast }; /* EWMH atoms */ + NetWMWindowTypeDialog, NetClientList, NetWMDesktop, NetLast }; /* EWMH atoms */ enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */ enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, ClkClientWin, ClkRootWin, ClkLast }; /* clicks */ @@ -82,6 +83,7 @@ typedef struct { const Arg arg; } Button; +typedef struct Clientlist Clientlist; typedef struct Monitor Monitor; typedef struct Client Client; struct Client { @@ -124,9 +126,8 @@ struct Monitor { unsigned int tagset[2]; int showbar; int topbar; - Client *clients; + Clientlist *cl; Client *sel; - Client *stack; Monitor *next; Window barwin; const Layout *lt[2]; @@ -141,12 +142,18 @@ typedef struct { int monitor; } Rule; +struct Clientlist { + Client *clients; + Client *stack; +}; + /* function declarations */ static void applyrules(Client *c); static int applysizehints(Client *c, int *x, int *y, int *w, int *h, int 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); @@ -184,7 +191,7 @@ static void maprequest(XEvent *e); 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); @@ -226,6 +233,7 @@ static void updatesizehints(Client *c); static void updatestatus(void); static void updatetitle(Client *c); static void updatewindowtype(Client *c); +static void updatewmdesktop(Window w, int tag); static void updatewmhints(Client *c); static void view(const Arg *arg); static Client *wintoclient(Window w); @@ -268,6 +276,7 @@ static Display *dpy; static Drw *drw; static Monitor *mons, *selmon; static Window root, wmcheckwin; +static Clientlist *cl; /* configuration, allows nested code to access above variables */ #include "config.h" @@ -300,7 +309,7 @@ applyrules(Client *c) { 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; } @@ -382,9 +391,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); restack(m); @@ -396,6 +405,10 @@ void arrangemon(Monitor *m) { strncpy(m->ltsymbol, m->lt[m->sellt]->symbol, sizeof m->ltsymbol); + if (m->cl) + for (Client* c = m->cl->clients; c; c = c->next) + updatewmdesktop(c->win, c->tags); + if (m->lt[m->sellt]->arrange) m->lt[m->sellt]->arrange(m); } @@ -403,15 +416,49 @@ arrangemon(Monitor *m) 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 |= tm->tagset[tm->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 @@ -478,8 +525,8 @@ cleanup(void) view(&a); selmon->lt[selmon->sellt] = &foo; for (m = mons; m; m = m->next) - while (m->stack) - unmanage(m->stack, 0); + while (m->cl->stack) + unmanage(m->cl->stack, 0); XUngrabKey(dpy, AnyKey, AnyModifier, root); while (mons) cleanupmon(mons); @@ -565,7 +612,7 @@ configurenotify(XEvent *e) drw_resize(drw, sw, bh); updatebars(); 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->isfullscreen) resizeclient(c, m->mx, m->my, m->mw, m->mh); XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, m->ww, bh); @@ -611,7 +658,7 @@ configurerequest(XEvent *e) 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 configure(c); @@ -631,10 +678,31 @@ configurerequest(XEvent *e) Monitor * createmon(void) { - Monitor *m; + Monitor *m, *tm; + int i; + /* bail out if the number of monitors exceeds the number of tags */ + for (i=1, tm=mons; tm; i++, tm=tm->next); + if (i > LENGTH(tags)) { + fprintf(stderr, "dwm: failed to add monitor, number of tags exceeded\n"); + return NULL; + } + /* find the first tag that isn't in use */ + for (i=0; i < LENGTH(tags); i++) { + for (tm=mons; tm && !(tm->tagset[tm->seltags] & (1<next); + if (!tm) + break; + } + /* reassign all tags to monitors since there's currently no free tag for the + * new monitor */ + if (i >= LENGTH(tags)) + for (i=0, tm=mons; tm; tm=tm->next, i++) { + tm->seltags ^= 1; + tm->tagset[tm->seltags] = (1<tagset[0] = m->tagset[1] = 1; + m->cl = cl; + m->tagset[0] = m->tagset[1] = (1<mfact = mfact; m->nmaster = nmaster; m->showbar = showbar; @@ -660,7 +728,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; } @@ -669,11 +737,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; } } @@ -709,7 +777,7 @@ drawbar(Monitor *m) drw_text(drw, m->ww - tw, 0, tw, bh, 0, stext, 0); } - 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; @@ -784,8 +852,8 @@ expose(XEvent *e) 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); if (selmon->sel && selmon->sel != c) unfocus(selmon->sel, 0); if (c) { @@ -838,16 +906,16 @@ focusstack(const Arg *arg) if (!selmon->sel || (selmon->sel->isfullscreen && lockfullscreen)) 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) { @@ -1107,12 +1175,12 @@ monocle(Monitor *m) 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, 0); } @@ -1194,9 +1262,9 @@ movemouse(const Arg *arg) } Client * -nexttiled(Client *c) +nexttiled(Client *c, Monitor *m) { - for (; c && (c->isfloating || !ISVISIBLE(c)); c = c->next); + for (; c && (c->isfloating || !ISVISIBLE(c, m)); c = c->next); return c; } @@ -1360,8 +1428,8 @@ restack(Monitor *m) 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; } @@ -1414,11 +1482,9 @@ sendmon(Client *c, Monitor *m) if (c->mon == m) return; unfocus(c, 1); - 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); @@ -1541,6 +1607,8 @@ setup(void) screen = DefaultScreen(dpy); sw = DisplayWidth(dpy, screen); sh = DisplayHeight(dpy, screen); + if(!(cl = (Clientlist *)calloc(1, sizeof(Clientlist)))) + die("fatal: could not malloc() %u bytes\n", sizeof(Clientlist)); root = RootWindow(dpy, screen); drw = drw_create(dpy, screen, root, sw, sh); if (!drw_fontset_create(drw, fonts, LENGTH(fonts))) @@ -1559,6 +1627,7 @@ setup(void) netatom[NetWMName] = XInternAtom(dpy, "_NET_WM_NAME", False); netatom[NetWMState] = XInternAtom(dpy, "_NET_WM_STATE", False); netatom[NetWMCheck] = XInternAtom(dpy, "_NET_SUPPORTING_WM_CHECK", False); + netatom[NetWMDesktop] = XInternAtom(dpy, "_NET_WM_DESKTOP", False); netatom[NetWMFullscreen] = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False); netatom[NetWMWindowType] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False); netatom[NetWMWindowTypeDialog] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DIALOG", False); @@ -1616,7 +1685,7 @@ showhide(Client *c) { if (!c) return; - if (ISVISIBLE(c)) { + 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) @@ -1656,7 +1725,23 @@ spawn(const Arg *arg) 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); @@ -1677,7 +1762,7 @@ tile(Monitor *m) 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; @@ -1685,7 +1770,7 @@ tile(Monitor *m) 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), 0); @@ -1725,12 +1810,17 @@ togglefloating(const Arg *arg) 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); @@ -1740,12 +1830,27 @@ toggletag(const Arg *arg) 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; - focus(NULL); + attachclients(selmon); arrange(selmon); + focus(NULL); + + unsigned int newtagset = selmon->tagset[selmon->seltags] ^ (arg->ui & TAGMASK); + + if (newtagset) { + selmon->tagset[selmon->seltags] = newtagset; + attachclients(selmon); + arrange(selmon); + focus(NULL); + } } } @@ -1844,7 +1949,7 @@ updateclientlist() XDeleteProperty(dpy, root, netatom[NetClientList]); for (m = mons; m; m = m->next) - for (c = m->clients; c; c = c->next) + for (c = m->cl->clients; c; c = c->next) XChangeProperty(dpy, root, netatom[NetClientList], XA_WINDOW, 32, PropModeAppend, (unsigned char *) &(c->win), 1); @@ -1874,8 +1979,10 @@ updategeom(void) if (n <= nn) { /* new monitors available */ for (i = 0; i < (nn - n); i++) { for (m = mons; m && m->next; m = m->next); - if (m) + if (m) { m->next = createmon(); + attachclients(m->next); + } else mons = createmon(); } @@ -1895,16 +2002,13 @@ updategeom(void) } else { /* less monitors available nn < n */ for (i = nn; i < n; i++) { for (m = mons; m && m->next; m = m->next); - while ((c = m->clients)) { - dirty = 1; - 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); } } @@ -2016,6 +2120,14 @@ updatewindowtype(Client *c) c->isfloating = 1; } +void updatewmdesktop(Window w, int tag) { + long data[] = { tag }; + int i=0; + while(*data >> (i+1)) { i++; } + data[0] = i; + XChangeProperty(dpy, w, netatom[NetWMDesktop], XA_CARDINAL, 32, PropModeReplace, (unsigned char *)data, 1); +} + void updatewmhints(Client *c) { @@ -2038,13 +2150,32 @@ updatewmhints(Client *c) 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->sel = selmon->sel; + 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; - focus(NULL); + attachclients(selmon); arrange(selmon); + focus(NULL); } Client * @@ -2054,7 +2185,7 @@ wintoclient(Window w) 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; @@ -2121,8 +2252,8 @@ zoom(const Arg *arg) 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); } -- 2.33.1