dwm-anybar-polybar-tray-fix-20200810-bb2e722.diff (13021B)
1 From 9b5719969ce85c3ecc0238d49c0255c5c2cc79f0 Mon Sep 17 00:00:00 2001 2 From: mihirlad55 <mihirlad55@gmail.com> 3 Date: Mon, 10 Aug 2020 01:39:28 +0000 4 Subject: [PATCH] Add support for managing external status bars 5 6 This patch allows dwm to manage other status bars such as 7 polybar/lemonbar without them needing to set override-redirect. For 8 all intents and purposes, DWM treats this bar as if it were its own 9 and as a result helps the status bar and DWM live in harmony. 10 11 This has a few advantages 12 * The bar does not block fullscreen windows 13 * DWM makes room for the status bar, so windows do not overlap the bar 14 * The bar can be hidden/killed and DWM will not keep an unsightly gap 15 where the bar was 16 * DWM receives EnterNotify events when your cursor enters the bar 17 18 To use another status bar, set usealtbar to 1 in your config.h and set 19 altbarclass to the class name (can be found using xprop) to the class 20 name of your status bar. Also make sure that if your status bar will 21 be displayed on top, topbar is set to 1 in your config, and if it will 22 be displayed on bottom, topbar is set to 0. This patch does not 23 support bars that are not docked at the top or at the bottom of your 24 monitor. 25 26 This verison of the patch fixes handling of polybar's tray. 27 28 The patch is developed at https://github.com/mihirlad55/dwm-anybar 29 --- 30 config.def.h | 4 ++ 31 dwm.c | 192 +++++++++++++++++++++++++++++++++++++++++++++++---- 32 2 files changed, 181 insertions(+), 15 deletions(-) 33 34 diff --git a/config.def.h b/config.def.h 35 index 1c0b587..f45211b 100644 36 --- a/config.def.h 37 +++ b/config.def.h 38 @@ -5,6 +5,10 @@ static const unsigned int borderpx = 1; /* border pixel of windows */ 39 static const unsigned int snap = 32; /* snap pixel */ 40 static const int showbar = 1; /* 0 means no bar */ 41 static const int topbar = 1; /* 0 means bottom bar */ 42 +static const int usealtbar = 1; /* 1 means use non-dwm status bar */ 43 +static const char *altbarclass = "Polybar"; /* Alternate bar class name */ 44 +static const char *alttrayname = "tray"; /* Polybar tray instance name */ 45 +static const char *altbarcmd = "$HOME/bar.sh"; /* Alternate bar launch command */ 46 static const char *fonts[] = { "monospace:size=10" }; 47 static const char dmenufont[] = "monospace:size=10"; 48 static const char col_gray1[] = "#222222"; 49 diff --git a/dwm.c b/dwm.c 50 index 9fd0286..c1d8ce0 100644 51 --- a/dwm.c 52 +++ b/dwm.c 53 @@ -47,8 +47,8 @@ 54 /* macros */ 55 #define BUTTONMASK (ButtonPressMask|ButtonReleaseMask) 56 #define CLEANMASK(mask) (mask & ~(numlockmask|LockMask) & (ShiftMask|ControlMask|Mod1Mask|Mod2Mask|Mod3Mask|Mod4Mask|Mod5Mask)) 57 -#define INTERSECT(x,y,w,h,m) (MAX(0, MIN((x)+(w),(m)->wx+(m)->ww) - MAX((x),(m)->wx)) \ 58 - * MAX(0, MIN((y)+(h),(m)->wy+(m)->wh) - MAX((y),(m)->wy))) 59 +#define INTERSECT(x,y,w,h,m) (MAX(0, MIN((x)+(w),(m)->mx+(m)->mw) - MAX((x),(m)->mx)) \ 60 + * MAX(0, MIN((y)+(h),(m)->my+(m)->mh) - MAX((y),(m)->my))) 61 #define ISVISIBLE(C) ((C->tags & C->mon->tagset[C->mon->seltags])) 62 #define LENGTH(X) (sizeof X / sizeof X[0]) 63 #define MOUSEMASK (BUTTONMASK|PointerMotionMask) 64 @@ -116,7 +116,8 @@ struct Monitor { 65 float mfact; 66 int nmaster; 67 int num; 68 - int by; /* bar geometry */ 69 + int by, bh; /* bar geometry */ 70 + int tx, tw; /* bar tray geometry */ 71 int mx, my, mw, mh; /* screen size */ 72 int wx, wy, ww, wh; /* window area */ 73 unsigned int seltags; 74 @@ -129,6 +130,7 @@ struct Monitor { 75 Client *stack; 76 Monitor *next; 77 Window barwin; 78 + Window traywin; 79 const Layout *lt[2]; 80 }; 81 82 @@ -179,6 +181,8 @@ static void incnmaster(const Arg *arg); 83 static void keypress(XEvent *e); 84 static void killclient(const Arg *arg); 85 static void manage(Window w, XWindowAttributes *wa); 86 +static void managealtbar(Window win, XWindowAttributes *wa); 87 +static void managetray(Window win, XWindowAttributes *wa); 88 static void mappingnotify(XEvent *e); 89 static void maprequest(XEvent *e); 90 static void monocle(Monitor *m); 91 @@ -195,6 +199,7 @@ static void resizemouse(const Arg *arg); 92 static void restack(Monitor *m); 93 static void run(void); 94 static void scan(void); 95 +static void scantray(void); 96 static int sendevent(Client *c, Atom proto); 97 static void sendmon(Client *c, Monitor *m); 98 static void setclientstate(Client *c, long state); 99 @@ -207,6 +212,7 @@ static void seturgent(Client *c, int urg); 100 static void showhide(Client *c); 101 static void sigchld(int unused); 102 static void spawn(const Arg *arg); 103 +static void spawnbar(); 104 static void tag(const Arg *arg); 105 static void tagmon(const Arg *arg); 106 static void tile(Monitor *); 107 @@ -216,6 +222,8 @@ static void toggletag(const Arg *arg); 108 static void toggleview(const Arg *arg); 109 static void unfocus(Client *c, int setfocus); 110 static void unmanage(Client *c, int destroyed); 111 +static void unmanagealtbar(Window w); 112 +static void unmanagetray(Window w); 113 static void unmapnotify(XEvent *e); 114 static void updatebarpos(Monitor *m); 115 static void updatebars(void); 116 @@ -230,6 +238,7 @@ static void updatewmhints(Client *c); 117 static void view(const Arg *arg); 118 static Client *wintoclient(Window w); 119 static Monitor *wintomon(Window w); 120 +static int wmclasscontains(Window win, const char *class, const char *name); 121 static int xerror(Display *dpy, XErrorEvent *ee); 122 static int xerrordummy(Display *dpy, XErrorEvent *ee); 123 static int xerrorstart(Display *dpy, XErrorEvent *ee); 124 @@ -505,8 +514,10 @@ cleanupmon(Monitor *mon) 125 for (m = mons; m && m->next != mon; m = m->next); 126 m->next = mon->next; 127 } 128 - XUnmapWindow(dpy, mon->barwin); 129 - XDestroyWindow(dpy, mon->barwin); 130 + if (!usealtbar) { 131 + XUnmapWindow(dpy, mon->barwin); 132 + XDestroyWindow(dpy, mon->barwin); 133 + } 134 free(mon); 135 } 136 137 @@ -568,7 +579,7 @@ configurenotify(XEvent *e) 138 for (c = m->clients; c; c = c->next) 139 if (c->isfullscreen) 140 resizeclient(c, m->mx, m->my, m->mw, m->mh); 141 - XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, m->ww, bh); 142 + XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, m->ww, m->bh); 143 } 144 focus(NULL); 145 arrange(NULL); 146 @@ -639,6 +650,7 @@ createmon(void) 147 m->nmaster = nmaster; 148 m->showbar = showbar; 149 m->topbar = topbar; 150 + m->bh = bh; 151 m->lt[0] = &layouts[0]; 152 m->lt[1] = &layouts[1 % LENGTH(layouts)]; 153 strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol); 154 @@ -649,10 +661,15 @@ void 155 destroynotify(XEvent *e) 156 { 157 Client *c; 158 + Monitor *m; 159 XDestroyWindowEvent *ev = &e->xdestroywindow; 160 161 if ((c = wintoclient(ev->window))) 162 unmanage(c, 1); 163 + else if ((m = wintomon(ev->window)) && m->barwin == ev->window) 164 + unmanagealtbar(ev->window); 165 + else if (m->traywin == ev->window) 166 + unmanagetray(ev->window); 167 } 168 169 void 170 @@ -696,6 +713,9 @@ dirtomon(int dir) 171 void 172 drawbar(Monitor *m) 173 { 174 + if (usealtbar) 175 + return; 176 + 177 int x, w, tw = 0; 178 int boxs = drw->fonts->h / 9; 179 int boxw = drw->fonts->h / 6 + 2; 180 @@ -1077,6 +1097,45 @@ manage(Window w, XWindowAttributes *wa) 181 focus(NULL); 182 } 183 184 +void 185 +managealtbar(Window win, XWindowAttributes *wa) 186 +{ 187 + Monitor *m; 188 + if (!(m = recttomon(wa->x, wa->y, wa->width, wa->height))) 189 + return; 190 + 191 + m->barwin = win; 192 + m->by = wa->y; 193 + bh = m->bh = wa->height; 194 + updatebarpos(m); 195 + arrange(m); 196 + XSelectInput(dpy, win, EnterWindowMask|FocusChangeMask|PropertyChangeMask|StructureNotifyMask); 197 + XMoveResizeWindow(dpy, win, wa->x, wa->y, wa->width, wa->height); 198 + XMapWindow(dpy, win); 199 + XChangeProperty(dpy, root, netatom[NetClientList], XA_WINDOW, 32, PropModeAppend, 200 + (unsigned char *) &win, 1); 201 +} 202 + 203 +void 204 +managetray(Window win, XWindowAttributes *wa) 205 +{ 206 + Monitor *m; 207 + if (!(m = recttomon(wa->x, wa->y, wa->width, wa->height))) 208 + return; 209 + 210 + m->traywin = win; 211 + m->tx = wa->x; 212 + m->tw = wa->width; 213 + updatebarpos(m); 214 + arrange(m); 215 + XSelectInput(dpy, win, EnterWindowMask|FocusChangeMask|PropertyChangeMask|StructureNotifyMask); 216 + XMoveResizeWindow(dpy, win, wa->x, wa->y, wa->width, wa->height); 217 + XMapWindow(dpy, win); 218 + XChangeProperty(dpy, root, netatom[NetClientList], XA_WINDOW, 32, PropModeAppend, 219 + (unsigned char *) &win, 1); 220 +} 221 + 222 + 223 void 224 mappingnotify(XEvent *e) 225 { 226 @@ -1097,7 +1156,9 @@ maprequest(XEvent *e) 227 return; 228 if (wa.override_redirect) 229 return; 230 - if (!wintoclient(ev->window)) 231 + if (wmclasscontains(ev->window, altbarclass, "")) 232 + managealtbar(ev->window, &wa); 233 + else if (!wintoclient(ev->window)) 234 manage(ev->window, &wa); 235 } 236 237 @@ -1393,7 +1454,9 @@ scan(void) 238 if (!XGetWindowAttributes(dpy, wins[i], &wa) 239 || wa.override_redirect || XGetTransientForHint(dpy, wins[i], &d1)) 240 continue; 241 - if (wa.map_state == IsViewable || getstate(wins[i]) == IconicState) 242 + if (wmclasscontains(wins[i], altbarclass, "")) 243 + managealtbar(wins[i], &wa); 244 + else if (wa.map_state == IsViewable || getstate(wins[i]) == IconicState) 245 manage(wins[i], &wa); 246 } 247 for (i = 0; i < num; i++) { /* now the transients */ 248 @@ -1408,6 +1471,29 @@ scan(void) 249 } 250 } 251 252 +void 253 +scantray(void) 254 +{ 255 + unsigned int num; 256 + Window d1, d2, *wins = NULL; 257 + XWindowAttributes wa; 258 + 259 + if (XQueryTree(dpy, root, &d1, &d2, &wins, &num)) { 260 + for (unsigned int i = 0; i < num; i++) { 261 + if (wmclasscontains(wins[i], altbarclass, alttrayname)) { 262 + if (!XGetWindowAttributes(dpy, wins[i], &wa)) 263 + break; 264 + managetray(wins[i], &wa); 265 + } 266 + } 267 + } 268 + 269 + if (wins) 270 + XFree(wins); 271 +} 272 + 273 + 274 + 275 void 276 sendmon(Client *c, Monitor *m) 277 { 278 @@ -1546,7 +1632,7 @@ setup(void) 279 if (!drw_fontset_create(drw, fonts, LENGTH(fonts))) 280 die("no fonts could be loaded."); 281 lrpad = drw->fonts->h; 282 - bh = drw->fonts->h + 2; 283 + bh = usealtbar ? 0 : drw->fonts->h + 2; 284 updategeom(); 285 /* init atoms */ 286 utf8string = XInternAtom(dpy, "UTF8_STRING", False); 287 @@ -1595,6 +1681,7 @@ setup(void) 288 XSelectInput(dpy, root, wa.event_mask); 289 grabkeys(); 290 focus(NULL); 291 + spawnbar(); 292 } 293 294 295 @@ -1653,6 +1740,13 @@ spawn(const Arg *arg) 296 } 297 } 298 299 +void 300 +spawnbar() 301 +{ 302 + if (*altbarcmd) 303 + system(altbarcmd); 304 +} 305 + 306 void 307 tag(const Arg *arg) 308 { 309 @@ -1702,9 +1796,18 @@ tile(Monitor *m) 310 void 311 togglebar(const Arg *arg) 312 { 313 + /** 314 + * Polybar tray does not raise maprequest event. It must be manually scanned 315 + * for. Scanning it too early while the tray is being populated would give 316 + * wrong dimensions. 317 + */ 318 + if (!selmon->traywin) 319 + scantray(); 320 + 321 selmon->showbar = !selmon->showbar; 322 updatebarpos(selmon); 323 - XMoveResizeWindow(dpy, selmon->barwin, selmon->wx, selmon->by, selmon->ww, bh); 324 + XMoveResizeWindow(dpy, selmon->barwin, selmon->wx, selmon->by, selmon->ww, selmon->bh); 325 + XMoveResizeWindow(dpy, selmon->traywin, selmon->tx, selmon->by, selmon->tw, selmon->bh); 326 arrange(selmon); 327 } 328 329 @@ -1787,10 +1890,41 @@ unmanage(Client *c, int destroyed) 330 arrange(m); 331 } 332 333 +void 334 +unmanagealtbar(Window w) 335 +{ 336 + Monitor *m = wintomon(w); 337 + 338 + if (!m) 339 + return; 340 + 341 + m->barwin = 0; 342 + m->by = 0; 343 + m->bh = 0; 344 + updatebarpos(m); 345 + arrange(m); 346 +} 347 + 348 +void 349 +unmanagetray(Window w) 350 +{ 351 + Monitor *m = wintomon(w); 352 + 353 + if (!m) 354 + return; 355 + 356 + m->traywin = 0; 357 + m->tx = 0; 358 + m->tw = 0; 359 + updatebarpos(m); 360 + arrange(m); 361 +} 362 + 363 void 364 unmapnotify(XEvent *e) 365 { 366 Client *c; 367 + Monitor *m; 368 XUnmapEvent *ev = &e->xunmap; 369 370 if ((c = wintoclient(ev->window))) { 371 @@ -1798,12 +1932,18 @@ unmapnotify(XEvent *e) 372 setclientstate(c, WithdrawnState); 373 else 374 unmanage(c, 0); 375 - } 376 + } else if ((m = wintomon(ev->window)) && m->barwin == ev->window) 377 + unmanagealtbar(ev->window); 378 + else if (m->traywin == ev->window) 379 + unmanagetray(ev->window); 380 } 381 382 void 383 updatebars(void) 384 { 385 + if (usealtbar) 386 + return; 387 + 388 Monitor *m; 389 XSetWindowAttributes wa = { 390 .override_redirect = True, 391 @@ -1829,11 +1969,11 @@ updatebarpos(Monitor *m) 392 m->wy = m->my; 393 m->wh = m->mh; 394 if (m->showbar) { 395 - m->wh -= bh; 396 + m->wh -= m->bh; 397 m->by = m->topbar ? m->wy : m->wy + m->wh; 398 - m->wy = m->topbar ? m->wy + bh : m->wy; 399 + m->wy = m->topbar ? m->wy + m->bh : m->wy; 400 } else 401 - m->by = -bh; 402 + m->by = -m->bh; 403 } 404 405 void 406 @@ -2070,13 +2210,35 @@ wintomon(Window w) 407 if (w == root && getrootptr(&x, &y)) 408 return recttomon(x, y, 1, 1); 409 for (m = mons; m; m = m->next) 410 - if (w == m->barwin) 411 + if (w == m->barwin || w == m->traywin) 412 return m; 413 if ((c = wintoclient(w))) 414 return c->mon; 415 return selmon; 416 } 417 418 +int 419 +wmclasscontains(Window win, const char *class, const char *name) 420 +{ 421 + XClassHint ch = { NULL, NULL }; 422 + int res = 1; 423 + 424 + if (XGetClassHint(dpy, win, &ch)) { 425 + if (ch.res_name && strstr(ch.res_name, name) == NULL) 426 + res = 0; 427 + if (ch.res_class && strstr(ch.res_class, class) == NULL) 428 + res = 0; 429 + } else 430 + res = 0; 431 + 432 + if (ch.res_class) 433 + XFree(ch.res_class); 434 + if (ch.res_name) 435 + XFree(ch.res_name); 436 + 437 + return res; 438 +} 439 + 440 /* There's no way to check accesses to destroyed windows, thus those cases are 441 * ignored (especially on UnmapNotify's). Other types of errors call Xlibs 442 * default error handler, which may call exit. */ 443 -- 444 2.28.0 445