From 9b5719969ce85c3ecc0238d49c0255c5c2cc79f0 Mon Sep 17 00:00:00 2001 From: mihirlad55 Date: Mon, 10 Aug 2020 01:39:28 +0000 Subject: [PATCH] Add support for managing external status bars This patch allows dwm to manage other status bars such as polybar/lemonbar without them needing to set override-redirect. For all intents and purposes, DWM treats this bar as if it were its own and as a result helps the status bar and DWM live in harmony. This has a few advantages * The bar does not block fullscreen windows * DWM makes room for the status bar, so windows do not overlap the bar * The bar can be hidden/killed and DWM will not keep an unsightly gap where the bar was * DWM receives EnterNotify events when your cursor enters the bar To use another status bar, set usealtbar to 1 in your config.h and set altbarclass to the class name (can be found using xprop) to the class name of your status bar. Also make sure that if your status bar will be displayed on top, topbar is set to 1 in your config, and if it will be displayed on bottom, topbar is set to 0. This patch does not support bars that are not docked at the top or at the bottom of your monitor. This verison of the patch fixes handling of polybar's tray. The patch is developed at https://github.com/mihirlad55/dwm-anybar --- config.def.h | 4 ++ dwm.c | 192 +++++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 181 insertions(+), 15 deletions(-) diff --git a/dwm.c b/dwm.c index 9fd0286..c1d8ce0 100644 --- a/dwm.c +++ b/dwm.c @@ -47,8 +47,8 @@ /* macros */ #define BUTTONMASK (ButtonPressMask|ButtonReleaseMask) #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 INTERSECT(x,y,w,h,m) (MAX(0, MIN((x)+(w),(m)->mx+(m)->mw) - MAX((x),(m)->mx)) \ + * MAX(0, MIN((y)+(h),(m)->my+(m)->mh) - MAX((y),(m)->my))) #define ISVISIBLE(C) ((C->tags & C->mon->tagset[C->mon->seltags])) #define LENGTH(X) (sizeof X / sizeof X[0]) #define MOUSEMASK (BUTTONMASK|PointerMotionMask) @@ -116,7 +116,8 @@ struct Monitor { float mfact; int nmaster; int num; - int by; /* bar geometry */ + int by, bh; /* bar geometry */ + int tx, tw; /* bar tray geometry */ int mx, my, mw, mh; /* screen size */ int wx, wy, ww, wh; /* window area */ unsigned int seltags; @@ -129,6 +130,7 @@ struct Monitor { Client *stack; Monitor *next; Window barwin; + Window traywin; const Layout *lt[2]; }; @@ -179,6 +181,8 @@ static void incnmaster(const Arg *arg); static void keypress(XEvent *e); static void killclient(const Arg *arg); static void manage(Window w, XWindowAttributes *wa); +static void managealtbar(Window win, XWindowAttributes *wa); +static void managetray(Window win, XWindowAttributes *wa); static void mappingnotify(XEvent *e); static void maprequest(XEvent *e); static void monocle(Monitor *m); @@ -195,6 +199,7 @@ static void resizemouse(const Arg *arg); static void restack(Monitor *m); static void run(void); static void scan(void); +static void scantray(void); static int sendevent(Client *c, Atom proto); static void sendmon(Client *c, Monitor *m); static void setclientstate(Client *c, long state); @@ -207,6 +212,7 @@ static void seturgent(Client *c, int urg); static void showhide(Client *c); static void sigchld(int unused); static void spawn(const Arg *arg); +static void spawnbar(); static void tag(const Arg *arg); static void tagmon(const Arg *arg); static void tile(Monitor *); @@ -216,6 +222,8 @@ static void toggletag(const Arg *arg); static void toggleview(const Arg *arg); static void unfocus(Client *c, int setfocus); static void unmanage(Client *c, int destroyed); +static void unmanagealtbar(Window w); +static void unmanagetray(Window w); static void unmapnotify(XEvent *e); static void updatebarpos(Monitor *m); static void updatebars(void); @@ -230,6 +238,7 @@ static void updatewmhints(Client *c); static void view(const Arg *arg); static Client *wintoclient(Window w); static Monitor *wintomon(Window w); +static int wmclasscontains(Window win, const char *class, const char *name); static int xerror(Display *dpy, XErrorEvent *ee); static int xerrordummy(Display *dpy, XErrorEvent *ee); static int xerrorstart(Display *dpy, XErrorEvent *ee); @@ -505,8 +514,10 @@ cleanupmon(Monitor *mon) for (m = mons; m && m->next != mon; m = m->next); m->next = mon->next; } - XUnmapWindow(dpy, mon->barwin); - XDestroyWindow(dpy, mon->barwin); + if (!usealtbar) { + XUnmapWindow(dpy, mon->barwin); + XDestroyWindow(dpy, mon->barwin); + } free(mon); } @@ -568,7 +579,7 @@ configurenotify(XEvent *e) for (c = m->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); + XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, m->ww, m->bh); } focus(NULL); arrange(NULL); @@ -639,6 +650,7 @@ createmon(void) m->nmaster = nmaster; m->showbar = showbar; m->topbar = topbar; + m->bh = bh; m->lt[0] = &layouts[0]; m->lt[1] = &layouts[1 % LENGTH(layouts)]; strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol); @@ -649,10 +661,15 @@ void destroynotify(XEvent *e) { Client *c; + Monitor *m; XDestroyWindowEvent *ev = &e->xdestroywindow; if ((c = wintoclient(ev->window))) unmanage(c, 1); + else if ((m = wintomon(ev->window)) && m->barwin == ev->window) + unmanagealtbar(ev->window); + else if (m->traywin == ev->window) + unmanagetray(ev->window); } void @@ -696,6 +713,9 @@ dirtomon(int dir) void drawbar(Monitor *m) { + if (usealtbar) + return; + int x, w, tw = 0; int boxs = drw->fonts->h / 9; int boxw = drw->fonts->h / 6 + 2; @@ -1077,6 +1097,45 @@ manage(Window w, XWindowAttributes *wa) focus(NULL); } +void +managealtbar(Window win, XWindowAttributes *wa) +{ + Monitor *m; + if (!(m = recttomon(wa->x, wa->y, wa->width, wa->height))) + return; + + m->barwin = win; + m->by = wa->y; + bh = m->bh = wa->height; + updatebarpos(m); + arrange(m); + XSelectInput(dpy, win, EnterWindowMask|FocusChangeMask|PropertyChangeMask|StructureNotifyMask); + XMoveResizeWindow(dpy, win, wa->x, wa->y, wa->width, wa->height); + XMapWindow(dpy, win); + XChangeProperty(dpy, root, netatom[NetClientList], XA_WINDOW, 32, PropModeAppend, + (unsigned char *) &win, 1); +} + +void +managetray(Window win, XWindowAttributes *wa) +{ + Monitor *m; + if (!(m = recttomon(wa->x, wa->y, wa->width, wa->height))) + return; + + m->traywin = win; + m->tx = wa->x; + m->tw = wa->width; + updatebarpos(m); + arrange(m); + XSelectInput(dpy, win, EnterWindowMask|FocusChangeMask|PropertyChangeMask|StructureNotifyMask); + XMoveResizeWindow(dpy, win, wa->x, wa->y, wa->width, wa->height); + XMapWindow(dpy, win); + XChangeProperty(dpy, root, netatom[NetClientList], XA_WINDOW, 32, PropModeAppend, + (unsigned char *) &win, 1); +} + + void mappingnotify(XEvent *e) { @@ -1097,7 +1156,9 @@ maprequest(XEvent *e) return; if (wa.override_redirect) return; - if (!wintoclient(ev->window)) + if (wmclasscontains(ev->window, altbarclass, "")) + managealtbar(ev->window, &wa); + else if (!wintoclient(ev->window)) manage(ev->window, &wa); } @@ -1393,7 +1454,9 @@ scan(void) if (!XGetWindowAttributes(dpy, wins[i], &wa) || wa.override_redirect || XGetTransientForHint(dpy, wins[i], &d1)) continue; - if (wa.map_state == IsViewable || getstate(wins[i]) == IconicState) + if (wmclasscontains(wins[i], altbarclass, "")) + managealtbar(wins[i], &wa); + else if (wa.map_state == IsViewable || getstate(wins[i]) == IconicState) manage(wins[i], &wa); } for (i = 0; i < num; i++) { /* now the transients */ @@ -1408,6 +1471,29 @@ scan(void) } } +void +scantray(void) +{ + unsigned int num; + Window d1, d2, *wins = NULL; + XWindowAttributes wa; + + if (XQueryTree(dpy, root, &d1, &d2, &wins, &num)) { + for (unsigned int i = 0; i < num; i++) { + if (wmclasscontains(wins[i], altbarclass, alttrayname)) { + if (!XGetWindowAttributes(dpy, wins[i], &wa)) + break; + managetray(wins[i], &wa); + } + } + } + + if (wins) + XFree(wins); +} + + + void sendmon(Client *c, Monitor *m) { @@ -1546,7 +1632,7 @@ setup(void) if (!drw_fontset_create(drw, fonts, LENGTH(fonts))) die("no fonts could be loaded."); lrpad = drw->fonts->h; - bh = drw->fonts->h + 2; + bh = usealtbar ? 0 : drw->fonts->h + 2; updategeom(); /* init atoms */ utf8string = XInternAtom(dpy, "UTF8_STRING", False); @@ -1595,6 +1681,7 @@ setup(void) XSelectInput(dpy, root, wa.event_mask); grabkeys(); focus(NULL); + spawnbar(); } @@ -1653,6 +1740,13 @@ spawn(const Arg *arg) } } +void +spawnbar() +{ + if (*altbarcmd) + system(altbarcmd); +} + void tag(const Arg *arg) { @@ -1702,9 +1796,18 @@ tile(Monitor *m) void togglebar(const Arg *arg) { + /** + * Polybar tray does not raise maprequest event. It must be manually scanned + * for. Scanning it too early while the tray is being populated would give + * wrong dimensions. + */ + if (!selmon->traywin) + scantray(); + selmon->showbar = !selmon->showbar; updatebarpos(selmon); - XMoveResizeWindow(dpy, selmon->barwin, selmon->wx, selmon->by, selmon->ww, bh); + XMoveResizeWindow(dpy, selmon->barwin, selmon->wx, selmon->by, selmon->ww, selmon->bh); + XMoveResizeWindow(dpy, selmon->traywin, selmon->tx, selmon->by, selmon->tw, selmon->bh); arrange(selmon); } @@ -1787,10 +1890,41 @@ unmanage(Client *c, int destroyed) arrange(m); } +void +unmanagealtbar(Window w) +{ + Monitor *m = wintomon(w); + + if (!m) + return; + + m->barwin = 0; + m->by = 0; + m->bh = 0; + updatebarpos(m); + arrange(m); +} + +void +unmanagetray(Window w) +{ + Monitor *m = wintomon(w); + + if (!m) + return; + + m->traywin = 0; + m->tx = 0; + m->tw = 0; + updatebarpos(m); + arrange(m); +} + void unmapnotify(XEvent *e) { Client *c; + Monitor *m; XUnmapEvent *ev = &e->xunmap; if ((c = wintoclient(ev->window))) { @@ -1798,12 +1932,18 @@ unmapnotify(XEvent *e) setclientstate(c, WithdrawnState); else unmanage(c, 0); - } + } else if ((m = wintomon(ev->window)) && m->barwin == ev->window) + unmanagealtbar(ev->window); + else if (m->traywin == ev->window) + unmanagetray(ev->window); } void updatebars(void) { + if (usealtbar) + return; + Monitor *m; XSetWindowAttributes wa = { .override_redirect = True, @@ -1829,11 +1969,11 @@ updatebarpos(Monitor *m) m->wy = m->my; m->wh = m->mh; if (m->showbar) { - m->wh -= bh; + m->wh -= m->bh; m->by = m->topbar ? m->wy : m->wy + m->wh; - m->wy = m->topbar ? m->wy + bh : m->wy; + m->wy = m->topbar ? m->wy + m->bh : m->wy; } else - m->by = -bh; + m->by = -m->bh; } void @@ -2070,13 +2210,35 @@ wintomon(Window w) if (w == root && getrootptr(&x, &y)) return recttomon(x, y, 1, 1); for (m = mons; m; m = m->next) - if (w == m->barwin) + if (w == m->barwin || w == m->traywin) return m; if ((c = wintoclient(w))) return c->mon; return selmon; } +int +wmclasscontains(Window win, const char *class, const char *name) +{ + XClassHint ch = { NULL, NULL }; + int res = 1; + + if (XGetClassHint(dpy, win, &ch)) { + if (ch.res_name && strstr(ch.res_name, name) == NULL) + res = 0; + if (ch.res_class && strstr(ch.res_class, class) == NULL) + res = 0; + } else + res = 0; + + if (ch.res_class) + XFree(ch.res_class); + if (ch.res_name) + XFree(ch.res_name); + + return res; +} + /* There's no way to check accesses to destroyed windows, thus those cases are * ignored (especially on UnmapNotify's). Other types of errors call Xlibs * default error handler, which may call exit. */ -- 2.28.0