dwm-systray-6.6.diff - sites - public wiki contents of suckless.org
(HTM) git clone git://git.suckless.org/sites
(DIR) Log
(DIR) Files
(DIR) Refs
---
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 +