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