Adding systray patch

This commit is contained in:
bakkeby 2019-09-05 22:10:00 +02:00
parent 2c9a889b22
commit cba0991170
8 changed files with 512 additions and 10 deletions

View File

@ -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 ```c
#define ALPHA_PATCH 1 #define ALPHA_PATCH 1
``` ```
@ -11,9 +11,13 @@ Refer to [https://dwm.suckless.org/](https://dwm.suckless.org/) for details on t
### Changelog: ### Changelog:
2019-09-05 - Systray patch added
2019-09-05 - Alpha patch added 2019-09-05 - Alpha patch added
### Patches included: ### Patches included:
- [alpha](https://dwm.suckless.org/patches/alpha/) - [alpha](https://dwm.suckless.org/patches/alpha/)
- adds transparency for the status bar - adds transparency for the status bar
- [systray](https://dwm.suckless.org/patches/systray/)
- adds system tray in the status bar

View File

@ -5,6 +5,12 @@ static const unsigned int borderpx = 1; /* border pixel of windows */
static const unsigned int snap = 32; /* snap pixel */ static const unsigned int snap = 32; /* snap pixel */
static const int showbar = 1; /* 0 means no bar */ static const int showbar = 1; /* 0 means no bar */
static const int topbar = 1; /* 0 means bottom 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 *fonts[] = { "monospace:size=10" };
static const char dmenufont[] = "monospace:size=10"; static const char dmenufont[] = "monospace:size=10";
static const char col_gray1[] = "#222222"; static const char col_gray1[] = "#222222";

261
dwm.c
View File

@ -61,9 +61,15 @@
/* enums */ /* enums */
enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */ enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */
enum { SchemeNorm, SchemeSel }; /* color schemes */ 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, enum { NetSupported, NetWMName, NetWMState, NetWMCheck,
NetWMFullscreen, NetActiveWindow, NetWMWindowType, NetWMFullscreen, NetActiveWindow, NetWMWindowType,
NetWMWindowTypeDialog, NetClientList, NetLast }; /* EWMH atoms */ NetWMWindowTypeDialog, NetClientList, NetLast }; /* EWMH atoms */
#endif // SYSTRAY_PATCH
enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */ enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */
enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle,
ClkClientWin, ClkRootWin, ClkLast }; /* clicks */ ClkClientWin, ClkRootWin, ClkLast }; /* clicks */
@ -195,7 +201,11 @@ static void resizemouse(const Arg *arg);
static void restack(Monitor *m); static void restack(Monitor *m);
static void run(void); static void run(void);
static void scan(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); static int sendevent(Client *c, Atom proto);
#endif // SYSTRAY_PATCH
static void sendmon(Client *c, Monitor *m); static void sendmon(Client *c, Monitor *m);
static void setclientstate(Client *c, long state); static void setclientstate(Client *c, long state);
static void setfocus(Client *c); static void setfocus(Client *c);
@ -235,6 +245,8 @@ static int xerrordummy(Display *dpy, XErrorEvent *ee);
static int xerrorstart(Display *dpy, XErrorEvent *ee); static int xerrorstart(Display *dpy, XErrorEvent *ee);
static void zoom(const Arg *arg); static void zoom(const Arg *arg);
#include "patch/include.h"
/* variables */ /* variables */
static const char broken[] = "broken"; static const char broken[] = "broken";
static char stext[256]; static char stext[256];
@ -258,9 +270,16 @@ static void (*handler[LASTEvent]) (XEvent *) = {
[MapRequest] = maprequest, [MapRequest] = maprequest,
[MotionNotify] = motionnotify, [MotionNotify] = motionnotify,
[PropertyNotify] = propertynotify, [PropertyNotify] = propertynotify,
#if SYSTRAY_PATCH
[ResizeRequest] = resizerequest,
#endif // SYSTRAY_PATCH
[UnmapNotify] = unmapnotify [UnmapNotify] = unmapnotify
}; };
#if SYSTRAY_PATCH
static Atom wmatom[WMLast], netatom[NetLast], xatom[XLast];
#else
static Atom wmatom[WMLast], netatom[NetLast]; static Atom wmatom[WMLast], netatom[NetLast];
#endif // SYSTRAY_PATCH
static int running = 1; static int running = 1;
static Cur *cursor[CurLast]; static Cur *cursor[CurLast];
static Clr **scheme; static Clr **scheme;
@ -269,8 +288,6 @@ static Drw *drw;
static Monitor *mons, *selmon; static Monitor *mons, *selmon;
static Window root, wmcheckwin; static Window root, wmcheckwin;
#include "patch/include.h"
/* configuration, allows nested code to access above variables */ /* configuration, allows nested code to access above variables */
#include "config.h" #include "config.h"
@ -444,7 +461,11 @@ buttonpress(XEvent *e)
arg.ui = 1 << i; arg.ui = 1 << i;
} else if (ev->x < x + blw) } else if (ev->x < x + blw)
click = ClkLtSymbol; click = ClkLtSymbol;
#if SYSTRAY_PATCH
else if (ev->x > selmon->ww - TEXTW(stext) - getsystraywidth())
#else
else if (ev->x > selmon->ww - TEXTW(stext)) else if (ev->x > selmon->ww - TEXTW(stext))
#endif // SYSTRAY_PATCH
click = ClkStatusText; click = ClkStatusText;
else else
click = ClkWinTitle; click = ClkWinTitle;
@ -487,6 +508,13 @@ cleanup(void)
XUngrabKey(dpy, AnyKey, AnyModifier, root); XUngrabKey(dpy, AnyKey, AnyModifier, root);
while (mons) while (mons)
cleanupmon(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++) for (i = 0; i < CurLast; i++)
drw_cur_free(drw, cursor[i]); drw_cur_free(drw, cursor[i]);
for (i = 0; i < LENGTH(colors); i++) for (i = 0; i < LENGTH(colors); i++)
@ -517,9 +545,58 @@ cleanupmon(Monitor *mon)
void void
clientmessage(XEvent *e) clientmessage(XEvent *e)
{ {
#if SYSTRAY_PATCH
XWindowAttributes wa;
XSetWindowAttributes swa;
#endif // SYSTRAY_PATCH
XClientMessageEvent *cme = &e->xclient; XClientMessageEvent *cme = &e->xclient;
Client *c = wintoclient(cme->window); 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) if (!c)
return; return;
if (cme->message_type == netatom[NetWMState]) { if (cme->message_type == netatom[NetWMState]) {
@ -657,6 +734,12 @@ destroynotify(XEvent *e)
if ((c = wintoclient(ev->window))) if ((c = wintoclient(ev->window)))
unmanage(c, 1); unmanage(c, 1);
#if SYSTRAY_PATCH
else if (showsystray && (c = wintosystrayicon(ev->window))) {
removesystrayicon(c);
updatesystray();
}
#endif // SYSTRAY_PATCH
} }
void void
@ -701,16 +784,28 @@ void
drawbar(Monitor *m) drawbar(Monitor *m)
{ {
int x, w, sw = 0; int x, w, sw = 0;
#if SYSTRAY_PATCH
int stw = 0;
#endif // SYSTRAY_PATCH
int boxs = drw->fonts->h / 9; int boxs = drw->fonts->h / 9;
int boxw = drw->fonts->h / 6 + 2; int boxw = drw->fonts->h / 6 + 2;
unsigned int i, occ = 0, urg = 0; unsigned int i, occ = 0, urg = 0;
Client *c; 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 */ /* draw status first so it can be overdrawn by tags later */
if (m == selmon) { /* status is only drawn on selected monitor */ if (m == selmon) { /* status is only drawn on selected monitor */
drw_setscheme(drw, scheme[SchemeNorm]); drw_setscheme(drw, scheme[SchemeNorm]);
sw = TEXTW(stext) - lrpad + 2; /* 2px right padding */ 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); drw_text(drw, m->ww - sw, 0, sw, bh, 0, stext, 0);
#endif // SYSTRAY_PATCH
} }
for (c = m->clients; c; c = c->next) { for (c = m->clients; c; c = c->next) {
@ -733,7 +828,11 @@ drawbar(Monitor *m)
drw_setscheme(drw, scheme[SchemeNorm]); drw_setscheme(drw, scheme[SchemeNorm]);
x = drw_text(drw, x, 0, w, bh, lrpad / 2, m->ltsymbol, 0); 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) { if ((w = m->ww - sw - x) > bh) {
#endif // SYSTRAY_PATCH
if (m->sel) { if (m->sel) {
drw_setscheme(drw, scheme[m == selmon ? SchemeSel : SchemeNorm]); drw_setscheme(drw, scheme[m == selmon ? SchemeSel : SchemeNorm]);
drw_text(drw, x, 0, w, bh, lrpad / 2, m->sel->name, 0); 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); 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); drw_map(drw, m->barwin, 0, 0, m->ww, bh);
#endif // SYSTRAY_PATCH
} }
void void
@ -781,8 +884,13 @@ expose(XEvent *e)
Monitor *m; Monitor *m;
XExposeEvent *ev = &e->xexpose; XExposeEvent *ev = &e->xexpose;
if (ev->count == 0 && (m = wintomon(ev->window))) if (ev->count == 0 && (m = wintomon(ev->window))) {
drawbar(m); drawbar(m);
#if SYSTRAY_PATCH
if (showsystray && m == selmon)
updatesystray();
#endif // SYSTRAY_PATCH
}
} }
void void
@ -816,8 +924,21 @@ focusin(XEvent *e)
{ {
XFocusChangeEvent *ev = &e->xfocus; 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) if (selmon->sel && ev->window != selmon->sel->win)
setfocus(selmon->sel); setfocus(selmon->sel);
#if SYSTRAY_PATCH
if (showsystray)
updatesystray();
#endif // SYSTRAY_PATCH
} }
void void
@ -868,11 +989,27 @@ getatomprop(Client *c, Atom prop)
unsigned char *p = NULL; unsigned char *p = NULL;
Atom da, atom = None; 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, if (XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, XA_ATOM,
&da, &di, &dl, &dl, &p) == Success && p) { &da, &di, &dl, &dl, &p) == Success && p) {
atom = *(Atom *)p; atom = *(Atom *)p;
XFree(p); XFree(p);
} }
#endif // SYSTRAY_PATCH
return atom; return atom;
} }
@ -1008,7 +1145,11 @@ killclient(const Arg *arg)
{ {
if (!selmon->sel) if (!selmon->sel)
return; 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])) { if (!sendevent(selmon->sel, wmatom[WMDelete])) {
#endif // SYSTRAY_PATCH
XGrabServer(dpy); XGrabServer(dpy);
XSetErrorHandler(xerrordummy); XSetErrorHandler(xerrordummy);
XSetCloseDownMode(dpy, DestroyAll); XSetCloseDownMode(dpy, DestroyAll);
@ -1097,6 +1238,14 @@ maprequest(XEvent *e)
static XWindowAttributes wa; static XWindowAttributes wa;
XMapRequestEvent *ev = &e->xmaprequest; 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)) if (!XGetWindowAttributes(dpy, ev->window, &wa))
return; return;
if (wa.override_redirect) if (wa.override_redirect)
@ -1220,6 +1369,18 @@ propertynotify(XEvent *e)
Window trans; Window trans;
XPropertyEvent *ev = &e->xproperty; 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)) if ((ev->window == root) && (ev->atom == XA_WM_NAME))
updatestatus(); updatestatus();
else if (ev->state == PropertyDelete) else if (ev->state == PropertyDelete)
@ -1438,19 +1599,53 @@ setclientstate(Client *c, long state)
} }
int 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) sendevent(Client *c, Atom proto)
#endif // SYSTRAY_PATCH
{ {
int n; int n;
Atom *protocols; Atom *protocols;
#if SYSTRAY_PATCH
Atom mt;
#endif // SYSTRAY_PATCH
int exists = 0; int exists = 0;
XEvent ev; 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)) { if (XGetWMProtocols(dpy, c->win, &protocols, &n)) {
while (!exists && n--) while (!exists && n--)
exists = protocols[n] == proto; exists = protocols[n] == proto;
XFree(protocols); XFree(protocols);
} }
#endif // SYSTRAY_PATCH
if (exists) { 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.type = ClientMessage;
ev.xclient.window = c->win; ev.xclient.window = c->win;
ev.xclient.message_type = wmatom[WMProtocols]; 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[0] = proto;
ev.xclient.data.l[1] = CurrentTime; ev.xclient.data.l[1] = CurrentTime;
XSendEvent(dpy, c->win, False, NoEventMask, &ev); XSendEvent(dpy, c->win, False, NoEventMask, &ev);
#endif // SYSTRAY_PATCH
} }
return exists; return exists;
} }
@ -1471,7 +1667,11 @@ setfocus(Client *c)
XA_WINDOW, 32, PropModeReplace, XA_WINDOW, 32, PropModeReplace,
(unsigned char *) &(c->win), 1); (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]); sendevent(c, wmatom[WMTakeFocus]);
#endif // SYSTRAY_PATCH
} }
void void
@ -1565,6 +1765,17 @@ setup(void)
wmatom[WMTakeFocus] = XInternAtom(dpy, "WM_TAKE_FOCUS", False); wmatom[WMTakeFocus] = XInternAtom(dpy, "WM_TAKE_FOCUS", False);
netatom[NetActiveWindow] = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", False); netatom[NetActiveWindow] = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", False);
netatom[NetSupported] = XInternAtom(dpy, "_NET_SUPPORTED", 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[NetWMName] = XInternAtom(dpy, "_NET_WM_NAME", False);
netatom[NetWMState] = XInternAtom(dpy, "_NET_WM_STATE", False); netatom[NetWMState] = XInternAtom(dpy, "_NET_WM_STATE", False);
netatom[NetWMCheck] = XInternAtom(dpy, "_NET_SUPPORTING_WM_CHECK", False); netatom[NetWMCheck] = XInternAtom(dpy, "_NET_SUPPORTING_WM_CHECK", False);
@ -1584,6 +1795,11 @@ setup(void)
#else #else
scheme[i] = drw_scm_create(drw, colors[i], 3); scheme[i] = drw_scm_create(drw, colors[i], 3);
#endif // ALPHA_PATCH #endif // ALPHA_PATCH
#if SYSTRAY_PATCH
/* init system tray */
if (showsystray)
updatesystray();
#endif // SYSTRAY_PATCH
/* init bars */ /* init bars */
updatebars(); updatebars();
updatestatus(); updatestatus();
@ -1716,6 +1932,19 @@ togglebar(const Arg *arg)
selmon->showbar = !selmon->showbar; selmon->showbar = !selmon->showbar;
updatebarpos(selmon); updatebarpos(selmon);
XMoveResizeWindow(dpy, selmon->barwin, selmon->wx, selmon->by, selmon->ww, bh); 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); arrange(selmon);
} }
@ -1809,12 +2038,21 @@ unmapnotify(XEvent *e)
setclientstate(c, WithdrawnState); setclientstate(c, WithdrawnState);
else else
unmanage(c, 0); 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 void
updatebars(void) updatebars(void)
{ {
unsigned int w;
Monitor *m; Monitor *m;
XSetWindowAttributes wa = { XSetWindowAttributes wa = {
.override_redirect = True, .override_redirect = True,
@ -1831,16 +2069,25 @@ updatebars(void)
for (m = mons; m; m = m->next) { for (m = mons; m; m = m->next) {
if (m->barwin) if (m->barwin)
continue; continue;
w = m->ww;
#if SYSTRAY_PATCH
if (showsystray && m == systraytomon(m))
w -= getsystraywidth();
#endif // SYSTRAY_PATCH
#if ALPHA_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, InputOutput, visual,
CWOverrideRedirect|CWBackPixel|CWBorderPixel|CWColormap|CWEventMask, &wa); CWOverrideRedirect|CWBackPixel|CWBorderPixel|CWColormap|CWEventMask, &wa);
#else #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), CopyFromParent, DefaultVisual(dpy, screen),
CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa); CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa);
#endif // ALPHA_PATCH #endif // ALPHA_PATCH
XDefineCursor(dpy, m->barwin, cursor[CurNormal]->cursor); 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); XMapRaised(dpy, m->barwin);
XSetClassHint(dpy, m->barwin, &ch); XSetClassHint(dpy, m->barwin, &ch);
} }
@ -2016,6 +2263,10 @@ updatestatus(void)
if (!gettextprop(root, XA_WM_NAME, stext, sizeof(stext))) if (!gettextprop(root, XA_WM_NAME, stext, sizeof(stext)))
strcpy(stext, "dwm-"VERSION); strcpy(stext, "dwm-"VERSION);
drawbar(selmon); drawbar(selmon);
#if SYSTRAY_PATCH
if (showsystray)
updatesystray();
#endif // SYSTRAY_PATCH
} }
void void

View File

@ -1,5 +1,7 @@
#if ALPHA_PATCH #if ALPHA_PATCH
#include "alpha.c" #include "alpha.c"
#endif #endif
#if SYSTRAY_PATCH
#include "systray.c"
#endif

View File

@ -1,5 +1,7 @@
#if ALPHA_PATCH #if ALPHA_PATCH
#include "alpha.h" #include "alpha.h"
#endif #endif
#if SYSTRAY_PATCH
#include "systray.h"
#endif

194
patch/systray.c Normal file
View File

@ -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;
}

37
patch/systray.h Normal file
View File

@ -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);

View File

@ -12,3 +12,9 @@
* https://dwm.suckless.org/patches/alpha/ * https://dwm.suckless.org/patches/alpha/
*/ */
#define ALPHA_PATCH 0 #define ALPHA_PATCH 0
/*
* The systray patch adds systray for the status bar.
* https://dwm.suckless.org/patches/systray/
*/
#define SYSTRAY_PATCH 0