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