01_dwm-systray-6.2.diff (23966B)
1https://dwm.suckless.org/patches/systray/ 2 3From 4001ccae7b1a41bdcb247b0cf095a51af7b68c28 Mon Sep 17 00:00:00 2001 4From: Igor Gevka <igor.gevka@gmail.com> 5Date: Sun, 16 Feb 2020 15:03:10 -0800 6Subject: [PATCH] [PATCH] Implements a system tray for dwm. 7 8Original author: Jan Christoph Ebersbach <jceb@e-jc.de>, inspired by http://code.google.com/p/dwm-plus 9URL: http://dwm.suckless.org/patches/systray 10dwm 6.2 port by Igor Gevka <igor.gevka@gmail.com> 11--- 12 config.def.h | 4 + 13 dwm.c | 404 +++++++++++++++++++++++++++++++++++++++++++++++---- 14 2 files changed, 382 insertions(+), 26 deletions(-) 15 16diff --git a/config.def.h b/config.def.h 17index 1c0b587..2d824d1 100644 18--- a/config.def.h 19+++ b/config.def.h 20@@ -3,6 +3,10 @@ 21 /* appearance */ 22 static const unsigned int borderpx = 1; /* border pixel of windows */ 23 static const unsigned int snap = 32; /* snap pixel */ 24+static const unsigned int systraypinning = 0; /* 0: sloppy systray follows selected monitor, >0: pin systray to monitor X */ 25+static const unsigned int systrayspacing = 2; /* systray spacing */ 26+static const int systraypinningfailfirst = 1; /* 1: if pinning fails, display systray on the first monitor, False: display systray on the last monitor*/ 27+static const int showsystray = 1; /* 0 means no systray */ 28 static const int showbar = 1; /* 0 means no bar */ 29 static const int topbar = 1; /* 0 means bottom bar */ 30 static const char *fonts[] = { "monospace:size=10" }; 31diff --git a/dwm.c b/dwm.c 32index 4465af1..3e361fa 100644 33--- a/dwm.c 34+++ b/dwm.c 35@@ -57,12 +57,30 @@ 36 #define TAGMASK ((1 << LENGTH(tags)) - 1) 37 #define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad) 38 39+#define SYSTEM_TRAY_REQUEST_DOCK 0 40+ 41+/* XEMBED messages */ 42+#define XEMBED_EMBEDDED_NOTIFY 0 43+#define XEMBED_WINDOW_ACTIVATE 1 44+#define XEMBED_FOCUS_IN 4 45+#define XEMBED_MODALITY_ON 10 46+ 47+#define XEMBED_MAPPED (1 << 0) 48+#define XEMBED_WINDOW_ACTIVATE 1 49+#define XEMBED_WINDOW_DEACTIVATE 2 50+ 51+#define VERSION_MAJOR 0 52+#define VERSION_MINOR 0 53+#define XEMBED_EMBEDDED_VERSION (VERSION_MAJOR << 16) | VERSION_MINOR 54+ 55 /* enums */ 56 enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */ 57 enum { SchemeNorm, SchemeSel }; /* color schemes */ 58 enum { NetSupported, NetWMName, NetWMState, NetWMCheck, 59+ NetSystemTray, NetSystemTrayOP, NetSystemTrayOrientation, NetSystemTrayOrientationHorz, 60 NetWMFullscreen, NetActiveWindow, NetWMWindowType, 61 NetWMWindowTypeDialog, NetClientList, NetLast }; /* EWMH atoms */ 62+enum { Manager, Xembed, XembedInfo, XLast }; /* Xembed atoms */ 63 enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */ 64 enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, 65 ClkClientWin, ClkRootWin, ClkLast }; /* clicks */ 66@@ -141,6 +159,12 @@ typedef struct { 67 int monitor; 68 } Rule; 69 70+typedef struct Systray Systray; 71+struct Systray { 72+ Window win; 73+ Client *icons; 74+}; 75+ 76 /* function declarations */ 77 static void applyrules(Client *c); 78 static int applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact); 79@@ -169,8 +193,10 @@ static void focus(Client *c); 80 static void focusin(XEvent *e); 81 static void focusmon(const Arg *arg); 82 static void focusstack(const Arg *arg); 83+static Atom getatomprop(Client *c, Atom prop); 84 static int getrootptr(int *x, int *y); 85 static long getstate(Window w); 86+static unsigned int getsystraywidth(); 87 static int gettextprop(Window w, Atom atom, char *text, unsigned int size); 88 static void grabbuttons(Client *c, int focused); 89 static void grabkeys(void); 90@@ -188,13 +214,16 @@ static void pop(Client *); 91 static void propertynotify(XEvent *e); 92 static void quit(const Arg *arg); 93 static Monitor *recttomon(int x, int y, int w, int h); 94+static void removesystrayicon(Client *i); 95 static void resize(Client *c, int x, int y, int w, int h, int interact); 96+static void resizebarwin(Monitor *m); 97 static void resizeclient(Client *c, int x, int y, int w, int h); 98 static void resizemouse(const Arg *arg); 99+static void resizerequest(XEvent *e); 100 static void restack(Monitor *m); 101 static void run(void); 102 static void scan(void); 103-static int sendevent(Client *c, Atom proto); 104+static int sendevent(Window w, Atom proto, int m, long d0, long d1, long d2, long d3, long d4); 105 static void sendmon(Client *c, Monitor *m); 106 static void setclientstate(Client *c, long state); 107 static void setfocus(Client *c); 108@@ -206,6 +235,7 @@ static void seturgent(Client *c, int urg); 109 static void showhide(Client *c); 110 static void sigchld(int unused); 111 static void spawn(const Arg *arg); 112+static Monitor *systraytomon(Monitor *m); 113 static void tag(const Arg *arg); 114 static void tagmon(const Arg *arg); 115 static void tile(Monitor *); 116@@ -223,18 +253,23 @@ static int updategeom(void); 117 static void updatenumlockmask(void); 118 static void updatesizehints(Client *c); 119 static void updatestatus(void); 120+static void updatesystray(void); 121+static void updatesystrayicongeom(Client *i, int w, int h); 122+static void updatesystrayiconstate(Client *i, XPropertyEvent *ev); 123 static void updatetitle(Client *c); 124 static void updatewindowtype(Client *c); 125 static void updatewmhints(Client *c); 126 static void view(const Arg *arg); 127 static Client *wintoclient(Window w); 128 static Monitor *wintomon(Window w); 129+static Client *wintosystrayicon(Window w); 130 static int xerror(Display *dpy, XErrorEvent *ee); 131 static int xerrordummy(Display *dpy, XErrorEvent *ee); 132 static int xerrorstart(Display *dpy, XErrorEvent *ee); 133 static void zoom(const Arg *arg); 134 135 /* variables */ 136+static Systray *systray = NULL; 137 static const char broken[] = "broken"; 138 static char stext[256]; 139 static int screen; 140@@ -257,9 +292,10 @@ static void (*handler[LASTEvent]) (XEvent *) = { 141 [MapRequest] = maprequest, 142 [MotionNotify] = motionnotify, 143 [PropertyNotify] = propertynotify, 144+ [ResizeRequest] = resizerequest, 145 [UnmapNotify] = unmapnotify 146 }; 147-static Atom wmatom[WMLast], netatom[NetLast]; 148+static Atom wmatom[WMLast], netatom[NetLast], xatom[XLast]; 149 static int running = 1; 150 static Cur *cursor[CurLast]; 151 static Clr **scheme; 152@@ -439,7 +475,7 @@ buttonpress(XEvent *e) 153 arg.ui = 1 << i; 154 } else if (ev->x < x + blw) 155 click = ClkLtSymbol; 156- else if (ev->x > selmon->ww - TEXTW(stext)) 157+ else if (ev->x > selmon->ww - TEXTW(stext) - getsystraywidth()) 158 click = ClkStatusText; 159 else 160 click = ClkWinTitle; 161@@ -482,6 +518,11 @@ cleanup(void) 162 XUngrabKey(dpy, AnyKey, AnyModifier, root); 163 while (mons) 164 cleanupmon(mons); 165+ if (showsystray) { 166+ XUnmapWindow(dpy, systray->win); 167+ XDestroyWindow(dpy, systray->win); 168+ free(systray); 169+ } 170 for (i = 0; i < CurLast; i++) 171 drw_cur_free(drw, cursor[i]); 172 for (i = 0; i < LENGTH(colors); i++) 173@@ -512,9 +553,57 @@ cleanupmon(Monitor *mon) 174 void 175 clientmessage(XEvent *e) 176 { 177+ XWindowAttributes wa; 178+ XSetWindowAttributes swa; 179 XClientMessageEvent *cme = &e->xclient; 180 Client *c = wintoclient(cme->window); 181 182+ if (showsystray && cme->window == systray->win && cme->message_type == netatom[NetSystemTrayOP]) { 183+ /* add systray icons */ 184+ if (cme->data.l[1] == SYSTEM_TRAY_REQUEST_DOCK) { 185+ if (!(c = (Client *)calloc(1, sizeof(Client)))) 186+ die("fatal: could not malloc() %u bytes\n", sizeof(Client)); 187+ if (!(c->win = cme->data.l[2])) { 188+ free(c); 189+ return; 190+ } 191+ c->mon = selmon; 192+ c->next = systray->icons; 193+ systray->icons = c; 194+ if (!XGetWindowAttributes(dpy, c->win, &wa)) { 195+ /* use sane defaults */ 196+ wa.width = bh; 197+ wa.height = bh; 198+ wa.border_width = 0; 199+ } 200+ c->x = c->oldx = c->y = c->oldy = 0; 201+ c->w = c->oldw = wa.width; 202+ c->h = c->oldh = wa.height; 203+ c->oldbw = wa.border_width; 204+ c->bw = 0; 205+ c->isfloating = True; 206+ /* reuse tags field as mapped status */ 207+ c->tags = 1; 208+ updatesizehints(c); 209+ updatesystrayicongeom(c, wa.width, wa.height); 210+ XAddToSaveSet(dpy, c->win); 211+ XSelectInput(dpy, c->win, StructureNotifyMask | PropertyChangeMask | ResizeRedirectMask); 212+ XReparentWindow(dpy, c->win, systray->win, 0, 0); 213+ /* use parents background color */ 214+ swa.background_pixel = scheme[SchemeNorm][ColBg].pixel; 215+ XChangeWindowAttributes(dpy, c->win, CWBackPixel, &swa); 216+ sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_EMBEDDED_NOTIFY, 0 , systray->win, XEMBED_EMBEDDED_VERSION); 217+ /* FIXME not sure if I have to send these events, too */ 218+ sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_FOCUS_IN, 0 , systray->win, XEMBED_EMBEDDED_VERSION); 219+ sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_WINDOW_ACTIVATE, 0 , systray->win, XEMBED_EMBEDDED_VERSION); 220+ sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_MODALITY_ON, 0 , systray->win, XEMBED_EMBEDDED_VERSION); 221+ XSync(dpy, False); 222+ resizebarwin(selmon); 223+ updatesystray(); 224+ setclientstate(c, NormalState); 225+ } 226+ return; 227+ } 228 if (!c) 229 return; 230 if (cme->message_type == netatom[NetWMState]) { 231@@ -567,7 +656,7 @@ configurenotify(XEvent *e) 232 for (c = m->clients; c; c = c->next) 233 if (c->isfullscreen) 234 resizeclient(c, m->mx, m->my, m->mw, m->mh); 235- XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, m->ww, bh); 236+ resizebarwin(m); 237 } 238 focus(NULL); 239 arrange(NULL); 240@@ -652,6 +741,11 @@ destroynotify(XEvent *e) 241 242 if ((c = wintoclient(ev->window))) 243 unmanage(c, 1); 244+ else if ((c = wintosystrayicon(ev->window))) { 245+ removesystrayicon(c); 246+ resizebarwin(selmon); 247+ updatesystray(); 248+ } 249 } 250 251 void 252@@ -695,19 +789,23 @@ dirtomon(int dir) 253 void 254 drawbar(Monitor *m) 255 { 256- int x, w, sw = 0; 257+ int x, w, sw = 0, stw = 0; 258 int boxs = drw->fonts->h / 9; 259 int boxw = drw->fonts->h / 6 + 2; 260 unsigned int i, occ = 0, urg = 0; 261 Client *c; 262 263+ if(showsystray && m == systraytomon(m)) 264+ stw = getsystraywidth(); 265+ 266 /* draw status first so it can be overdrawn by tags later */ 267 if (m == selmon) { /* status is only drawn on selected monitor */ 268 drw_setscheme(drw, scheme[SchemeNorm]); 269- sw = TEXTW(stext) - lrpad + 2; /* 2px right padding */ 270- drw_text(drw, m->ww - sw, 0, sw, bh, 0, stext, 0); 271+ sw = TEXTW(stext) - lrpad / 2 + 2; /* 2px right padding */ 272+ drw_text(drw, m->ww - sw - stw, 0, sw, bh, lrpad / 2 - 2, stext, 0); 273 } 274 275+ resizebarwin(m); 276 for (c = m->clients; c; c = c->next) { 277 occ |= c->tags; 278 if (c->isurgent) 279@@ -728,7 +826,7 @@ drawbar(Monitor *m) 280 drw_setscheme(drw, scheme[SchemeNorm]); 281 x = drw_text(drw, x, 0, w, bh, lrpad / 2, m->ltsymbol, 0); 282 283- if ((w = m->ww - sw - x) > bh) { 284+ if ((w = m->ww - sw - stw - x) > bh) { 285 if (m->sel) { 286 drw_setscheme(drw, scheme[m == selmon ? SchemeSel : SchemeNorm]); 287 drw_text(drw, x, 0, w, bh, lrpad / 2, m->sel->name, 0); 288@@ -739,7 +837,7 @@ drawbar(Monitor *m) 289 drw_rect(drw, x, 0, w, bh, 1, 1); 290 } 291 } 292- drw_map(drw, m->barwin, 0, 0, m->ww, bh); 293+ drw_map(drw, m->barwin, 0, 0, m->ww - stw, bh); 294 } 295 296 void 297@@ -776,8 +874,11 @@ expose(XEvent *e) 298 Monitor *m; 299 XExposeEvent *ev = &e->xexpose; 300 301- if (ev->count == 0 && (m = wintomon(ev->window))) 302+ if (ev->count == 0 && (m = wintomon(ev->window))) { 303 drawbar(m); 304+ if (m == selmon) 305+ updatesystray(); 306+ } 307 } 308 309 void 310@@ -862,10 +963,17 @@ getatomprop(Client *c, Atom prop) 311 unsigned long dl; 312 unsigned char *p = NULL; 313 Atom da, atom = None; 314+ /* FIXME getatomprop should return the number of items and a pointer to 315+ * the stored data instead of this workaround */ 316+ Atom req = XA_ATOM; 317+ if (prop == xatom[XembedInfo]) 318+ req = xatom[XembedInfo]; 319 320- if (XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, XA_ATOM, 321+ if (XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, req, 322 &da, &di, &dl, &dl, &p) == Success && p) { 323 atom = *(Atom *)p; 324+ if (da == xatom[XembedInfo] && dl == 2) 325+ atom = ((Atom *)p)[1]; 326 XFree(p); 327 } 328 return atom; 329@@ -899,6 +1007,16 @@ getstate(Window w) 330 return result; 331 } 332 333+unsigned int 334+getsystraywidth() 335+{ 336+ unsigned int w = 0; 337+ Client *i; 338+ if(showsystray) 339+ for(i = systray->icons; i; w += i->w + systrayspacing, i = i->next) ; 340+ return w ? w + systrayspacing : 1; 341+} 342+ 343 int 344 gettextprop(Window w, Atom atom, char *text, unsigned int size) 345 { 346@@ -1003,7 +1121,7 @@ killclient(const Arg *arg) 347 { 348 if (!selmon->sel) 349 return; 350- if (!sendevent(selmon->sel, wmatom[WMDelete])) { 351+ if (!sendevent(selmon->sel->win, wmatom[WMDelete], NoEventMask, wmatom[WMDelete], CurrentTime, 0 , 0, 0)) { 352 XGrabServer(dpy); 353 XSetErrorHandler(xerrordummy); 354 XSetCloseDownMode(dpy, DestroyAll); 355@@ -1091,6 +1209,12 @@ maprequest(XEvent *e) 356 { 357 static XWindowAttributes wa; 358 XMapRequestEvent *ev = &e->xmaprequest; 359+ Client *i; 360+ if ((i = wintosystrayicon(ev->window))) { 361+ sendevent(i->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_WINDOW_ACTIVATE, 0, systray->win, XEMBED_EMBEDDED_VERSION); 362+ resizebarwin(selmon); 363+ updatesystray(); 364+ } 365 366 if (!XGetWindowAttributes(dpy, ev->window, &wa)) 367 return; 368@@ -1215,6 +1339,16 @@ propertynotify(XEvent *e) 369 Window trans; 370 XPropertyEvent *ev = &e->xproperty; 371 372+ if ((c = wintosystrayicon(ev->window))) { 373+ if (ev->atom == XA_WM_NORMAL_HINTS) { 374+ updatesizehints(c); 375+ updatesystrayicongeom(c, c->w, c->h); 376+ } 377+ else 378+ updatesystrayiconstate(c, ev); 379+ resizebarwin(selmon); 380+ updatesystray(); 381+ } 382 if ((ev->window == root) && (ev->atom == XA_WM_NAME)) 383 updatestatus(); 384 else if (ev->state == PropertyDelete) 385@@ -1265,6 +1399,20 @@ recttomon(int x, int y, int w, int h) 386 return r; 387 } 388 389+void 390+removesystrayicon(Client *i) 391+{ 392+ Client **ii; 393+ 394+ if (!showsystray || !i) 395+ return; 396+ for (ii = &systray->icons; *ii && *ii != i; ii = &(*ii)->next); 397+ if (ii) 398+ *ii = i->next; 399+ free(i); 400+} 401+ 402+ 403 void 404 resize(Client *c, int x, int y, int w, int h, int interact) 405 { 406@@ -1272,6 +1420,14 @@ resize(Client *c, int x, int y, int w, int h, int interact) 407 resizeclient(c, x, y, w, h); 408 } 409 410+void 411+resizebarwin(Monitor *m) { 412+ unsigned int w = m->ww; 413+ if (showsystray && m == systraytomon(m)) 414+ w -= getsystraywidth(); 415+ XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, w, bh); 416+} 417+ 418 void 419 resizeclient(Client *c, int x, int y, int w, int h) 420 { 421@@ -1344,6 +1500,19 @@ resizemouse(const Arg *arg) 422 } 423 } 424 425+void 426+resizerequest(XEvent *e) 427+{ 428+ XResizeRequestEvent *ev = &e->xresizerequest; 429+ Client *i; 430+ 431+ if ((i = wintosystrayicon(ev->window))) { 432+ updatesystrayicongeom(i, ev->width, ev->height); 433+ resizebarwin(selmon); 434+ updatesystray(); 435+ } 436+} 437+ 438 void 439 restack(Monitor *m) 440 { 441@@ -1433,26 +1602,36 @@ setclientstate(Client *c, long state) 442 } 443 444 int 445-sendevent(Client *c, Atom proto) 446+sendevent(Window w, Atom proto, int mask, long d0, long d1, long d2, long d3, long d4) 447 { 448 int n; 449- Atom *protocols; 450+ Atom *protocols, mt; 451 int exists = 0; 452 XEvent ev; 453 454- if (XGetWMProtocols(dpy, c->win, &protocols, &n)) { 455- while (!exists && n--) 456- exists = protocols[n] == proto; 457- XFree(protocols); 458+ if (proto == wmatom[WMTakeFocus] || proto == wmatom[WMDelete]) { 459+ mt = wmatom[WMProtocols]; 460+ if (XGetWMProtocols(dpy, w, &protocols, &n)) { 461+ while (!exists && n--) 462+ exists = protocols[n] == proto; 463+ XFree(protocols); 464+ } 465+ } 466+ else { 467+ exists = True; 468+ mt = proto; 469 } 470 if (exists) { 471 ev.type = ClientMessage; 472- ev.xclient.window = c->win; 473- ev.xclient.message_type = wmatom[WMProtocols]; 474+ ev.xclient.window = w; 475+ ev.xclient.message_type = mt; 476 ev.xclient.format = 32; 477- ev.xclient.data.l[0] = proto; 478- ev.xclient.data.l[1] = CurrentTime; 479- XSendEvent(dpy, c->win, False, NoEventMask, &ev); 480+ ev.xclient.data.l[0] = d0; 481+ ev.xclient.data.l[1] = d1; 482+ ev.xclient.data.l[2] = d2; 483+ ev.xclient.data.l[3] = d3; 484+ ev.xclient.data.l[4] = d4; 485+ XSendEvent(dpy, w, False, mask, &ev); 486 } 487 return exists; 488 } 489@@ -1466,7 +1645,7 @@ setfocus(Client *c) 490 XA_WINDOW, 32, PropModeReplace, 491 (unsigned char *) &(c->win), 1); 492 } 493- sendevent(c, wmatom[WMTakeFocus]); 494+ sendevent(c->win, wmatom[WMTakeFocus], NoEventMask, wmatom[WMTakeFocus], CurrentTime, 0, 0, 0); 495 } 496 497 void 498@@ -1555,6 +1734,10 @@ setup(void) 499 wmatom[WMTakeFocus] = XInternAtom(dpy, "WM_TAKE_FOCUS", False); 500 netatom[NetActiveWindow] = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", False); 501 netatom[NetSupported] = XInternAtom(dpy, "_NET_SUPPORTED", False); 502+ netatom[NetSystemTray] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_S0", False); 503+ netatom[NetSystemTrayOP] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_OPCODE", False); 504+ netatom[NetSystemTrayOrientation] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_ORIENTATION", False); 505+ netatom[NetSystemTrayOrientationHorz] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_ORIENTATION_HORZ", False); 506 netatom[NetWMName] = XInternAtom(dpy, "_NET_WM_NAME", False); 507 netatom[NetWMState] = XInternAtom(dpy, "_NET_WM_STATE", False); 508 netatom[NetWMCheck] = XInternAtom(dpy, "_NET_SUPPORTING_WM_CHECK", False); 509@@ -1562,6 +1745,9 @@ setup(void) 510 netatom[NetWMWindowType] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False); 511 netatom[NetWMWindowTypeDialog] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DIALOG", False); 512 netatom[NetClientList] = XInternAtom(dpy, "_NET_CLIENT_LIST", False); 513+ xatom[Manager] = XInternAtom(dpy, "MANAGER", False); 514+ xatom[Xembed] = XInternAtom(dpy, "_XEMBED", False); 515+ xatom[XembedInfo] = XInternAtom(dpy, "_XEMBED_INFO", False); 516 /* init cursors */ 517 cursor[CurNormal] = drw_cur_create(drw, XC_left_ptr); 518 cursor[CurResize] = drw_cur_create(drw, XC_sizing); 519@@ -1570,6 +1756,8 @@ setup(void) 520 scheme = ecalloc(LENGTH(colors), sizeof(Clr *)); 521 for (i = 0; i < LENGTH(colors); i++) 522 scheme[i] = drw_scm_create(drw, colors[i], 3); 523+ /* init system tray */ 524+ updatesystray(); 525 /* init bars */ 526 updatebars(); 527 updatestatus(); 528@@ -1701,7 +1889,18 @@ togglebar(const Arg *arg) 529 { 530 selmon->showbar = !selmon->showbar; 531 updatebarpos(selmon); 532- XMoveResizeWindow(dpy, selmon->barwin, selmon->wx, selmon->by, selmon->ww, bh); 533+ resizebarwin(selmon); 534+ if (showsystray) { 535+ XWindowChanges wc; 536+ if (!selmon->showbar) 537+ wc.y = -bh; 538+ else if (selmon->showbar) { 539+ wc.y = 0; 540+ if (!selmon->topbar) 541+ wc.y = selmon->mh - bh; 542+ } 543+ XConfigureWindow(dpy, systray->win, CWY, &wc); 544+ } 545 arrange(selmon); 546 } 547 548@@ -1796,11 +1995,18 @@ unmapnotify(XEvent *e) 549 else 550 unmanage(c, 0); 551 } 552+ else if ((c = wintosystrayicon(ev->window))) { 553+ /* KLUDGE! sometimes icons occasionally unmap their windows, but do 554+ * _not_ destroy them. We map those windows back */ 555+ XMapRaised(dpy, c->win); 556+ updatesystray(); 557+ } 558 } 559 560 void 561 updatebars(void) 562 { 563+ unsigned int w; 564 Monitor *m; 565 XSetWindowAttributes wa = { 566 .override_redirect = True, 567@@ -1811,10 +2017,15 @@ updatebars(void) 568 for (m = mons; m; m = m->next) { 569 if (m->barwin) 570 continue; 571- m->barwin = XCreateWindow(dpy, root, m->wx, m->by, m->ww, bh, 0, DefaultDepth(dpy, screen), 572+ w = m->ww; 573+ if (showsystray && m == systraytomon(m)) 574+ w -= getsystraywidth(); 575+ m->barwin = XCreateWindow(dpy, root, m->wx, m->by, w, bh, 0, DefaultDepth(dpy, screen), 576 CopyFromParent, DefaultVisual(dpy, screen), 577 CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa); 578 XDefineCursor(dpy, m->barwin, cursor[CurNormal]->cursor); 579+ if (showsystray && m == systraytomon(m)) 580+ XMapRaised(dpy, systray->win); 581 XMapRaised(dpy, m->barwin); 582 XSetClassHint(dpy, m->barwin, &ch); 583 } 584@@ -1990,6 +2201,121 @@ updatestatus(void) 585 if (!gettextprop(root, XA_WM_NAME, stext, sizeof(stext))) 586 strcpy(stext, "dwm-"VERSION); 587 drawbar(selmon); 588+ updatesystray(); 589+} 590+ 591+void 592+updatesystrayicongeom(Client *i, int w, int h) 593+{ 594+ if (i) { 595+ i->h = bh; 596+ if (w == h) 597+ i->w = bh; 598+ else if (h == bh) 599+ i->w = w; 600+ else 601+ i->w = (int) ((float)bh * ((float)w / (float)h)); 602+ applysizehints(i, &(i->x), &(i->y), &(i->w), &(i->h), False); 603+ /* force icons into the systray dimensions if they don't want to */ 604+ if (i->h > bh) { 605+ if (i->w == i->h) 606+ i->w = bh; 607+ else 608+ i->w = (int) ((float)bh * ((float)i->w / (float)i->h)); 609+ i->h = bh; 610+ } 611+ } 612+} 613+ 614+void 615+updatesystrayiconstate(Client *i, XPropertyEvent *ev) 616+{ 617+ long flags; 618+ int code = 0; 619+ 620+ if (!showsystray || !i || ev->atom != xatom[XembedInfo] || 621+ !(flags = getatomprop(i, xatom[XembedInfo]))) 622+ return; 623+ 624+ if (flags & XEMBED_MAPPED && !i->tags) { 625+ i->tags = 1; 626+ code = XEMBED_WINDOW_ACTIVATE; 627+ XMapRaised(dpy, i->win); 628+ setclientstate(i, NormalState); 629+ } 630+ else if (!(flags & XEMBED_MAPPED) && i->tags) { 631+ i->tags = 0; 632+ code = XEMBED_WINDOW_DEACTIVATE; 633+ XUnmapWindow(dpy, i->win); 634+ setclientstate(i, WithdrawnState); 635+ } 636+ else 637+ return; 638+ sendevent(i->win, xatom[Xembed], StructureNotifyMask, CurrentTime, code, 0, 639+ systray->win, XEMBED_EMBEDDED_VERSION); 640+} 641+ 642+void 643+updatesystray(void) 644+{ 645+ XSetWindowAttributes wa; 646+ XWindowChanges wc; 647+ Client *i; 648+ Monitor *m = systraytomon(NULL); 649+ unsigned int x = m->mx + m->mw; 650+ unsigned int w = 1; 651+ 652+ if (!showsystray) 653+ return; 654+ if (!systray) { 655+ /* init systray */ 656+ if (!(systray = (Systray *)calloc(1, sizeof(Systray)))) 657+ die("fatal: could not malloc() %u bytes\n", sizeof(Systray)); 658+ systray->win = XCreateSimpleWindow(dpy, root, x, m->by, w, bh, 0, 0, scheme[SchemeSel][ColBg].pixel); 659+ wa.event_mask = ButtonPressMask | ExposureMask; 660+ wa.override_redirect = True; 661+ wa.background_pixel = scheme[SchemeNorm][ColBg].pixel; 662+ XSelectInput(dpy, systray->win, SubstructureNotifyMask); 663+ XChangeProperty(dpy, systray->win, netatom[NetSystemTrayOrientation], XA_CARDINAL, 32, 664+ PropModeReplace, (unsigned char *)&netatom[NetSystemTrayOrientationHorz], 1); 665+ XChangeWindowAttributes(dpy, systray->win, CWEventMask|CWOverrideRedirect|CWBackPixel, &wa); 666+ XMapRaised(dpy, systray->win); 667+ XSetSelectionOwner(dpy, netatom[NetSystemTray], systray->win, CurrentTime); 668+ if (XGetSelectionOwner(dpy, netatom[NetSystemTray]) == systray->win) { 669+ sendevent(root, xatom[Manager], StructureNotifyMask, CurrentTime, netatom[NetSystemTray], systray->win, 0, 0); 670+ XSync(dpy, False); 671+ } 672+ else { 673+ fprintf(stderr, "dwm: unable to obtain system tray.\n"); 674+ free(systray); 675+ systray = NULL; 676+ return; 677+ } 678+ } 679+ for (w = 0, i = systray->icons; i; i = i->next) { 680+ /* make sure the background color stays the same */ 681+ wa.background_pixel = scheme[SchemeNorm][ColBg].pixel; 682+ XChangeWindowAttributes(dpy, i->win, CWBackPixel, &wa); 683+ XMapRaised(dpy, i->win); 684+ w += systrayspacing; 685+ i->x = w; 686+ XMoveResizeWindow(dpy, i->win, i->x, 0, i->w, i->h); 687+ w += i->w; 688+ if (i->mon != m) 689+ i->mon = m; 690+ } 691+ w = w ? w + systrayspacing : 1; 692+ x -= w; 693+ XMoveResizeWindow(dpy, systray->win, x, m->by, w, bh); 694+ wc.x = x; wc.y = m->by; wc.width = w; wc.height = bh; 695+ wc.stack_mode = Above; wc.sibling = m->barwin; 696+ XConfigureWindow(dpy, systray->win, CWX|CWY|CWWidth|CWHeight|CWSibling|CWStackMode, &wc); 697+ XMapWindow(dpy, systray->win); 698+ XMapSubwindows(dpy, systray->win); 699+ /* redraw background */ 700+ XSetForeground(dpy, drw->gc, scheme[SchemeNorm][ColBg].pixel); 701+ XFillRectangle(dpy, systray->win, drw->gc, 0, 0, w, bh); 702+ XSync(dpy, False); 703 } 704 705 void 706@@ -2057,6 +2383,16 @@ wintoclient(Window w) 707 return NULL; 708 } 709 710+Client * 711+wintosystrayicon(Window w) { 712+ Client *i = NULL; 713+ 714+ if (!showsystray || !w) 715+ return i; 716+ for (i = systray->icons; i && i->win != w; i = i->next) ; 717+ return i; 718+} 719+ 720 Monitor * 721 wintomon(Window w) 722 { 723@@ -2110,6 +2446,22 @@ xerrorstart(Display *dpy, XErrorEvent *ee) 724 return -1; 725 } 726 727+Monitor * 728+systraytomon(Monitor *m) { 729+ Monitor *t; 730+ int i, n; 731+ if(!systraypinning) { 732+ if(!m) 733+ return selmon; 734+ return m == selmon ? m : NULL; 735+ } 736+ for(n = 1, t = mons; t && t->next; n++, t = t->next) ; 737+ for(i = 1, t = mons; t && t->next && i < systraypinning; i++, t = t->next) ; 738+ if(systraypinningfailfirst && n < systraypinning) 739+ return mons; 740+ return t; 741+} 742+ 743 void 744 zoom(const Arg *arg) 745 { 746-- 7472.17.1 748