diff --git a/README.md b/README.md index 93bc99c..64b6df3 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -This side project has a very different take on dwm patching. It uses preprocessor directives to decide whether or not to include a patch during build time; essentially this means that this build, for better or worse, contains both the patched _and_ the original code. The aim being that you can select which patches to include and the build will contain that code and nothing more. +This side project has a different take on dwm patching. It uses preprocessor directives to decide whether or not to include a patch during build time; essentially this means that this build, for better or worse, contains both the patched _and_ the original code. The aim being that you can select which patches to include and the build will contain that code and nothing more. -For example to include the alpha patch then you would only need to flip this setting from 0 to 1 in `dwm.c`: +For example to include the alpha patch then you would only need to flip this setting from 0 to 1 in [patches.h](https://github.com/bakkeby/dwm-flexipatch/blob/master/patches.h): ```c #define ALPHA_PATCH 1 ``` @@ -11,9 +11,13 @@ Refer to [https://dwm.suckless.org/](https://dwm.suckless.org/) for details on t ### Changelog: +2019-09-05 - Systray patch added 2019-09-05 - Alpha patch added ### Patches included: - [alpha](https://dwm.suckless.org/patches/alpha/) - adds transparency for the status bar + + - [systray](https://dwm.suckless.org/patches/systray/) + - adds system tray in the status bar \ No newline at end of file diff --git a/config.def.h b/config.def.h index db06407..12a0753 100644 --- a/config.def.h +++ b/config.def.h @@ -5,6 +5,12 @@ static const unsigned int borderpx = 1; /* border pixel of windows */ static const unsigned int snap = 32; /* snap pixel */ static const int showbar = 1; /* 0 means no bar */ static const int topbar = 1; /* 0 means bottom bar */ +#if SYSTRAY_PATCH +static const unsigned int systraypinning = 0; /* 0: sloppy systray follows selected monitor, >0: pin systray to monitor X */ +static const unsigned int systrayspacing = 2; /* systray spacing */ +static const int systraypinningfailfirst = 1; /* 1: if pinning fails, display systray on the first monitor, False: display systray on the last monitor*/ +static const int showsystray = 1; /* 0 means no systray */ +#endif // SYSTRAY_PATCH static const char *fonts[] = { "monospace:size=10" }; static const char dmenufont[] = "monospace:size=10"; static const char col_gray1[] = "#222222"; diff --git a/dwm.c b/dwm.c index d717679..7c46ca7 100644 --- a/dwm.c +++ b/dwm.c @@ -61,9 +61,15 @@ /* enums */ enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */ enum { SchemeNorm, SchemeSel }; /* color schemes */ +#if SYSTRAY_PATCH +enum { NetSupported, NetSystemTray, NetSystemTrayOP, NetSystemTrayOrientation, NetSystemTrayVisual, + NetWMName, NetWMState, NetWMFullscreen, NetActiveWindow, NetWMWindowType, NetWMWindowTypeDock, + NetSystemTrayOrientationHorz, NetWMWindowTypeDialog, NetClientList, NetWMCheck, NetLast }; /* EWMH atoms */ +#else enum { NetSupported, NetWMName, NetWMState, NetWMCheck, NetWMFullscreen, NetActiveWindow, NetWMWindowType, NetWMWindowTypeDialog, NetClientList, NetLast }; /* EWMH atoms */ +#endif // SYSTRAY_PATCH enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */ enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, ClkClientWin, ClkRootWin, ClkLast }; /* clicks */ @@ -195,7 +201,11 @@ static void resizemouse(const Arg *arg); static void restack(Monitor *m); static void run(void); static void scan(void); +#if SYSTRAY_PATCH +static int sendevent(Window w, Atom proto, int m, long d0, long d1, long d2, long d3, long d4); +#else static int sendevent(Client *c, Atom proto); +#endif // SYSTRAY_PATCH static void sendmon(Client *c, Monitor *m); static void setclientstate(Client *c, long state); static void setfocus(Client *c); @@ -235,6 +245,8 @@ static int xerrordummy(Display *dpy, XErrorEvent *ee); static int xerrorstart(Display *dpy, XErrorEvent *ee); static void zoom(const Arg *arg); +#include "patch/include.h" + /* variables */ static const char broken[] = "broken"; static char stext[256]; @@ -258,9 +270,16 @@ static void (*handler[LASTEvent]) (XEvent *) = { [MapRequest] = maprequest, [MotionNotify] = motionnotify, [PropertyNotify] = propertynotify, + #if SYSTRAY_PATCH + [ResizeRequest] = resizerequest, + #endif // SYSTRAY_PATCH [UnmapNotify] = unmapnotify }; +#if SYSTRAY_PATCH +static Atom wmatom[WMLast], netatom[NetLast], xatom[XLast]; +#else static Atom wmatom[WMLast], netatom[NetLast]; +#endif // SYSTRAY_PATCH static int running = 1; static Cur *cursor[CurLast]; static Clr **scheme; @@ -269,8 +288,6 @@ static Drw *drw; static Monitor *mons, *selmon; static Window root, wmcheckwin; -#include "patch/include.h" - /* configuration, allows nested code to access above variables */ #include "config.h" @@ -444,7 +461,11 @@ buttonpress(XEvent *e) arg.ui = 1 << i; } else if (ev->x < x + blw) click = ClkLtSymbol; + #if SYSTRAY_PATCH + else if (ev->x > selmon->ww - TEXTW(stext) - getsystraywidth()) + #else else if (ev->x > selmon->ww - TEXTW(stext)) + #endif // SYSTRAY_PATCH click = ClkStatusText; else click = ClkWinTitle; @@ -487,6 +508,13 @@ cleanup(void) XUngrabKey(dpy, AnyKey, AnyModifier, root); while (mons) cleanupmon(mons); + #if SYSTRAY_PATCH + if (showsystray) { + XUnmapWindow(dpy, systray->win); + XDestroyWindow(dpy, systray->win); + free(systray); + } + #endif // SYSTRAY_PATCH for (i = 0; i < CurLast; i++) drw_cur_free(drw, cursor[i]); for (i = 0; i < LENGTH(colors); i++) @@ -517,9 +545,58 @@ cleanupmon(Monitor *mon) void clientmessage(XEvent *e) { + #if SYSTRAY_PATCH + XWindowAttributes wa; + XSetWindowAttributes swa; + #endif // SYSTRAY_PATCH XClientMessageEvent *cme = &e->xclient; Client *c = wintoclient(cme->window); + #if SYSTRAY_PATCH + if (showsystray && cme->window == systray->win && cme->message_type == netatom[NetSystemTrayOP]) { + /* add systray icons */ + if (cme->data.l[1] == SYSTEM_TRAY_REQUEST_DOCK) { + if (!(c = (Client *)calloc(1, sizeof(Client)))) + die("fatal: could not malloc() %u bytes\n", sizeof(Client)); + if (!(c->win = cme->data.l[2])) { + free(c); + return; + } + + /* Clear status bar to avoid artifacts beneath systray icons */ + drw_rect(drw, 0, 0, selmon->ww, bh, 1, 1); + drw_map(drw, selmon->barwin, 0, 0, selmon->ww, bh); + + c->mon = selmon; + c->next = systray->icons; + systray->icons = c; + XGetWindowAttributes(dpy, c->win, &wa); + c->x = c->oldx = c->y = c->oldy = 0; + c->w = c->oldw = wa.width; + c->h = c->oldh = wa.height; + c->oldbw = wa.border_width; + c->bw = 0; + c->isfloating = True; + /* reuse tags field as mapped status */ + c->tags = 1; + updatesizehints(c); + updatesystrayicongeom(c, wa.width, wa.height); + XAddToSaveSet(dpy, c->win); + XSelectInput(dpy, c->win, StructureNotifyMask | PropertyChangeMask | ResizeRedirectMask); + XReparentWindow(dpy, c->win, systray->win, 0, 0); + /* use parents background color */ + swa.background_pixel = scheme[SchemeNorm][ColBg].pixel; + XChangeWindowAttributes(dpy, c->win, CWBackPixel, &swa); + sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_EMBEDDED_NOTIFY, 0 , systray->win, XEMBED_EMBEDDED_VERSION); + XSync(dpy, False); + setclientstate(c, NormalState); + drawbar(selmon); + updatesystray(); + } + return; + } + #endif // SYSTRAY_PATCH + if (!c) return; if (cme->message_type == netatom[NetWMState]) { @@ -657,6 +734,12 @@ destroynotify(XEvent *e) if ((c = wintoclient(ev->window))) unmanage(c, 1); + #if SYSTRAY_PATCH + else if (showsystray && (c = wintosystrayicon(ev->window))) { + removesystrayicon(c); + updatesystray(); + } + #endif // SYSTRAY_PATCH } void @@ -701,16 +784,28 @@ void drawbar(Monitor *m) { int x, w, sw = 0; + #if SYSTRAY_PATCH + int stw = 0; + #endif // SYSTRAY_PATCH int boxs = drw->fonts->h / 9; int boxw = drw->fonts->h / 6 + 2; unsigned int i, occ = 0, urg = 0; Client *c; + #if SYSTRAY_PATCH + if (showsystray && m == systraytomon(m)) + stw = getsystraywidth(); + #endif // SYSTRAY_PATCH + /* draw status first so it can be overdrawn by tags later */ if (m == selmon) { /* status is only drawn on selected monitor */ drw_setscheme(drw, scheme[SchemeNorm]); sw = TEXTW(stext) - lrpad + 2; /* 2px right padding */ + #if SYSTRAY_PATCH + drw_text(drw, m->ww - sw - stw, 0, sw, bh, 0, stext, 0); + #else drw_text(drw, m->ww - sw, 0, sw, bh, 0, stext, 0); + #endif // SYSTRAY_PATCH } for (c = m->clients; c; c = c->next) { @@ -733,7 +828,11 @@ drawbar(Monitor *m) drw_setscheme(drw, scheme[SchemeNorm]); x = drw_text(drw, x, 0, w, bh, lrpad / 2, m->ltsymbol, 0); + #if SYSTRAY_PATCH + if ((w = m->ww - sw - stw - x) > bh) { + #else if ((w = m->ww - sw - x) > bh) { + #endif // SYSTRAY_PATCH if (m->sel) { drw_setscheme(drw, scheme[m == selmon ? SchemeSel : SchemeNorm]); drw_text(drw, x, 0, w, bh, lrpad / 2, m->sel->name, 0); @@ -744,7 +843,11 @@ drawbar(Monitor *m) drw_rect(drw, x, 0, w, bh, 1, 1); } } + #if SYSTRAY_PATCH + drw_map(drw, m->barwin, 0, 0, m->ww - stw, bh); + #else drw_map(drw, m->barwin, 0, 0, m->ww, bh); + #endif // SYSTRAY_PATCH } void @@ -781,8 +884,13 @@ expose(XEvent *e) Monitor *m; XExposeEvent *ev = &e->xexpose; - if (ev->count == 0 && (m = wintomon(ev->window))) + if (ev->count == 0 && (m = wintomon(ev->window))) { drawbar(m); + #if SYSTRAY_PATCH + if (showsystray && m == selmon) + updatesystray(); + #endif // SYSTRAY_PATCH + } } void @@ -816,8 +924,21 @@ focusin(XEvent *e) { XFocusChangeEvent *ev = &e->xfocus; + #if SYSTRAY_PATCH + if (showsystray) { + /* Clear status bar to avoid artifacts beneath systray icons */ + drw_rect(drw, 0, 0, selmon->ww, bh, 1, 1); + drw_map(drw, selmon->barwin, 0, 0, selmon->ww, bh); + } + #endif // SYSTRAY_PATCH + if (selmon->sel && ev->window != selmon->sel->win) setfocus(selmon->sel); + + #if SYSTRAY_PATCH + if (showsystray) + updatesystray(); + #endif // SYSTRAY_PATCH } void @@ -868,11 +989,27 @@ getatomprop(Client *c, Atom prop) unsigned char *p = NULL; Atom da, atom = None; + #if SYSTRAY_PATCH + /* FIXME getatomprop should return the number of items and a pointer to + * the stored data instead of this workaround */ + Atom req = XA_ATOM; + if (prop == xatom[XembedInfo]) + req = xatom[XembedInfo]; + + if (XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, req, + &da, &di, &dl, &dl, &p) == Success && p) { + atom = *(Atom *)p; + if (da == xatom[XembedInfo] && dl == 2) + atom = ((Atom *)p)[1]; + XFree(p); + } + #else if (XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, XA_ATOM, &da, &di, &dl, &dl, &p) == Success && p) { atom = *(Atom *)p; XFree(p); } + #endif // SYSTRAY_PATCH return atom; } @@ -1008,7 +1145,11 @@ killclient(const Arg *arg) { if (!selmon->sel) return; + #if SYSTRAY_PATCH + if (!sendevent(selmon->sel->win, wmatom[WMDelete], NoEventMask, wmatom[WMDelete], CurrentTime, 0, 0, 0)) { + #else if (!sendevent(selmon->sel, wmatom[WMDelete])) { + #endif // SYSTRAY_PATCH XGrabServer(dpy); XSetErrorHandler(xerrordummy); XSetCloseDownMode(dpy, DestroyAll); @@ -1097,6 +1238,14 @@ maprequest(XEvent *e) static XWindowAttributes wa; XMapRequestEvent *ev = &e->xmaprequest; + #if SYSTRAY_PATCH + Client *i; + if (showsystray && (i = wintosystrayicon(ev->window))) { + sendevent(i->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_WINDOW_ACTIVATE, 0, systray->win, XEMBED_EMBEDDED_VERSION); + updatesystray(); + } + #endif // SYSTRAY_PATCH + if (!XGetWindowAttributes(dpy, ev->window, &wa)) return; if (wa.override_redirect) @@ -1220,6 +1369,18 @@ propertynotify(XEvent *e) Window trans; XPropertyEvent *ev = &e->xproperty; + #if SYSTRAY_PATCH + if (showsystray && (c = wintosystrayicon(ev->window))) { + if (ev->atom == XA_WM_NORMAL_HINTS) { + updatesizehints(c); + updatesystrayicongeom(c, c->w, c->h); + } + else + updatesystrayiconstate(c, ev); + updatesystray(); + } + #endif // SYSTRAY_PATCH + if ((ev->window == root) && (ev->atom == XA_WM_NAME)) updatestatus(); else if (ev->state == PropertyDelete) @@ -1438,19 +1599,53 @@ setclientstate(Client *c, long state) } int +#if SYSTRAY_PATCH +sendevent(Window w, Atom proto, int mask, long d0, long d1, long d2, long d3, long d4) +#else sendevent(Client *c, Atom proto) +#endif // SYSTRAY_PATCH { int n; Atom *protocols; + #if SYSTRAY_PATCH + Atom mt; + #endif // SYSTRAY_PATCH int exists = 0; XEvent ev; + #if SYSTRAY_PATCH + if (proto == wmatom[WMTakeFocus] || proto == wmatom[WMDelete]) { + mt = wmatom[WMProtocols]; + if (XGetWMProtocols(dpy, w, &protocols, &n)) { + while (!exists && n--) + exists = protocols[n] == proto; + XFree(protocols); + } + } else { + exists = True; + mt = proto; + } + #else if (XGetWMProtocols(dpy, c->win, &protocols, &n)) { while (!exists && n--) exists = protocols[n] == proto; XFree(protocols); } + #endif // SYSTRAY_PATCH + if (exists) { + #if SYSTRAY_PATCH + ev.type = ClientMessage; + ev.xclient.window = w; + ev.xclient.message_type = mt; + ev.xclient.format = 32; + ev.xclient.data.l[0] = d0; + ev.xclient.data.l[1] = d1; + ev.xclient.data.l[2] = d2; + ev.xclient.data.l[3] = d3; + ev.xclient.data.l[4] = d4; + XSendEvent(dpy, w, False, mask, &ev); + #else ev.type = ClientMessage; ev.xclient.window = c->win; ev.xclient.message_type = wmatom[WMProtocols]; @@ -1458,6 +1653,7 @@ sendevent(Client *c, Atom proto) ev.xclient.data.l[0] = proto; ev.xclient.data.l[1] = CurrentTime; XSendEvent(dpy, c->win, False, NoEventMask, &ev); + #endif // SYSTRAY_PATCH } return exists; } @@ -1471,7 +1667,11 @@ setfocus(Client *c) XA_WINDOW, 32, PropModeReplace, (unsigned char *) &(c->win), 1); } + #if SYSTRAY_PATCH + sendevent(c->win, wmatom[WMTakeFocus], NoEventMask, wmatom[WMTakeFocus], CurrentTime, 0, 0, 0); + #else sendevent(c, wmatom[WMTakeFocus]); + #endif // SYSTRAY_PATCH } void @@ -1565,6 +1765,17 @@ setup(void) wmatom[WMTakeFocus] = XInternAtom(dpy, "WM_TAKE_FOCUS", False); netatom[NetActiveWindow] = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", False); netatom[NetSupported] = XInternAtom(dpy, "_NET_SUPPORTED", False); + #if SYSTRAY_PATCH + netatom[NetSystemTray] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_S0", False); + netatom[NetSystemTrayOP] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_OPCODE", False); + netatom[NetSystemTrayOrientation] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_ORIENTATION", False); + netatom[NetSystemTrayOrientationHorz] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_ORIENTATION_HORZ", False); + netatom[NetSystemTrayVisual] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_VISUAL", False); + netatom[NetWMWindowTypeDock] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DOCK", False); + xatom[Manager] = XInternAtom(dpy, "MANAGER", False); + xatom[Xembed] = XInternAtom(dpy, "_XEMBED", False); + xatom[XembedInfo] = XInternAtom(dpy, "_XEMBED_INFO", False); + #endif // SYSTRAY_PATCH 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); @@ -1584,6 +1795,11 @@ setup(void) #else scheme[i] = drw_scm_create(drw, colors[i], 3); #endif // ALPHA_PATCH + #if SYSTRAY_PATCH + /* init system tray */ + if (showsystray) + updatesystray(); + #endif // SYSTRAY_PATCH /* init bars */ updatebars(); updatestatus(); @@ -1716,6 +1932,19 @@ togglebar(const Arg *arg) selmon->showbar = !selmon->showbar; updatebarpos(selmon); XMoveResizeWindow(dpy, selmon->barwin, selmon->wx, selmon->by, selmon->ww, bh); + #if SYSTRAY_PATCH + if (showsystray) { + XWindowChanges wc; + if (!selmon->showbar) + wc.y = -bh; + else if (selmon->showbar) { + wc.y = 0; + if (!selmon->topbar) + wc.y = selmon->mh - bh; + } + XConfigureWindow(dpy, systray->win, CWY, &wc); + } + #endif // SYSTRAY_PATCH arrange(selmon); } @@ -1809,12 +2038,21 @@ unmapnotify(XEvent *e) setclientstate(c, WithdrawnState); else unmanage(c, 0); + #if SYSTRAY_PATCH + } else if (showsystray && (c = wintosystrayicon(ev->window))) { + /* KLUDGE! sometimes icons occasionally unmap their windows, but do + * _not_ destroy them. We map those windows back */ + XMapRaised(dpy, c->win); + removesystrayicon(c); + updatesystray(); + #endif // SYSTRAY_PATCH } } void updatebars(void) { + unsigned int w; Monitor *m; XSetWindowAttributes wa = { .override_redirect = True, @@ -1831,16 +2069,25 @@ updatebars(void) for (m = mons; m; m = m->next) { if (m->barwin) continue; + w = m->ww; + #if SYSTRAY_PATCH + if (showsystray && m == systraytomon(m)) + w -= getsystraywidth(); + #endif // SYSTRAY_PATCH #if ALPHA_PATCH - m->barwin = XCreateWindow(dpy, root, m->wx, m->by, m->ww, bh, 0, depth, + m->barwin = XCreateWindow(dpy, root, m->wx, m->by, w, bh, 0, depth, InputOutput, visual, CWOverrideRedirect|CWBackPixel|CWBorderPixel|CWColormap|CWEventMask, &wa); #else - m->barwin = XCreateWindow(dpy, root, m->wx, m->by, m->ww, bh, 0, DefaultDepth(dpy, screen), + m->barwin = XCreateWindow(dpy, root, m->wx, m->by, w, bh, 0, DefaultDepth(dpy, screen), CopyFromParent, DefaultVisual(dpy, screen), CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa); #endif // ALPHA_PATCH XDefineCursor(dpy, m->barwin, cursor[CurNormal]->cursor); + #if SYSTRAY_PATCH + if (showsystray && m == systraytomon(m)) + XMapRaised(dpy, systray->win); + #endif // SYSTRAY_PATCH XMapRaised(dpy, m->barwin); XSetClassHint(dpy, m->barwin, &ch); } @@ -2016,6 +2263,10 @@ updatestatus(void) if (!gettextprop(root, XA_WM_NAME, stext, sizeof(stext))) strcpy(stext, "dwm-"VERSION); drawbar(selmon); + #if SYSTRAY_PATCH + if (showsystray) + updatesystray(); + #endif // SYSTRAY_PATCH } void diff --git a/patch/include.c b/patch/include.c index de31d1e..67bbf91 100644 --- a/patch/include.c +++ b/patch/include.c @@ -1,5 +1,7 @@ - #if ALPHA_PATCH #include "alpha.c" #endif +#if SYSTRAY_PATCH +#include "systray.c" +#endif diff --git a/patch/include.h b/patch/include.h index c5c8947..5605b77 100644 --- a/patch/include.h +++ b/patch/include.h @@ -1,5 +1,7 @@ - #if ALPHA_PATCH #include "alpha.h" #endif +#if SYSTRAY_PATCH +#include "systray.h" +#endif \ No newline at end of file diff --git a/patch/systray.c b/patch/systray.c new file mode 100644 index 0000000..c96509d --- /dev/null +++ b/patch/systray.c @@ -0,0 +1,194 @@ + +static Systray *systray = NULL; +static unsigned long systrayorientation = _NET_SYSTEM_TRAY_ORIENTATION_HORZ; + +unsigned int +getsystraywidth() +{ + unsigned int w = 0; + Client *i; + if (showsystray) + for (i = systray->icons; i; w += i->w + systrayspacing, i = i->next); + return w ? w + systrayspacing : 0; +} + +void +removesystrayicon(Client *i) +{ + Client **ii; + + if (!showsystray || !i) + return; + for (ii = &systray->icons; *ii && *ii != i; ii = &(*ii)->next); + if (ii) + *ii = i->next; + free(i); +} + +void +resizerequest(XEvent *e) +{ + XResizeRequestEvent *ev = &e->xresizerequest; + Client *i; + + if ((i = wintosystrayicon(ev->window))) { + updatesystrayicongeom(i, ev->width, ev->height); + updatesystray(); + } +} + +Monitor * +systraytomon(Monitor *m) { + Monitor *t; + int i, n; + if(!systraypinning) { + if(!m) + return selmon; + return m == selmon ? m : NULL; + } + for(n = 1, t = mons; t && t->next; n++, t = t->next) ; + for(i = 1, t = mons; t && t->next && i < systraypinning; i++, t = t->next) ; + if(systraypinningfailfirst && n < systraypinning) + return mons; + return t; +} + +void +updatesystray(void) +{ + XSetWindowAttributes wa; + XWindowChanges wc; + Client *i; + Monitor *m = systraytomon(NULL); + unsigned int x = m->mx + m->mw; + unsigned int w = 1; + + if (!showsystray) + return; + if (!systray) { + /* init systray */ + if (!(systray = (Systray *)calloc(1, sizeof(Systray)))) + die("fatal: could not malloc() %u bytes\n", sizeof(Systray)); + + #if ALPHA_PATCH + XSetWindowAttributes wa = { + .override_redirect = True, + .background_pixel = scheme[SchemeNorm][ColBg].pixel, + .border_pixel = 0, + .colormap = cmap, + .event_mask = ButtonPressMask|ExposureMask + }; + systray->win = XCreateWindow(dpy, root, x, m->by, w, bh, 0, depth, + InputOutput, visual, + CWOverrideRedirect|CWBackPixel|CWBorderPixel|CWColormap|CWEventMask, &wa); + #else + systray->win = XCreateSimpleWindow(dpy, root, x, m->by, w, bh, 0, 0, scheme[SchemeSel][ColBg].pixel); + #endif // ALPHA_PATCH + XSelectInput(dpy, systray->win, SubstructureNotifyMask); + XChangeProperty(dpy, systray->win, netatom[NetSystemTrayOrientation], XA_CARDINAL, 32, + PropModeReplace, (unsigned char *)&systrayorientation, 1); + #if ALPHA_PATCH + XChangeProperty(dpy, systray->win, netatom[NetSystemTrayVisual], XA_VISUALID, 32, + PropModeReplace, (unsigned char *)&visual->visualid, 1); + #endif // ALPHA_PATCH + XChangeProperty(dpy, systray->win, netatom[NetWMWindowType], XA_ATOM, 32, + PropModeReplace, (unsigned char *)&netatom[NetWMWindowTypeDock], 1); + XMapRaised(dpy, systray->win); + XSetSelectionOwner(dpy, netatom[NetSystemTray], systray->win, CurrentTime); + if (XGetSelectionOwner(dpy, netatom[NetSystemTray]) == systray->win) { + sendevent(root, xatom[Manager], StructureNotifyMask, CurrentTime, netatom[NetSystemTray], systray->win, 0, 0); + XSync(dpy, False); + } + else { + fprintf(stderr, "dwm: unable to obtain system tray.\n"); + free(systray); + systray = NULL; + return; + } + } + for (w = 0, i = systray->icons; i; i = i->next) { + /* make sure the background color stays the same */ + wa.background_pixel = scheme[SchemeNorm][ColBg].pixel; + XChangeWindowAttributes(dpy, i->win, CWBackPixel, &wa); + XMapRaised(dpy, i->win); + w += systrayspacing; + i->x = w; + XMoveResizeWindow(dpy, i->win, i->x, 0, i->w, i->h); + w += i->w; + if (i->mon != m) + i->mon = m; + } + w = w ? w + systrayspacing : 1; + x -= w; + XMoveResizeWindow(dpy, systray->win, x, m->by, w, bh); + wc.x = x; wc.y = m->by; wc.width = w; wc.height = bh; + wc.stack_mode = Above; wc.sibling = m->barwin; + XConfigureWindow(dpy, systray->win, CWX|CWY|CWWidth|CWHeight|CWSibling|CWStackMode, &wc); + XMapWindow(dpy, systray->win); + XMapSubwindows(dpy, systray->win); + /* redraw background */ + XSetForeground(dpy, drw->gc, scheme[SchemeNorm][ColBg].pixel); + XFillRectangle(dpy, systray->win, drw->gc, 0, 0, w, bh); + XSync(dpy, False); +} + +void +updatesystrayicongeom(Client *i, int w, int h) +{ + if (i) { + i->h = bh; + if (w == h) + i->w = bh; + else if (h == bh) + i->w = w; + else + i->w = (int) ((float)bh * ((float)w / (float)h)); + applysizehints(i, &(i->x), &(i->y), &(i->w), &(i->h), False); + /* force icons into the systray dimensions if they don't want to */ + if (i->h > bh) { + if (i->w == i->h) + i->w = bh; + else + i->w = (int) ((float)bh * ((float)i->w / (float)i->h)); + i->h = bh; + } + } +} + +void +updatesystrayiconstate(Client *i, XPropertyEvent *ev) +{ + long flags; + int code = 0; + + if (!showsystray || !i || ev->atom != xatom[XembedInfo] || + !(flags = getatomprop(i, xatom[XembedInfo]))) + return; + + if (flags & XEMBED_MAPPED && !i->tags) { + i->tags = 1; + code = XEMBED_WINDOW_ACTIVATE; + XMapRaised(dpy, i->win); + setclientstate(i, NormalState); + } + else if (!(flags & XEMBED_MAPPED) && i->tags) { + i->tags = 0; + code = XEMBED_WINDOW_DEACTIVATE; + XUnmapWindow(dpy, i->win); + setclientstate(i, WithdrawnState); + } + else + return; + sendevent(i->win, xatom[Xembed], StructureNotifyMask, CurrentTime, code, 0, + systray->win, XEMBED_EMBEDDED_VERSION); +} + +Client * +wintosystrayicon(Window w) { + Client *i = NULL; + + if (!showsystray || !w) + return i; + for (i = systray->icons; i && i->win != w; i = i->next); + return i; +} \ No newline at end of file diff --git a/patch/systray.h b/patch/systray.h new file mode 100644 index 0000000..1a2bab6 --- /dev/null +++ b/patch/systray.h @@ -0,0 +1,37 @@ +#define SYSTEM_TRAY_REQUEST_DOCK 0 +#define _NET_SYSTEM_TRAY_ORIENTATION_HORZ 0 + +/* XEMBED messages */ +#define XEMBED_EMBEDDED_NOTIFY 0 +#define XEMBED_WINDOW_ACTIVATE 1 +#define XEMBED_FOCUS_IN 4 +#define XEMBED_MODALITY_ON 10 + +#define XEMBED_MAPPED (1 << 0) +#define XEMBED_WINDOW_ACTIVATE 1 +#define XEMBED_WINDOW_DEACTIVATE 2 + +#define VERSION_MAJOR 0 +#define VERSION_MINOR 0 +#define XEMBED_EMBEDDED_VERSION (VERSION_MAJOR << 16) | VERSION_MINOR + +/* enums */ +enum { Manager, Xembed, XembedInfo, XLast }; /* Xembed atoms */ + +typedef struct Systray Systray; +struct Systray { + Window win; + Client *icons; +}; + +/* function declarations */ +static Atom getatomprop(Client *c, Atom prop); +static unsigned int getsystraywidth(); +static void removesystrayicon(Client *i); +static void resizerequest(XEvent *e); +static Monitor *systraytomon(Monitor *m); +static void updatesystray(void); +static void updatesystrayicongeom(Client *i, int w, int h); +static void updatesystrayiconstate(Client *i, XPropertyEvent *ev); +static Client *wintosystrayicon(Window w); + diff --git a/patches.h b/patches.h index c683143..d998b53 100644 --- a/patches.h +++ b/patches.h @@ -11,4 +11,10 @@ * The alpha patch adds transparency for the status bar. * https://dwm.suckless.org/patches/alpha/ */ -#define ALPHA_PATCH 0 \ No newline at end of file +#define ALPHA_PATCH 0 + +/* + * The systray patch adds systray for the status bar. + * https://dwm.suckless.org/patches/systray/ + */ +#define SYSTRAY_PATCH 0 \ No newline at end of file