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