dwm-single_tagset-20210623-67d76bd.diff (16552B)
1 From 116830e90487d53cbe44c05a8c0ffe8985e1509e Mon Sep 17 00:00:00 2001 2 From: BrunoCooper17 <BrunoCooper17@outlook.com> 3 Date: Tue, 22 Jun 2021 22:31:37 -0500 4 Subject: [PATCH] An updated single_tagset patch for dwm-6.2 5 6 Description 7 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. 8 9 Several deep changes needed to be made: 10 11 Macro ISVISIBLE expects a second parameter, the monitor 12 Monitor->clients and Monitor->stack were moved to the global variable Clientlist cl. All monitors refer to this one list. 13 A new method attachclients was added. When changing between tags this function ensures that all clients are pointing to the right monitor. 14 15 Authors 16 Jan Christoph Ebersbach - jceb@e-jc.de 17 Mohammad Zeinali - mzeinali@tutanota.com 18 Jesus Mastache Caballero - <BrunoCooper17@outlook.com> 19 20 --- 21 dwm.c | 226 ++++++++++++++++++++++++++++++++++++++++++++-------------- 22 1 file changed, 171 insertions(+), 55 deletions(-) 23 24 diff --git a/dwm.c b/dwm.c 25 index b0b3466..10d7f6e 100644 26 --- a/dwm.c 27 +++ b/dwm.c 28 @@ -49,7 +49,7 @@ 29 #define CLEANMASK(mask) (mask & ~(numlockmask|LockMask) & (ShiftMask|ControlMask|Mod1Mask|Mod2Mask|Mod3Mask|Mod4Mask|Mod5Mask)) 30 #define INTERSECT(x,y,w,h,m) (MAX(0, MIN((x)+(w),(m)->wx+(m)->ww) - MAX((x),(m)->wx)) \ 31 * MAX(0, MIN((y)+(h),(m)->wy+(m)->wh) - MAX((y),(m)->wy))) 32 -#define ISVISIBLE(C) ((C->tags & C->mon->tagset[C->mon->seltags])) 33 +#define ISVISIBLE(C, M) ((C->tags & M->tagset[M->seltags])) 34 #define LENGTH(X) (sizeof X / sizeof X[0]) 35 #define MOUSEMASK (BUTTONMASK|PointerMotionMask) 36 #define WIDTH(X) ((X)->w + 2 * (X)->bw) 37 @@ -82,6 +82,7 @@ typedef struct { 38 const Arg arg; 39 } Button; 40 41 +typedef struct Clientlist Clientlist; 42 typedef struct Monitor Monitor; 43 typedef struct Client Client; 44 struct Client { 45 @@ -124,9 +125,8 @@ struct Monitor { 46 unsigned int tagset[2]; 47 int showbar; 48 int topbar; 49 - Client *clients; 50 + Clientlist *cl; 51 Client *sel; 52 - Client *stack; 53 Monitor *next; 54 Window barwin; 55 const Layout *lt[2]; 56 @@ -141,12 +141,18 @@ typedef struct { 57 int monitor; 58 } Rule; 59 60 +struct Clientlist { 61 + Client *clients; 62 + Client *stack; 63 +}; 64 + 65 /* function declarations */ 66 static void applyrules(Client *c); 67 static int applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact); 68 static void arrange(Monitor *m); 69 static void arrangemon(Monitor *m); 70 static void attach(Client *c); 71 +static void attachclients(Monitor *m); 72 static void attachstack(Client *c); 73 static void buttonpress(XEvent *e); 74 static void checkotherwm(void); 75 @@ -184,7 +190,7 @@ static void maprequest(XEvent *e); 76 static void monocle(Monitor *m); 77 static void motionnotify(XEvent *e); 78 static void movemouse(const Arg *arg); 79 -static Client *nexttiled(Client *c); 80 +static Client *nexttiled(Client *c, Monitor *m); 81 static void pop(Client *); 82 static void propertynotify(XEvent *e); 83 static void quit(const Arg *arg); 84 @@ -268,6 +274,7 @@ static Display *dpy; 85 static Drw *drw; 86 static Monitor *mons, *selmon; 87 static Window root, wmcheckwin; 88 +static Clientlist *cl; 89 90 /* configuration, allows nested code to access above variables */ 91 #include "config.h" 92 @@ -300,7 +307,7 @@ applyrules(Client *c) 93 { 94 c->isfloating = r->isfloating; 95 c->tags |= r->tags; 96 - for (m = mons; m && m->num != r->monitor; m = m->next); 97 + for (m = mons; m && (m->tagset[m->seltags] & c->tags) == 0; m = m->next); 98 if (m) 99 c->mon = m; 100 } 101 @@ -382,9 +389,9 @@ void 102 arrange(Monitor *m) 103 { 104 if (m) 105 - showhide(m->stack); 106 + showhide(m->cl->stack); 107 else for (m = mons; m; m = m->next) 108 - showhide(m->stack); 109 + showhide(m->cl->stack); 110 if (m) { 111 arrangemon(m); 112 restack(m); 113 @@ -403,15 +410,49 @@ arrangemon(Monitor *m) 114 void 115 attach(Client *c) 116 { 117 - c->next = c->mon->clients; 118 - c->mon->clients = c; 119 + c->next = c->mon->cl->clients; 120 + c->mon->cl->clients = c; 121 +} 122 + 123 +void 124 +attachclients(Monitor *m) { 125 + /* attach clients to the specified monitor */ 126 + Monitor *tm; 127 + Client *c; 128 + unsigned int utags = 0; 129 + Bool rmons = False; 130 + if(!m) 131 + return; 132 + 133 + /* collect information about the tags in use */ 134 + for (tm = mons; tm; tm = tm->next) 135 + if(tm != m) 136 + utags |= tm->tagset[tm->seltags]; 137 + 138 + for (c = m->cl->clients; c; c = c->next) 139 + if(ISVISIBLE(c, m)) { 140 + /* if client is also visible on other tags that are displayed on 141 + * other monitors, remove these tags */ 142 + if(c->tags & utags) { 143 + c->tags = c->tags & m->tagset[m->seltags]; 144 + rmons = True; 145 + } 146 + unfocus(c, True); 147 + c->mon = m; 148 + } 149 + 150 + if (rmons) 151 + for (tm = mons; tm; tm = tm->next) 152 + if(tm != m) 153 + arrange(tm); 154 + 155 } 156 157 void 158 attachstack(Client *c) 159 { 160 - c->snext = c->mon->stack; 161 - c->mon->stack = c; 162 + c->snext = c->mon->cl->stack; 163 + c->mon->cl->stack = c; 164 } 165 166 void 167 @@ -478,8 +519,8 @@ cleanup(void) 168 view(&a); 169 selmon->lt[selmon->sellt] = &foo; 170 for (m = mons; m; m = m->next) 171 - while (m->stack) 172 - unmanage(m->stack, 0); 173 + while (m->cl->stack) 174 + unmanage(m->cl->stack, 0); 175 XUngrabKey(dpy, AnyKey, AnyModifier, root); 176 while (mons) 177 cleanupmon(mons); 178 @@ -565,7 +606,7 @@ configurenotify(XEvent *e) 179 drw_resize(drw, sw, bh); 180 updatebars(); 181 for (m = mons; m; m = m->next) { 182 - for (c = m->clients; c; c = c->next) 183 + for (c = m->cl->clients; c; c = c->next) 184 if (c->isfullscreen) 185 resizeclient(c, m->mx, m->my, m->mw, m->mh); 186 XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, m->ww, bh); 187 @@ -611,7 +652,7 @@ configurerequest(XEvent *e) 188 c->y = m->my + (m->mh / 2 - HEIGHT(c) / 2); /* center in y direction */ 189 if ((ev->value_mask & (CWX|CWY)) && !(ev->value_mask & (CWWidth|CWHeight))) 190 configure(c); 191 - if (ISVISIBLE(c)) 192 + if (ISVISIBLE(c, m)) 193 XMoveResizeWindow(dpy, c->win, c->x, c->y, c->w, c->h); 194 } else 195 configure(c); 196 @@ -631,10 +672,31 @@ configurerequest(XEvent *e) 197 Monitor * 198 createmon(void) 199 { 200 - Monitor *m; 201 + Monitor *m, *tm; 202 + int i; 203 204 + /* bail out if the number of monitors exceeds the number of tags */ 205 + for (i=1, tm=mons; tm; i++, tm=tm->next); 206 + if (i > LENGTH(tags)) { 207 + fprintf(stderr, "dwm: failed to add monitor, number of tags exceeded\n"); 208 + return NULL; 209 + } 210 + /* find the first tag that isn't in use */ 211 + for (i=0; i < LENGTH(tags); i++) { 212 + for (tm=mons; tm && !(tm->tagset[tm->seltags] & (1<<i)); tm=tm->next); 213 + if (!tm) 214 + break; 215 + } 216 + /* reassign all tags to monitors since there's currently no free tag for the 217 + * new monitor */ 218 + if (i >= LENGTH(tags)) 219 + for (i=0, tm=mons; tm; tm=tm->next, i++) { 220 + tm->seltags ^= 1; 221 + tm->tagset[tm->seltags] = (1<<i) & TAGMASK; 222 + } 223 m = ecalloc(1, sizeof(Monitor)); 224 - m->tagset[0] = m->tagset[1] = 1; 225 + m->cl = cl; 226 + m->tagset[0] = m->tagset[1] = (1<<i) & TAGMASK; 227 m->mfact = mfact; 228 m->nmaster = nmaster; 229 m->showbar = showbar; 230 @@ -660,7 +722,7 @@ detach(Client *c) 231 { 232 Client **tc; 233 234 - for (tc = &c->mon->clients; *tc && *tc != c; tc = &(*tc)->next); 235 + for (tc = &c->mon->cl->clients; *tc && *tc != c; tc = &(*tc)->next); 236 *tc = c->next; 237 } 238 239 @@ -669,11 +731,11 @@ detachstack(Client *c) 240 { 241 Client **tc, *t; 242 243 - for (tc = &c->mon->stack; *tc && *tc != c; tc = &(*tc)->snext); 244 + for (tc = &c->mon->cl->stack; *tc && *tc != c; tc = &(*tc)->snext); 245 *tc = c->snext; 246 247 if (c == c->mon->sel) { 248 - for (t = c->mon->stack; t && !ISVISIBLE(t); t = t->snext); 249 + for (t = c->mon->cl->stack; t && !ISVISIBLE(t, c->mon); t = t->snext); 250 c->mon->sel = t; 251 } 252 } 253 @@ -709,7 +771,7 @@ drawbar(Monitor *m) 254 drw_text(drw, m->ww - tw, 0, tw, bh, 0, stext, 0); 255 } 256 257 - for (c = m->clients; c; c = c->next) { 258 + for (c = m->cl->clients; c; c = c->next) { 259 occ |= c->tags; 260 if (c->isurgent) 261 urg |= c->tags; 262 @@ -784,8 +846,8 @@ expose(XEvent *e) 263 void 264 focus(Client *c) 265 { 266 - if (!c || !ISVISIBLE(c)) 267 - for (c = selmon->stack; c && !ISVISIBLE(c); c = c->snext); 268 + if (!c || !ISVISIBLE(c, selmon)) 269 + for (c = selmon->cl->stack; c && !ISVISIBLE(c, selmon); c = c->snext); 270 if (selmon->sel && selmon->sel != c) 271 unfocus(selmon->sel, 0); 272 if (c) { 273 @@ -838,16 +900,16 @@ focusstack(const Arg *arg) 274 if (!selmon->sel || selmon->sel->isfullscreen) 275 return; 276 if (arg->i > 0) { 277 - for (c = selmon->sel->next; c && !ISVISIBLE(c); c = c->next); 278 + for (c = selmon->sel->next; c && !ISVISIBLE(c, selmon); c = c->next); 279 if (!c) 280 - for (c = selmon->clients; c && !ISVISIBLE(c); c = c->next); 281 + for (c = selmon->cl->clients; c && !ISVISIBLE(c, selmon); c = c->next); 282 } else { 283 - for (i = selmon->clients; i != selmon->sel; i = i->next) 284 - if (ISVISIBLE(i)) 285 + for (i = selmon->cl->clients; i != selmon->sel; i = i->next) 286 + if (ISVISIBLE(i, selmon)) 287 c = i; 288 if (!c) 289 for (; i; i = i->next) 290 - if (ISVISIBLE(i)) 291 + if (ISVISIBLE(i, selmon)) 292 c = i; 293 } 294 if (c) { 295 @@ -1107,12 +1169,12 @@ monocle(Monitor *m) 296 unsigned int n = 0; 297 Client *c; 298 299 - for (c = m->clients; c; c = c->next) 300 - if (ISVISIBLE(c)) 301 + for (c = m->cl->clients; c; c = c->next) 302 + if (ISVISIBLE(c, m)) 303 n++; 304 if (n > 0) /* override layout symbol */ 305 snprintf(m->ltsymbol, sizeof m->ltsymbol, "[%d]", n); 306 - for (c = nexttiled(m->clients); c; c = nexttiled(c->next)) 307 + for (c = nexttiled(m->cl->clients, m); c; c = nexttiled(c->next, m)) 308 resize(c, m->wx, m->wy, m->ww - 2 * c->bw, m->wh - 2 * c->bw, 0); 309 } 310 311 @@ -1194,9 +1256,9 @@ movemouse(const Arg *arg) 312 } 313 314 Client * 315 -nexttiled(Client *c) 316 +nexttiled(Client *c, Monitor *m) 317 { 318 - for (; c && (c->isfloating || !ISVISIBLE(c)); c = c->next); 319 + for (; c && (c->isfloating || !ISVISIBLE(c, m)); c = c->next); 320 return c; 321 } 322 323 @@ -1360,8 +1422,8 @@ restack(Monitor *m) 324 if (m->lt[m->sellt]->arrange) { 325 wc.stack_mode = Below; 326 wc.sibling = m->barwin; 327 - for (c = m->stack; c; c = c->snext) 328 - if (!c->isfloating && ISVISIBLE(c)) { 329 + for (c = m->cl->stack; c; c = c->snext) 330 + if (!c->isfloating && ISVISIBLE(c, m)) { 331 XConfigureWindow(dpy, c->win, CWSibling|CWStackMode, &wc); 332 wc.sibling = c->win; 333 } 334 @@ -1414,11 +1476,9 @@ sendmon(Client *c, Monitor *m) 335 if (c->mon == m) 336 return; 337 unfocus(c, 1); 338 - detach(c); 339 detachstack(c); 340 c->mon = m; 341 c->tags = m->tagset[m->seltags]; /* assign tags of target monitor */ 342 - attach(c); 343 attachstack(c); 344 focus(NULL); 345 arrange(NULL); 346 @@ -1541,6 +1601,8 @@ setup(void) 347 screen = DefaultScreen(dpy); 348 sw = DisplayWidth(dpy, screen); 349 sh = DisplayHeight(dpy, screen); 350 + if(!(cl = (Clientlist *)calloc(1, sizeof(Clientlist)))) 351 + die("fatal: could not malloc() %u bytes\n", sizeof(Clientlist)); 352 root = RootWindow(dpy, screen); 353 drw = drw_create(dpy, screen, root, sw, sh); 354 if (!drw_fontset_create(drw, fonts, LENGTH(fonts))) 355 @@ -1616,7 +1678,7 @@ showhide(Client *c) 356 { 357 if (!c) 358 return; 359 - if (ISVISIBLE(c)) { 360 + if (ISVISIBLE(c, c->mon)) { 361 /* show clients top down */ 362 XMoveWindow(dpy, c->win, c->x, c->y); 363 if ((!c->mon->lt[c->mon->sellt]->arrange || c->isfloating) && !c->isfullscreen) 364 @@ -1656,7 +1718,23 @@ spawn(const Arg *arg) 365 void 366 tag(const Arg *arg) 367 { 368 + Monitor *m; 369 + unsigned int newtags; 370 if (selmon->sel && arg->ui & TAGMASK) { 371 + newtags = arg->ui & TAGMASK; 372 + for (m = mons; m; m = m->next) 373 + /* if tag is visible on another monitor, move client to the new monitor */ 374 + if (m != selmon && m->tagset[m->seltags] & newtags) { 375 + /* prevent moving client to all tags (MODKEY-Shift-0) when multiple monitors are connected */ 376 + if(newtags & selmon->tagset[selmon->seltags]) 377 + return; 378 + selmon->sel->tags = newtags; 379 + selmon->sel->mon = m; 380 + arrange(m); 381 + break; 382 + } 383 + /* workaround in case just one monitor is connected */ 384 + 385 selmon->sel->tags = arg->ui & TAGMASK; 386 focus(NULL); 387 arrange(selmon); 388 @@ -1677,7 +1755,7 @@ tile(Monitor *m) 389 unsigned int i, n, h, mw, my, ty; 390 Client *c; 391 392 - for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++); 393 + for (n = 0, c = nexttiled(m->cl->clients, m); c; c = nexttiled(c->next, m), n++); 394 if (n == 0) 395 return; 396 397 @@ -1685,7 +1763,7 @@ tile(Monitor *m) 398 mw = m->nmaster ? m->ww * m->mfact : 0; 399 else 400 mw = m->ww; 401 - for (i = my = ty = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) 402 + for (i = my = ty = 0, c = nexttiled(m->cl->clients, m); c; c = nexttiled(c->next, m), i++) 403 if (i < m->nmaster) { 404 h = (m->wh - my) / (MIN(n, m->nmaster) - i); 405 resize(c, m->wx, m->wy + my, mw - (2*c->bw), h - (2*c->bw), 0); 406 @@ -1725,12 +1803,17 @@ togglefloating(const Arg *arg) 407 void 408 toggletag(const Arg *arg) 409 { 410 + Monitor *m; 411 unsigned int newtags; 412 413 if (!selmon->sel) 414 return; 415 newtags = selmon->sel->tags ^ (arg->ui & TAGMASK); 416 if (newtags) { 417 + /* prevent adding tags that are in use on other monitors */ 418 + for (m = mons; m; m = m->next) 419 + if (m != selmon && newtags & m->tagset[m->seltags]) 420 + return; 421 selmon->sel->tags = newtags; 422 focus(NULL); 423 arrange(selmon); 424 @@ -1740,12 +1823,27 @@ toggletag(const Arg *arg) 425 void 426 toggleview(const Arg *arg) 427 { 428 + Monitor *m; 429 unsigned int newtagset = selmon->tagset[selmon->seltags] ^ (arg->ui & TAGMASK); 430 431 if (newtagset) { 432 + /* prevent displaying the same tags on multiple monitors */ 433 + for(m = mons; m; m = m->next) 434 + if(m != selmon && newtagset & m->tagset[m->seltags]) 435 + return; 436 selmon->tagset[selmon->seltags] = newtagset; 437 - focus(NULL); 438 + attachclients(selmon); 439 arrange(selmon); 440 + focus(NULL); 441 + 442 + unsigned int newtagset = selmon->tagset[selmon->seltags] ^ (arg->ui & TAGMASK); 443 + 444 + if (newtagset) { 445 + selmon->tagset[selmon->seltags] = newtagset; 446 + attachclients(selmon); 447 + arrange(selmon); 448 + focus(NULL); 449 + } 450 } 451 } 452 453 @@ -1844,7 +1942,7 @@ updateclientlist() 454 455 XDeleteProperty(dpy, root, netatom[NetClientList]); 456 for (m = mons; m; m = m->next) 457 - for (c = m->clients; c; c = c->next) 458 + for (c = m->cl->clients; c; c = c->next) 459 XChangeProperty(dpy, root, netatom[NetClientList], 460 XA_WINDOW, 32, PropModeAppend, 461 (unsigned char *) &(c->win), 1); 462 @@ -1874,8 +1972,10 @@ updategeom(void) 463 if (n <= nn) { /* new monitors available */ 464 for (i = 0; i < (nn - n); i++) { 465 for (m = mons; m && m->next; m = m->next); 466 - if (m) 467 + if (m) { 468 m->next = createmon(); 469 + attachclients(m->next); 470 + } 471 else 472 mons = createmon(); 473 } 474 @@ -1895,16 +1995,13 @@ updategeom(void) 475 } else { /* less monitors available nn < n */ 476 for (i = nn; i < n; i++) { 477 for (m = mons; m && m->next; m = m->next); 478 - while ((c = m->clients)) { 479 - dirty = 1; 480 - m->clients = c->next; 481 - detachstack(c); 482 - c->mon = mons; 483 - attach(c); 484 - attachstack(c); 485 - } 486 if (m == selmon) 487 selmon = mons; 488 + for (c = m->cl->clients; c; c = c->next) { 489 + dirty = True; 490 + if (c->mon == m) 491 + c->mon = selmon; 492 + } 493 cleanupmon(m); 494 } 495 } 496 @@ -2038,13 +2135,32 @@ updatewmhints(Client *c) 497 void 498 view(const Arg *arg) 499 { 500 + Monitor *m; 501 + unsigned int newtagset = selmon->tagset[selmon->seltags ^ 1]; 502 if ((arg->ui & TAGMASK) == selmon->tagset[selmon->seltags]) 503 return; 504 + /* swap tags when trying to display a tag from another monitor */ 505 + if (arg->ui & TAGMASK) 506 + newtagset = arg->ui & TAGMASK; 507 + for (m = mons; m; m = m->next) 508 + if (m != selmon && newtagset & m->tagset[m->seltags]) { 509 + /* prevent displaying all tags (MODKEY-0) when multiple monitors 510 + * are connected */ 511 + if (newtagset & selmon->tagset[selmon->seltags]) 512 + return; 513 + m->sel = selmon->sel; 514 + m->seltags ^= 1; 515 + m->tagset[m->seltags] = selmon->tagset[selmon->seltags]; 516 + attachclients(m); 517 + arrange(m); 518 + break; 519 + } 520 selmon->seltags ^= 1; /* toggle sel tagset */ 521 if (arg->ui & TAGMASK) 522 selmon->tagset[selmon->seltags] = arg->ui & TAGMASK; 523 - focus(NULL); 524 + attachclients(selmon); 525 arrange(selmon); 526 + focus(NULL); 527 } 528 529 Client * 530 @@ -2054,7 +2170,7 @@ wintoclient(Window w) 531 Monitor *m; 532 533 for (m = mons; m; m = m->next) 534 - for (c = m->clients; c; c = c->next) 535 + for (c = m->cl->clients; c; c = c->next) 536 if (c->win == w) 537 return c; 538 return NULL; 539 @@ -2121,8 +2237,8 @@ zoom(const Arg *arg) 540 if (!selmon->lt[selmon->sellt]->arrange 541 || (selmon->sel && selmon->sel->isfloating)) 542 return; 543 - if (c == nexttiled(selmon->clients)) 544 - if (!c || !(c = nexttiled(c->next))) 545 + if (c == nexttiled(selmon->cl->clients, selmon)) 546 + if (!c || !(c = nexttiled(c->next, selmon))) 547 return; 548 pop(c); 549 } 550 -- 551 2.32.0 552