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