dwm-systray-6.6.diff (25814B)
1 diff '--color=auto' -up dwm-6.6/config.def.h systray-dwm-6.6/config.def.h 2 --- dwm-6.6/config.def.h 2025-08-09 15:00:55.740267680 +0200 3 +++ systray-dwm-6.6/config.def.h 2025-11-18 16:26:31.581907543 +0100 4 @@ -3,8 +3,15 @@ 5 /* appearance */ 6 static const unsigned int borderpx = 1; /* border pixel of windows */ 7 static const unsigned int snap = 32; /* snap pixel */ 8 +static const unsigned int systraypinning = 0; /* 0: sloppy systray follows selected monitor, >0: pin systray to monitor X */ 9 +static const unsigned int systrayonleft = 0; /* 0: systray in the right corner, >0: systray on left of status text */ 10 +static const unsigned int systrayspacing = 2; /* systray spacing */ 11 +static const int systraypinningfailfirst = 1; /* 1: if pinning fails, display systray on the first monitor, False: display systray on the last monitor*/ 12 +static const int showsystray = 1; /* 0 means no systray */ 13 static const int showbar = 1; /* 0 means no bar */ 14 static const int topbar = 1; /* 0 means bottom bar */ 15 +static const int vertpad = 10; /* vertical padding of bar */ 16 +static const int sidepad = 10; /* horizontal padding of bar */ 17 static const char *fonts[] = { "monospace:size=10" }; 18 static const char dmenufont[] = "monospace:size=10"; 19 static const char col_gray1[] = "#222222"; 20 diff '--color=auto' -up dwm-6.6/dwm.c systray-dwm-6.6/dwm.c 21 --- dwm-6.6/dwm.c 2025-08-09 15:00:55.740267680 +0200 22 +++ systray-dwm-6.6/dwm.c 2025-11-18 16:35:53.062929489 +0100 23 @@ -56,12 +56,27 @@ 24 #define TAGMASK ((1 << LENGTH(tags)) - 1) 25 #define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad) 26 27 +#define SYSTEM_TRAY_REQUEST_DOCK 0 28 +/* XEMBED messages */ 29 +#define XEMBED_EMBEDDED_NOTIFY 0 30 +#define XEMBED_WINDOW_ACTIVATE 1 31 +#define XEMBED_FOCUS_IN 4 32 +#define XEMBED_MODALITY_ON 10 33 +#define XEMBED_MAPPED (1 << 0) 34 +#define XEMBED_WINDOW_ACTIVATE 1 35 +#define XEMBED_WINDOW_DEACTIVATE 2 36 +#define VERSION_MAJOR 0 37 +#define VERSION_MINOR 0 38 +#define XEMBED_EMBEDDED_VERSION (VERSION_MAJOR << 16) | VERSION_MINOR 39 + 40 /* enums */ 41 enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */ 42 enum { SchemeNorm, SchemeSel }; /* color schemes */ 43 enum { NetSupported, NetWMName, NetWMState, NetWMCheck, 44 + NetSystemTray, NetSystemTrayOP, NetSystemTrayOrientation, NetSystemTrayOrientationHorz, 45 NetWMFullscreen, NetActiveWindow, NetWMWindowType, 46 NetWMWindowTypeDialog, NetClientList, NetLast }; /* EWMH atoms */ 47 +enum { Manager, Xembed, XembedInfo, XLast }; /* Xembed atoms */ 48 enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */ 49 enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, 50 ClkClientWin, ClkRootWin, ClkLast }; /* clicks */ 51 @@ -140,6 +155,12 @@ typedef struct { 52 int monitor; 53 } Rule; 54 55 +typedef struct Systray Systray; 56 +struct Systray { 57 + Window win; 58 + Client *icons; 59 +}; 60 + 61 /* function declarations */ 62 static void applyrules(Client *c); 63 static int applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact); 64 @@ -171,6 +192,7 @@ static void focusstack(const Arg *arg); 65 static Atom getatomprop(Client *c, Atom prop); 66 static int getrootptr(int *x, int *y); 67 static long getstate(Window w); 68 +static unsigned int getsystraywidth(); 69 static int gettextprop(Window w, Atom atom, char *text, unsigned int size); 70 static void grabbuttons(Client *c, int focused); 71 static void grabkeys(void); 72 @@ -188,13 +210,16 @@ static void pop(Client *c); 73 static void propertynotify(XEvent *e); 74 static void quit(const Arg *arg); 75 static Monitor *recttomon(int x, int y, int w, int h); 76 +static void removesystrayicon(Client *i); 77 static void resize(Client *c, int x, int y, int w, int h, int interact); 78 +static void resizebarwin(Monitor *m); 79 static void resizeclient(Client *c, int x, int y, int w, int h); 80 static void resizemouse(const Arg *arg); 81 +static void resizerequest(XEvent *e); 82 static void restack(Monitor *m); 83 static void run(void); 84 static void scan(void); 85 -static int sendevent(Client *c, Atom proto); 86 +static int sendevent(Window w, Atom proto, int m, long d0, long d1, long d2, long d3, long d4); 87 static void sendmon(Client *c, Monitor *m); 88 static void setclientstate(Client *c, long state); 89 static void setfocus(Client *c); 90 @@ -205,6 +230,7 @@ static void setup(void); 91 static void seturgent(Client *c, int urg); 92 static void showhide(Client *c); 93 static void spawn(const Arg *arg); 94 +static Monitor *systraytomon(Monitor *m); 95 static void tag(const Arg *arg); 96 static void tagmon(const Arg *arg); 97 static void tile(Monitor *m); 98 @@ -222,24 +248,31 @@ static int updategeom(void); 99 static void updatenumlockmask(void); 100 static void updatesizehints(Client *c); 101 static void updatestatus(void); 102 +static void updatesystray(void); 103 +static void updatesystrayicongeom(Client *i, int w, int h); 104 +static void updatesystrayiconstate(Client *i, XPropertyEvent *ev); 105 static void updatetitle(Client *c); 106 static void updatewindowtype(Client *c); 107 static void updatewmhints(Client *c); 108 static void view(const Arg *arg); 109 static Client *wintoclient(Window w); 110 static Monitor *wintomon(Window w); 111 +static Client *wintosystrayicon(Window w); 112 static int xerror(Display *dpy, XErrorEvent *ee); 113 static int xerrordummy(Display *dpy, XErrorEvent *ee); 114 static int xerrorstart(Display *dpy, XErrorEvent *ee); 115 static void zoom(const Arg *arg); 116 117 /* variables */ 118 +static Systray *systray = NULL; 119 static const char broken[] = "broken"; 120 static char stext[256]; 121 static int screen; 122 static int sw, sh; /* X display screen geometry width, height */ 123 static int bh; /* bar height */ 124 static int lrpad; /* sum of left and right padding for text */ 125 +static int vp; /* vertical padding for bar */ 126 +static int sp; /* side padding for bar */ 127 static int (*xerrorxlib)(Display *, XErrorEvent *); 128 static unsigned int numlockmask = 0; 129 static void (*handler[LASTEvent]) (XEvent *) = { 130 @@ -256,9 +289,10 @@ static void (*handler[LASTEvent]) (XEven 131 [MapRequest] = maprequest, 132 [MotionNotify] = motionnotify, 133 [PropertyNotify] = propertynotify, 134 + [ResizeRequest] = resizerequest, 135 [UnmapNotify] = unmapnotify 136 }; 137 -static Atom wmatom[WMLast], netatom[NetLast]; 138 +static Atom wmatom[WMLast], netatom[NetLast], xatom[XLast]; 139 static int running = 1; 140 static Cur *cursor[CurLast]; 141 static Clr **scheme; 142 @@ -440,7 +474,7 @@ buttonpress(XEvent *e) 143 arg.ui = 1 << i; 144 } else if (ev->x < x + TEXTW(selmon->ltsymbol)) 145 click = ClkLtSymbol; 146 - else if (ev->x > selmon->ww - (int)TEXTW(stext)) 147 + else if (ev->x > selmon->ww - (int)TEXTW(stext) - getsystraywidth()) 148 click = ClkStatusText; 149 else 150 click = ClkWinTitle; 151 @@ -483,6 +517,13 @@ cleanup(void) 152 XUngrabKey(dpy, AnyKey, AnyModifier, root); 153 while (mons) 154 cleanupmon(mons); 155 + 156 + if (showsystray) { 157 + XUnmapWindow(dpy, systray->win); 158 + XDestroyWindow(dpy, systray->win); 159 + free(systray); 160 + } 161 + 162 for (i = 0; i < CurLast; i++) 163 drw_cur_free(drw, cursor[i]); 164 for (i = 0; i < LENGTH(colors); i++) 165 @@ -514,9 +555,58 @@ 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 + 221 if (!c) 222 return; 223 if (cme->message_type == netatom[NetWMState]) { 224 @@ -569,7 +659,7 @@ configurenotify(XEvent *e) 225 for (c = m->clients; c; c = c->next) 226 if (c->isfullscreen) 227 resizeclient(c, m->mx, m->my, m->mw, m->mh); 228 - XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, m->ww, bh); 229 + resizebarwin(m); 230 } 231 focus(NULL); 232 arrange(NULL); 233 @@ -654,6 +744,11 @@ destroynotify(XEvent *e) 234 235 if ((c = wintoclient(ev->window))) 236 unmanage(c, 1); 237 + else if ((c = wintosystrayicon(ev->window))) { 238 + removesystrayicon(c); 239 + resizebarwin(selmon); 240 + updatesystray(); 241 + } 242 } 243 244 void 245 @@ -697,7 +792,7 @@ dirtomon(int dir) 246 void 247 drawbar(Monitor *m) 248 { 249 - int x, w, tw = 0; 250 + int x, w, tw = 0, stw = 0; 251 int boxs = drw->fonts->h / 9; 252 int boxw = drw->fonts->h / 6 + 2; 253 unsigned int i, occ = 0, urg = 0; 254 @@ -706,13 +801,17 @@ drawbar(Monitor *m) 255 if (!m->showbar) 256 return; 257 258 + if(showsystray && m == systraytomon(m) && !systrayonleft) 259 + stw = getsystraywidth(); 260 + 261 /* draw status first so it can be overdrawn by tags later */ 262 if (m == selmon) { /* status is only drawn on selected monitor */ 263 drw_setscheme(drw, scheme[SchemeNorm]); 264 - tw = TEXTW(stext) - lrpad + 2; /* 2px right padding */ 265 - drw_text(drw, m->ww - tw, 0, tw, bh, 0, stext, 0); 266 + tw = TEXTW(stext) - lrpad / 2 + 2; /* 2px right padding */ 267 + drw_text(drw, m->ww - tw - stw - 2 * sp, 0, tw, bh, lrpad / 2 - 2, stext, 0); 268 } 269 270 + resizebarwin(m); 271 for (c = m->clients; c; c = c->next) { 272 occ |= c->tags; 273 if (c->isurgent) 274 @@ -733,18 +832,18 @@ drawbar(Monitor *m) 275 drw_setscheme(drw, scheme[SchemeNorm]); 276 x = drw_text(drw, x, 0, w, bh, lrpad / 2, m->ltsymbol, 0); 277 278 - if ((w = m->ww - tw - x) > bh) { 279 + if ((w = m->ww - tw - stw - x) > bh) { 280 if (m->sel) { 281 drw_setscheme(drw, scheme[m == selmon ? SchemeSel : SchemeNorm]); 282 - drw_text(drw, x, 0, w, bh, lrpad / 2, m->sel->name, 0); 283 + drw_text(drw, x, 0, w - 2 * sp, bh, lrpad / 2, m->sel->name, 0); 284 if (m->sel->isfloating) 285 drw_rect(drw, x + boxs, boxs, boxw, boxw, m->sel->isfixed, 0); 286 } else { 287 drw_setscheme(drw, scheme[SchemeNorm]); 288 - drw_rect(drw, x, 0, w, bh, 1, 1); 289 + drw_rect(drw, x, 0, w - 2 * sp, 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 @@ -781,8 +880,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 @@ -868,14 +970,32 @@ getatomprop(Client *c, Atom prop) 311 unsigned char *p = NULL; 312 Atom da, atom = None; 313 314 - if (XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, XA_ATOM, 315 + /* FIXME getatomprop should return the number of items and a pointer to 316 + * the stored data instead of this workaround */ 317 + Atom req = XA_ATOM; 318 + if (prop == xatom[XembedInfo]) 319 + req = xatom[XembedInfo]; 320 + 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 } 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 getrootptr(int *x, int *y) 343 { 344 @@ -1016,7 +1136,8 @@ killclient(const Arg *arg) 345 { 346 if (!selmon->sel) 347 return; 348 - if (!sendevent(selmon->sel, wmatom[WMDelete])) { 349 + 350 + if (!sendevent(selmon->sel->win, wmatom[WMDelete], NoEventMask, wmatom[WMDelete], CurrentTime, 0 , 0, 0)) { 351 XGrabServer(dpy); 352 XSetErrorHandler(xerrordummy); 353 XSetCloseDownMode(dpy, DestroyAll); 354 @@ -1103,6 +1224,13 @@ maprequest(XEvent *e) 355 static XWindowAttributes wa; 356 XMapRequestEvent *ev = &e->xmaprequest; 357 358 + Client *i; 359 + if ((i = wintosystrayicon(ev->window))) { 360 + sendevent(i->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_WINDOW_ACTIVATE, 0, systray->win, XEMBED_EMBEDDED_VERSION); 361 + resizebarwin(selmon); 362 + updatesystray(); 363 + } 364 + 365 if (!XGetWindowAttributes(dpy, ev->window, &wa) || wa.override_redirect) 366 return; 367 if (!wintoclient(ev->window)) 368 @@ -1224,6 +1352,17 @@ 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 + 383 if ((ev->window == root) && (ev->atom == XA_WM_NAME)) 384 updatestatus(); 385 else if (ev->state == PropertyDelete) 386 @@ -1275,6 +1414,19 @@ recttomon(int x, int y, int w, int h) 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 +void 403 resize(Client *c, int x, int y, int w, int h, int interact) 404 { 405 if (applysizehints(c, &x, &y, &w, &h, interact)) 406 @@ -1282,6 +1434,14 @@ resize(Client *c, int x, int y, int w, i 407 } 408 409 void 410 +resizebarwin(Monitor *m) { 411 + unsigned int w = m->ww; 412 + if (showsystray && m == systraytomon(m) && !systrayonleft) 413 + w -= getsystraywidth(); 414 + XMoveResizeWindow(dpy, m->barwin, m->wx + sp, m->by + vp, w - 2 * sp, bh); 415 +} 416 + 417 +void 418 resizeclient(Client *c, int x, int y, int w, int h) 419 { 420 XWindowChanges wc; 421 @@ -1297,6 +1457,19 @@ resizeclient(Client *c, int x, int y, in 422 } 423 424 void 425 +resizerequest(XEvent *e) 426 +{ 427 + XResizeRequestEvent *ev = &e->xresizerequest; 428 + Client *i; 429 + 430 + if ((i = wintosystrayicon(ev->window))) { 431 + updatesystrayicongeom(i, ev->width, ev->height); 432 + resizebarwin(selmon); 433 + updatesystray(); 434 + } 435 +} 436 + 437 +void 438 resizemouse(const Arg *arg) 439 { 440 int ocx, ocy, nw, nh; 441 @@ -1442,26 +1615,37 @@ 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 + 471 if (exists) { 472 ev.type = ClientMessage; 473 - ev.xclient.window = c->win; 474 - ev.xclient.message_type = wmatom[WMProtocols]; 475 + ev.xclient.window = w; 476 + ev.xclient.message_type = mt; 477 ev.xclient.format = 32; 478 - ev.xclient.data.l[0] = proto; 479 - ev.xclient.data.l[1] = CurrentTime; 480 - XSendEvent(dpy, c->win, False, NoEventMask, &ev); 481 + ev.xclient.data.l[0] = d0; 482 + ev.xclient.data.l[1] = d1; 483 + ev.xclient.data.l[2] = d2; 484 + ev.xclient.data.l[3] = d3; 485 + ev.xclient.data.l[4] = d4; 486 + XSendEvent(dpy, w, False, mask, &ev); 487 } 488 return exists; 489 } 490 @@ -1475,7 +1659,7 @@ setfocus(Client *c) 491 XA_WINDOW, 32, PropModeReplace, 492 (unsigned char *) &(c->win), 1); 493 } 494 - sendevent(c, wmatom[WMTakeFocus]); 495 + sendevent(c->win, wmatom[WMTakeFocus], NoEventMask, wmatom[WMTakeFocus], CurrentTime, 0, 0, 0); 496 } 497 498 void 499 @@ -1563,6 +1747,9 @@ setup(void) 500 lrpad = drw->fonts->h; 501 bh = drw->fonts->h + 2; 502 updategeom(); 503 + sp = sidepad; 504 + vp = (topbar == 1) ? vertpad : - vertpad; 505 + 506 /* init atoms */ 507 utf8string = XInternAtom(dpy, "UTF8_STRING", False); 508 wmatom[WMProtocols] = XInternAtom(dpy, "WM_PROTOCOLS", False); 509 @@ -1571,6 +1758,10 @@ setup(void) 510 wmatom[WMTakeFocus] = XInternAtom(dpy, "WM_TAKE_FOCUS", False); 511 netatom[NetActiveWindow] = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", False); 512 netatom[NetSupported] = XInternAtom(dpy, "_NET_SUPPORTED", False); 513 + netatom[NetSystemTray] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_S0", False); 514 + netatom[NetSystemTrayOP] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_OPCODE", False); 515 + netatom[NetSystemTrayOrientation] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_ORIENTATION", False); 516 + netatom[NetSystemTrayOrientationHorz] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_ORIENTATION_HORZ", False); 517 netatom[NetWMName] = XInternAtom(dpy, "_NET_WM_NAME", False); 518 netatom[NetWMState] = XInternAtom(dpy, "_NET_WM_STATE", False); 519 netatom[NetWMCheck] = XInternAtom(dpy, "_NET_SUPPORTING_WM_CHECK", False); 520 @@ -1578,6 +1769,9 @@ setup(void) 521 netatom[NetWMWindowType] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False); 522 netatom[NetWMWindowTypeDialog] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DIALOG", False); 523 netatom[NetClientList] = XInternAtom(dpy, "_NET_CLIENT_LIST", False); 524 + xatom[Manager] = XInternAtom(dpy, "MANAGER", False); 525 + xatom[Xembed] = XInternAtom(dpy, "_XEMBED", False); 526 + xatom[XembedInfo] = XInternAtom(dpy, "_XEMBED_INFO", False); 527 /* init cursors */ 528 cursor[CurNormal] = drw_cur_create(drw, XC_left_ptr); 529 cursor[CurResize] = drw_cur_create(drw, XC_sizing); 530 @@ -1586,9 +1780,12 @@ setup(void) 531 scheme = ecalloc(LENGTH(colors), sizeof(Clr *)); 532 for (i = 0; i < LENGTH(colors); i++) 533 scheme[i] = drw_scm_create(drw, colors[i], 3); 534 + /* init system tray */ 535 + updatesystray(); 536 /* init bars */ 537 updatebars(); 538 updatestatus(); 539 + updatebarpos(selmon); 540 /* supporting window for NetWMCheck */ 541 wmcheckwin = XCreateSimpleWindow(dpy, root, 0, 0, 1, 1, 0, 0, 0); 542 XChangeProperty(dpy, wmcheckwin, netatom[NetWMCheck], XA_WINDOW, 32, 543 @@ -1716,7 +1913,18 @@ togglebar(const Arg *arg) 544 { 545 selmon->showbar = !selmon->showbar; 546 updatebarpos(selmon); 547 - XMoveResizeWindow(dpy, selmon->barwin, selmon->wx, selmon->by, selmon->ww, bh); 548 + resizebarwin(selmon); 549 + if (showsystray) { 550 + XWindowChanges wc; 551 + if (!selmon->showbar) 552 + wc.y = -bh; 553 + else if (selmon->showbar) { 554 + wc.y = 0; 555 + if (!selmon->topbar) 556 + wc.y = selmon->mh - bh; 557 + } 558 + XConfigureWindow(dpy, systray->win, CWY, &wc); 559 + } 560 arrange(selmon); 561 } 562 563 @@ -1812,11 +2020,18 @@ unmapnotify(XEvent *e) 564 else 565 unmanage(c, 0); 566 } 567 + else if ((c = wintosystrayicon(ev->window))) { 568 + /* KLUDGE! sometimes icons occasionally unmap their windows, but do 569 + * _not_ destroy them. We map those windows back */ 570 + XMapRaised(dpy, c->win); 571 + updatesystray(); 572 + } 573 } 574 575 void 576 updatebars(void) 577 { 578 + unsigned int w; 579 Monitor *m; 580 XSetWindowAttributes wa = { 581 .override_redirect = True, 582 @@ -1827,10 +2042,15 @@ updatebars(void) 583 for (m = mons; m; m = m->next) { 584 if (m->barwin) 585 continue; 586 - m->barwin = XCreateWindow(dpy, root, m->wx, m->by, m->ww, bh, 0, DefaultDepth(dpy, screen), 587 + w = m->ww; 588 + if (showsystray && m == systraytomon(m)) 589 + w -= getsystraywidth(); 590 + m->barwin = XCreateWindow(dpy, root, m->wx + sp, m->by + vp, w - 2 * sp, bh, 0, DefaultDepth(dpy, screen), 591 CopyFromParent, DefaultVisual(dpy, screen), 592 CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa); 593 XDefineCursor(dpy, m->barwin, cursor[CurNormal]->cursor); 594 + if (showsystray && m == systraytomon(m)) 595 + XMapRaised(dpy, systray->win); 596 XMapRaised(dpy, m->barwin); 597 XSetClassHint(dpy, m->barwin, &ch); 598 } 599 @@ -1842,11 +2062,11 @@ updatebarpos(Monitor *m) 600 m->wy = m->my; 601 m->wh = m->mh; 602 if (m->showbar) { 603 - m->wh -= bh; 604 - m->by = m->topbar ? m->wy : m->wy + m->wh; 605 - m->wy = m->topbar ? m->wy + bh : m->wy; 606 + m->wh = m->wh - vertpad - bh; 607 + m->by = m->topbar ? m->wy : m->wy + m->wh + vertpad; 608 + m->wy = m->topbar ? m->wy + bh + vp : m->wy; 609 } else 610 - m->by = -bh; 611 + m->by = -bh - vp; 612 } 613 614 void 615 @@ -2007,6 +2227,126 @@ updatestatus(void) 616 if (!gettextprop(root, XA_WM_NAME, stext, sizeof(stext))) 617 strcpy(stext, "dwm-"VERSION); 618 drawbar(selmon); 619 + updatesystray(); 620 +} 621 + 622 + 623 +void 624 +updatesystrayicongeom(Client *i, int w, int h) 625 +{ 626 + if (i) { 627 + i->h = bh; 628 + if (w == h) 629 + i->w = bh; 630 + else if (h == bh) 631 + i->w = w; 632 + else 633 + i->w = (int) ((float)bh * ((float)w / (float)h)); 634 + applysizehints(i, &(i->x), &(i->y), &(i->w), &(i->h), False); 635 + /* force icons into the systray dimensions if they don't want to */ 636 + if (i->h > bh) { 637 + if (i->w == i->h) 638 + i->w = bh; 639 + else 640 + i->w = (int) ((float)bh * ((float)i->w / (float)i->h)); 641 + i->h = bh; 642 + } 643 + } 644 +} 645 + 646 +void 647 +updatesystrayiconstate(Client *i, XPropertyEvent *ev) 648 +{ 649 + long flags; 650 + int code = 0; 651 + 652 + if (!showsystray || !i || ev->atom != xatom[XembedInfo] || 653 + !(flags = getatomprop(i, xatom[XembedInfo]))) 654 + return; 655 + 656 + if (flags & XEMBED_MAPPED && !i->tags) { 657 + i->tags = 1; 658 + code = XEMBED_WINDOW_ACTIVATE; 659 + XMapRaised(dpy, i->win); 660 + setclientstate(i, NormalState); 661 + } 662 + else if (!(flags & XEMBED_MAPPED) && i->tags) { 663 + i->tags = 0; 664 + code = XEMBED_WINDOW_DEACTIVATE; 665 + XUnmapWindow(dpy, i->win); 666 + setclientstate(i, WithdrawnState); 667 + } 668 + else 669 + return; 670 + sendevent(i->win, xatom[Xembed], StructureNotifyMask, CurrentTime, code, 0, 671 + systray->win, XEMBED_EMBEDDED_VERSION); 672 +} 673 + 674 +void 675 +updatesystray(void) 676 +{ 677 + XSetWindowAttributes wa; 678 + XWindowChanges wc; 679 + Client *i; 680 + Monitor *m = systraytomon(NULL); 681 + unsigned int x = m->mx + m->mw - vp; 682 + unsigned int y = m->by + sp; 683 + unsigned int sw = TEXTW(stext) - lrpad + systrayspacing; 684 + unsigned int w = 1; 685 + 686 + if (!showsystray) 687 + return; 688 + if (systrayonleft) 689 + x -= sw + lrpad / 2; 690 + if (!systray) { 691 + /* init systray */ 692 + if (!(systray = (Systray *)calloc(1, sizeof(Systray)))) 693 + die("fatal: could not malloc() %u bytes\n", sizeof(Systray)); 694 + systray->win = XCreateSimpleWindow(dpy, root, x, y, w, bh, 0, 0, scheme[SchemeSel][ColBg].pixel); 695 + wa.event_mask = ButtonPressMask | ExposureMask; 696 + wa.override_redirect = True; 697 + wa.background_pixel = scheme[SchemeNorm][ColBg].pixel; 698 + XSelectInput(dpy, systray->win, SubstructureNotifyMask); 699 + XChangeProperty(dpy, systray->win, netatom[NetSystemTrayOrientation], XA_CARDINAL, 32, 700 + PropModeReplace, (unsigned char *)&netatom[NetSystemTrayOrientationHorz], 1); 701 + XChangeWindowAttributes(dpy, systray->win, CWEventMask|CWOverrideRedirect|CWBackPixel, &wa); 702 + XMapRaised(dpy, systray->win); 703 + XSetSelectionOwner(dpy, netatom[NetSystemTray], systray->win, CurrentTime); 704 + if (XGetSelectionOwner(dpy, netatom[NetSystemTray]) == systray->win) { 705 + sendevent(root, xatom[Manager], StructureNotifyMask, CurrentTime, netatom[NetSystemTray], systray->win, 0, 0); 706 + XSync(dpy, False); 707 + } 708 + else { 709 + fprintf(stderr, "dwm: unable to obtain system tray.\n"); 710 + free(systray); 711 + systray = NULL; 712 + return; 713 + } 714 + } 715 + for (w = 0, i = systray->icons; i; i = i->next) { 716 + /* make sure the background color stays the same */ 717 + wa.background_pixel = scheme[SchemeNorm][ColBg].pixel; 718 + XChangeWindowAttributes(dpy, i->win, CWBackPixel, &wa); 719 + XMapRaised(dpy, i->win); 720 + w += systrayspacing; 721 + i->x = w; 722 + XMoveResizeWindow(dpy, i->win, i->x, y - sp, i->w, i->h); 723 + w += i->w; 724 + if (i->mon != m) 725 + i->mon = m; 726 + } 727 + w = w ? w + systrayspacing : 1; 728 + x -= w; 729 + XMoveResizeWindow(dpy, systray->win, x, y, w, bh); 730 + wc.x = x; wc.y = y; wc.width = w; wc.height = bh; 731 + wc.stack_mode = Above; wc.sibling = m->barwin; 732 + XConfigureWindow(dpy, systray->win, CWX|CWY|CWWidth|CWHeight|CWSibling|CWStackMode, &wc); 733 + XMapWindow(dpy, systray->win); 734 + XMapSubwindows(dpy, systray->win); 735 + /* redraw background */ 736 + XSetForeground(dpy, drw->gc, scheme[SchemeNorm][ColBg].pixel); 737 + XFillRectangle(dpy, systray->win, drw->gc, 0, 0, w, bh); 738 + XSync(dpy, False); 739 } 740 741 void 742 @@ -2074,6 +2414,16 @@ wintoclient(Window w) 743 return NULL; 744 } 745 746 +Client * 747 +wintosystrayicon(Window w) { 748 + Client *i = NULL; 749 + 750 + if (!showsystray || !w) 751 + return i; 752 + for (i = systray->icons; i && i->win != w; i = i->next) ; 753 + return i; 754 +} 755 + 756 Monitor * 757 wintomon(Window w) 758 { 759 @@ -2127,6 +2477,22 @@ xerrorstart(Display *dpy, XErrorEvent *e 760 return -1; 761 } 762 763 +Monitor * 764 +systraytomon(Monitor *m) { 765 + Monitor *t; 766 + int i, n; 767 + if(!systraypinning) { 768 + if(!m) 769 + return selmon; 770 + return m == selmon ? m : NULL; 771 + } 772 + for(n = 1, t = mons; t && t->next; n++, t = t->next) ; 773 + for(i = 1, t = mons; t && t->next && i < systraypinning; i++, t = t->next) ; 774 + if(systraypinningfailfirst && n < systraypinning) 775 + return mons; 776 + return t; 777 +} 778 + 779 void 780 zoom(const Arg *arg) 781 { 782 @@ -2162,3 +2528,4 @@ main(int argc, char *argv[]) 783 XCloseDisplay(dpy); 784 return EXIT_SUCCESS; 785 } 786 +