dwm-systray-6.2.diff (23966B)
1 https://dwm.suckless.org/patches/systray/ 2 3 From 4001ccae7b1a41bdcb247b0cf095a51af7b68c28 Mon Sep 17 00:00:00 2001 4 From: Igor Gevka <igor.gevka@gmail.com> 5 Date: Sun, 16 Feb 2020 15:03:10 -0800 6 Subject: [PATCH] [PATCH] Implements a system tray for dwm. 7 8 Original author: Jan Christoph Ebersbach <jceb@e-jc.de>, inspired by http://code.google.com/p/dwm-plus 9 URL: http://dwm.suckless.org/patches/systray 10 dwm 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 16 diff --git a/config.def.h b/config.def.h 17 index 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" }; 31 diff --git a/dwm.c b/dwm.c 32 index 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 -- 747 2.17.1 748