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