dwm-git-20130119-systray.diff - sites - public wiki contents of suckless.org
(HTM) git clone git://git.suckless.org/sites
(DIR) Log
(DIR) Files
(DIR) Refs
---
dwm-git-20130119-systray.diff (20248B)
---
1 Author: Jan Christoph Ebersbach <jceb@e-jc.de>, inspired by http://code.google.com/p/dwm-plus
2 URL: http://dwm.suckless.org/patches/systray
3 Implements a system tray for dwm.
4
5 diff -r c794a9f5ae5e config.def.h
6 --- a/config.def.h Sun Jul 08 09:45:53 2012 +0200
7 +++ b/config.def.h Sat Aug 18 13:28:31 2012 +0200
8 @@ -10,6 +10,8 @@
9 static const char selfgcolor[] = "#eeeeee";
10 static const unsigned int borderpx = 1; /* border pixel of windows */
11 static const unsigned int snap = 32; /* snap pixel */
12 +static const unsigned int systrayspacing = 2; /* systray spacing */
13 +static const Bool showsystray = True; /* False means no systray */
14 static const Bool showbar = True; /* False means no bar */
15 static const Bool topbar = True; /* False means bottom bar */
16
17 diff -r c794a9f5ae5e dwm.c
18 --- a/dwm.c Sun Jul 08 09:45:53 2012 +0200
19 +++ b/dwm.c Sat Aug 18 13:28:31 2012 +0200
20 @@ -56,12 +56,30 @@
21 #define TAGMASK ((1 << LENGTH(tags)) - 1)
22 #define TEXTW(X) (textnw(X, strlen(X)) + dc.font.height)
23
24 +#define SYSTEM_TRAY_REQUEST_DOCK 0
25 +#define _NET_SYSTEM_TRAY_ORIENTATION_HORZ 0
26 +
27 +/* XEMBED messages */
28 +#define XEMBED_EMBEDDED_NOTIFY 0
29 +#define XEMBED_WINDOW_ACTIVATE 1
30 +#define XEMBED_FOCUS_IN 4
31 +#define XEMBED_MODALITY_ON 10
32 +
33 +#define XEMBED_MAPPED (1 << 0)
34 +#define XEMBED_WINDOW_ACTIVATE 1
35 +#define XEMBED_WINDOW_DEACTIVATE 2
36 +
37 +#define VERSION_MAJOR 0
38 +#define VERSION_MINOR 0
39 +#define XEMBED_EMBEDDED_VERSION (VERSION_MAJOR << 16) | VERSION_MINOR
40 +
41 /* enums */
42 enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */
43 enum { ColBorder, ColFG, ColBG, ColLast }; /* color */
44 -enum { NetSupported, NetWMName, NetWMState,
45 - NetWMFullscreen, NetActiveWindow, NetWMWindowType,
46 - NetWMWindowTypeDialog, NetClientList, NetLast }; /* EWMH atoms */
47 +enum { NetSupported, NetSystemTray, NetSystemTrayOP, NetSystemTrayOrientation,
48 + NetWMName, NetWMState, NetWMFullscreen, NetActiveWindow, NetWMWindowType,
49 + NetWMWindowTypeDialog, NetClientList, NetLast }; /* EWMH atoms */
50 +enum { Manager, Xembed, XembedInfo, XLast }; /* Xembed atoms */
51 enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */
52 enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle,
53 ClkClientWin, ClkRootWin, ClkLast }; /* clicks */
54 @@ -155,6 +173,12 @@
55 int monitor;
56 } Rule;
57
58 +typedef struct Systray Systray;
59 +struct Systray {
60 + Window win;
61 + Client *icons;
62 +};
63 +
64 /* function declarations */
65 static void applyrules(Client *c);
66 static Bool applysizehints(Client *c, int *x, int *y, int *w, int *h, Bool interact);
67 @@ -187,9 +211,11 @@
68 static void focusin(XEvent *e);
69 static void focusmon(const Arg *arg);
70 static void focusstack(const Arg *arg);
71 +static Atom getatomprop(Client *c, Atom prop);
72 static unsigned long getcolor(const char *colstr);
73 static Bool getrootptr(int *x, int *y);
74 static long getstate(Window w);
75 +static unsigned int getsystraywidth();
76 static Bool gettextprop(Window w, Atom atom, char *text, unsigned int size);
77 static void grabbuttons(Client *c, Bool focused);
78 static void grabkeys(void);
79 @@ -208,13 +234,16 @@
80 static void propertynotify(XEvent *e);
81 static void quit(const Arg *arg);
82 static Monitor *recttomon(int x, int y, int w, int h);
83 +static void removesystrayicon(Client *i);
84 static void resize(Client *c, int x, int y, int w, int h, Bool interact);
85 +static void resizebarwin(Monitor *m);
86 static void resizeclient(Client *c, int x, int y, int w, int h);
87 static void resizemouse(const Arg *arg);
88 +static void resizerequest(XEvent *e);
89 static void restack(Monitor *m);
90 static void run(void);
91 static void scan(void);
92 -static Bool sendevent(Client *c, Atom proto);
93 +static Bool sendevent(Window w, Atom proto, int m, long d0, long d1, long d2, long d3, long d4);
94 static void sendmon(Client *c, Monitor *m);
95 static void setclientstate(Client *c, long state);
96 static void setfocus(Client *c);
97 @@ -243,18 +272,24 @@
98 static void updatenumlockmask(void);
99 static void updatesizehints(Client *c);
100 static void updatestatus(void);
101 +static void updatesystray(void);
102 +static void updatesystrayicongeom(Client *i, int w, int h);
103 +static void updatesystrayiconstate(Client *i, XPropertyEvent *ev);
104 static void updatewindowtype(Client *c);
105 static void updatetitle(Client *c);
106 static void updatewmhints(Client *c);
107 static void view(const Arg *arg);
108 static Client *wintoclient(Window w);
109 static Monitor *wintomon(Window w);
110 +static Client *wintosystrayicon(Window w);
111 static int xerror(Display *dpy, XErrorEvent *ee);
112 static int xerrordummy(Display *dpy, XErrorEvent *ee);
113 static int xerrorstart(Display *dpy, XErrorEvent *ee);
114 static void zoom(const Arg *arg);
115
116 /* variables */
117 +static Systray *systray = NULL;
118 +static unsigned long systrayorientation = _NET_SYSTEM_TRAY_ORIENTATION_HORZ;
119 static const char broken[] = "broken";
120 static char stext[256];
121 static int screen;
122 @@ -276,9 +311,10 @@
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 Bool running = True;
132 static Cursor cursor[CurLast];
133 static Display *dpy;
134 @@ -499,6 +535,11 @@
135 XFreeCursor(dpy, cursor[CurMove]);
136 while(mons)
137 cleanupmon(mons);
138 + if(showsystray) {
139 + XUnmapWindow(dpy, systray->win);
140 + XDestroyWindow(dpy, systray->win);
141 + free(systray);
142 + }
143 XSync(dpy, False);
144 XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime);
145 XDeleteProperty(dpy, root, netatom[NetActiveWindow]);
146 @@ -533,9 +575,49 @@
147
148 void
149 clientmessage(XEvent *e) {
150 + XWindowAttributes wa;
151 + XSetWindowAttributes swa;
152 XClientMessageEvent *cme = &e->xclient;
153 Client *c = wintoclient(cme->window);
154
155 + if(showsystray && cme->window == systray->win && cme->message_type == netatom[NetSystemTrayOP]) {
156 + /* add systray icons */
157 + if(cme->data.l[1] == SYSTEM_TRAY_REQUEST_DOCK) {
158 + if(!(c = (Client *)calloc(1, sizeof(Client))))
159 + die("fatal: could not malloc() %u bytes\n", sizeof(Client));
160 + c->win = cme->data.l[2];
161 + c->mon = selmon;
162 + c->next = systray->icons;
163 + systray->icons = c;
164 + XGetWindowAttributes(dpy, c->win, &wa);
165 + c->x = c->oldx = c->y = c->oldy = 0;
166 + c->w = c->oldw = wa.width;
167 + c->h = c->oldh = wa.height;
168 + c->oldbw = wa.border_width;
169 + c->bw = 0;
170 + c->isfloating = True;
171 + /* reuse tags field as mapped status */
172 + c->tags = 1;
173 + updatesizehints(c);
174 + updatesystrayicongeom(c, wa.width, wa.height);
175 + XAddToSaveSet(dpy, c->win);
176 + XSelectInput(dpy, c->win, StructureNotifyMask | PropertyChangeMask | ResizeRedirectMask);
177 + XReparentWindow(dpy, c->win, systray->win, 0, 0);
178 + /* use parents background pixmap */
179 + swa.background_pixmap = ParentRelative;
180 + swa.background_pixel = dc.norm[ColBG];
181 + XChangeWindowAttributes(dpy, c->win, CWBackPixmap|CWBackPixel, &swa);
182 + sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_EMBEDDED_NOTIFY, 0 , systray->win, XEMBED_EMBEDDED_VERSION);
183 + /* FIXME not sure if I have to send these events, too */
184 + sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_FOCUS_IN, 0 , systray->win, XEMBED_EMBEDDED_VERSION);
185 + sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_WINDOW_ACTIVATE, 0 , systray->win, XEMBED_EMBEDDED_VERSION);
186 + sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_MODALITY_ON, 0 , systray->win, XEMBED_EMBEDDED_VERSION);
187 + resizebarwin(selmon);
188 + updatesystray();
189 + setclientstate(c, NormalState);
190 + }
191 + return;
192 + }
193 if(!c)
194 return;
195 if(cme->message_type == netatom[NetWMState]) {
196 @@ -587,7 +667,7 @@
197 dc.drawable = XCreatePixmap(dpy, root, sw, bh, DefaultDepth(dpy, screen));
198 updatebars();
199 for(m = mons; m; m = m->next)
200 - XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, m->ww, bh);
201 + resizebarwin(m);
202 focus(NULL);
203 arrange(NULL);
204 }
205 @@ -671,6 +751,11 @@
206
207 if((c = wintoclient(ev->window)))
208 unmanage(c, True);
209 + else if((c = wintosystrayicon(ev->window))) {
210 + removesystrayicon(c);
211 + resizebarwin(selmon);
212 + updatesystray();
213 + }
214 }
215
216 void
217 @@ -726,6 +811,7 @@
218 unsigned long *col;
219 Client *c;
220
221 + resizebarwin(m);
222 for(c = m->clients; c; c = c->next) {
223 occ |= c->tags;
224 if(c->isurgent)
225 @@ -747,6 +833,9 @@
226 if(m == selmon) { /* status is only drawn on selected monitor */
227 dc.w = TEXTW(stext);
228 dc.x = m->ww - dc.w;
229 + if(showsystray && m == selmon) {
230 + dc.x -= getsystraywidth();
231 + }
232 if(dc.x < x) {
233 dc.x = x;
234 dc.w = m->ww - x;
235 @@ -775,6 +864,7 @@
236
237 for(m = mons; m; m = m->next)
238 drawbar(m);
239 + updatesystray();
240 }
241
242 void
243 @@ -924,10 +1014,17 @@
244 unsigned long dl;
245 unsigned char *p = NULL;
246 Atom da, atom = None;
247 + /* FIXME getatomprop should return the number of items and a pointer to
248 + * the stored data instead of this workaround */
249 + Atom req = XA_ATOM;
250 + if(prop == xatom[XembedInfo])
251 + req = xatom[XembedInfo];
252
253 - if(XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, XA_ATOM,
254 + if(XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, req,
255 &da, &di, &dl, &dl, &p) == Success && p) {
256 atom = *(Atom *)p;
257 + if(da == xatom[XembedInfo] && dl == 2)
258 + atom = ((Atom *)p)[1];
259 XFree(p);
260 }
261 return atom;
262 @@ -969,6 +1066,15 @@
263 return result;
264 }
265
266 +unsigned int
267 +getsystraywidth() {
268 + unsigned int w = 0;
269 + Client *i;
270 + if(showsystray)
271 + for(i = systray->icons; i; w += i->w + systrayspacing, i = i->next) ;
272 + return w ? w + systrayspacing : 1;
273 +}
274 +
275 Bool
276 gettextprop(Window w, Atom atom, char *text, unsigned int size) {
277 char **list = NULL;
278 @@ -1103,7 +1209,7 @@
279 killclient(const Arg *arg) {
280 if(!selmon->sel)
281 return;
282 - if(!sendevent(selmon->sel, wmatom[WMDelete])) {
283 + if(!sendevent(selmon->sel->win, wmatom[WMDelete], NoEventMask, wmatom[WMDelete], CurrentTime, 0 , 0, 0)) {
284 XGrabServer(dpy);
285 XSetErrorHandler(xerrordummy);
286 XSetCloseDownMode(dpy, DestroyAll);
287 @@ -1189,6 +1295,12 @@
288 maprequest(XEvent *e) {
289 static XWindowAttributes wa;
290 XMapRequestEvent *ev = &e->xmaprequest;
291 + Client *i;
292 + if((i = wintosystrayicon(ev->window))) {
293 + sendevent(i->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_WINDOW_ACTIVATE, 0, systray->win, XEMBED_EMBEDDED_VERSION);
294 + resizebarwin(selmon);
295 + updatesystray();
296 + }
297
298 if(!XGetWindowAttributes(dpy, ev->window, &wa))
299 return;
300 @@ -1305,6 +1417,16 @@
301 Window trans;
302 XPropertyEvent *ev = &e->xproperty;
303
304 + if((c = wintosystrayicon(ev->window))) {
305 + if(ev->atom == XA_WM_NORMAL_HINTS) {
306 + updatesizehints(c);
307 + updatesystrayicongeom(c, c->w, c->h);
308 + }
309 + else
310 + updatesystrayiconstate(c, ev);
311 + resizebarwin(selmon);
312 + updatesystray();
313 + }
314 if((ev->window == root) && (ev->atom == XA_WM_NAME))
315 updatestatus();
316 else if(ev->state == PropertyDelete)
317 @@ -1354,12 +1476,33 @@
318 }
319
320 void
321 +removesystrayicon(Client *i) {
322 + Client **ii;
323 +
324 + if(!showsystray || !i)
325 + return;
326 + for(ii = &systray->icons; *ii && *ii != i; ii = &(*ii)->next);
327 + if(ii)
328 + *ii = i->next;
329 + free(i);
330 +}
331 +
332 +
333 +void
334 resize(Client *c, int x, int y, int w, int h, Bool interact) {
335 if(applysizehints(c, &x, &y, &w, &h, interact))
336 resizeclient(c, x, y, w, h);
337 }
338
339 void
340 +resizebarwin(Monitor *m) {
341 + unsigned int w = m->ww;
342 + if(showsystray && m == selmon)
343 + w -= getsystraywidth();
344 + XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, w, bh);
345 +}
346 +
347 +void
348 resizeclient(Client *c, int x, int y, int w, int h) {
349 XWindowChanges wc;
350
351 @@ -1426,6 +1569,18 @@
352 }
353
354 void
355 +resizerequest(XEvent *e) {
356 + XResizeRequestEvent *ev = &e->xresizerequest;
357 + Client *i;
358 +
359 + if((i = wintosystrayicon(ev->window))) {
360 + updatesystrayicongeom(i, ev->width, ev->height);
361 + resizebarwin(selmon);
362 + updatesystray();
363 + }
364 +}
365 +
366 +void
367 restack(Monitor *m) {
368 Client *c;
369 XEvent ev;
370 @@ -1509,25 +1664,35 @@
371 }
372
373 Bool
374 -sendevent(Client *c, Atom proto) {
375 +sendevent(Window w, Atom proto, int mask, long d0, long d1, long d2, long d3, long d4) {
376 int n;
377 - Atom *protocols;
378 + Atom *protocols, mt;
379 Bool exists = False;
380 XEvent ev;
381
382 - if(XGetWMProtocols(dpy, c->win, &protocols, &n)) {
383 - while(!exists && n--)
384 - exists = protocols[n] == proto;
385 - XFree(protocols);
386 + if(proto == wmatom[WMTakeFocus] || proto == wmatom[WMDelete]) {
387 + mt = wmatom[WMProtocols];
388 + if(XGetWMProtocols(dpy, w, &protocols, &n)) {
389 + while(!exists && n--)
390 + exists = protocols[n] == proto;
391 + XFree(protocols);
392 + }
393 + }
394 + else {
395 + exists = True;
396 + mt = proto;
397 }
398 if(exists) {
399 ev.type = ClientMessage;
400 - ev.xclient.window = c->win;
401 - ev.xclient.message_type = wmatom[WMProtocols];
402 + ev.xclient.window = w;
403 + ev.xclient.message_type = mt;
404 ev.xclient.format = 32;
405 - ev.xclient.data.l[0] = proto;
406 - ev.xclient.data.l[1] = CurrentTime;
407 - XSendEvent(dpy, c->win, False, NoEventMask, &ev);
408 + ev.xclient.data.l[0] = d0;
409 + ev.xclient.data.l[1] = d1;
410 + ev.xclient.data.l[2] = d2;
411 + ev.xclient.data.l[3] = d3;
412 + ev.xclient.data.l[4] = d4;
413 + XSendEvent(dpy, w, False, mask, &ev);
414 }
415 return exists;
416 }
417 @@ -1540,7 +1705,7 @@
418 XA_WINDOW, 32, PropModeReplace,
419 (unsigned char *) &(c->win), 1);
420 }
421 - sendevent(c, wmatom[WMTakeFocus]);
422 + sendevent(c->win, wmatom[WMTakeFocus], NoEventMask, wmatom[WMTakeFocus], CurrentTime, 0, 0, 0);
423 }
424
425 void
426 @@ -1620,12 +1785,18 @@
427 wmatom[WMTakeFocus] = XInternAtom(dpy, "WM_TAKE_FOCUS", False);
428 netatom[NetActiveWindow] = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", False);
429 netatom[NetSupported] = XInternAtom(dpy, "_NET_SUPPORTED", False);
430 + netatom[NetSystemTray] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_S0", False);
431 + netatom[NetSystemTrayOP] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_OPCODE", False);
432 + netatom[NetSystemTrayOrientation] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_ORIENTATION", False);
433 netatom[NetWMName] = XInternAtom(dpy, "_NET_WM_NAME", False);
434 netatom[NetWMState] = XInternAtom(dpy, "_NET_WM_STATE", False);
435 netatom[NetWMFullscreen] = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False);
436 netatom[NetWMWindowType] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False);
437 netatom[NetWMWindowTypeDialog] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DIALOG", False);
438 netatom[NetClientList] = XInternAtom(dpy, "_NET_CLIENT_LIST", False);
439 + xatom[Manager] = XInternAtom(dpy, "MANAGER", False);
440 + xatom[Xembed] = XInternAtom(dpy, "_XEMBED", False);
441 + xatom[XembedInfo] = XInternAtom(dpy, "_XEMBED_INFO", False);
442 /* init cursors */
443 cursor[CurNormal] = XCreateFontCursor(dpy, XC_left_ptr);
444 cursor[CurResize] = XCreateFontCursor(dpy, XC_sizing);
445 @@ -1642,6 +1813,8 @@
446 XSetLineAttributes(dpy, dc.gc, 1, LineSolid, CapButt, JoinMiter);
447 if(!dc.font.set)
448 XSetFont(dpy, dc.gc, dc.font.xfont->fid);
449 + /* init system tray */
450 + updatesystray();
451 /* init bars */
452 updatebars();
453 updatestatus();
454 @@ -1751,7 +1924,18 @@
455 togglebar(const Arg *arg) {
456 selmon->showbar = !selmon->showbar;
457 updatebarpos(selmon);
458 - XMoveResizeWindow(dpy, selmon->barwin, selmon->wx, selmon->by, selmon->ww, bh);
459 + resizebarwin(selmon);
460 + if(showsystray) {
461 + XWindowChanges wc;
462 + if(!selmon->showbar)
463 + wc.y = -bh;
464 + else if(selmon->showbar) {
465 + wc.y = 0;
466 + if(!selmon->topbar)
467 + wc.y = selmon->mh - bh;
468 + }
469 + XConfigureWindow(dpy, systray->win, CWY, &wc);
470 + }
471 arrange(selmon);
472 }
473
474 @@ -1841,11 +2025,18 @@
475 else
476 unmanage(c, False);
477 }
478 + else if((c = wintosystrayicon(ev->window))) {
479 + removesystrayicon(c);
480 + resizebarwin(selmon);
481 + updatesystray();
482 + }
483 }
484
485 void
486 updatebars(void) {
487 + unsigned int w;
488 Monitor *m;
489 +
490 XSetWindowAttributes wa = {
491 .override_redirect = True,
492 .background_pixmap = ParentRelative,
493 @@ -1854,10 +2045,15 @@
494 for(m = mons; m; m = m->next) {
495 if (m->barwin)
496 continue;
497 - m->barwin = XCreateWindow(dpy, root, m->wx, m->by, m->ww, bh, 0, DefaultDepth(dpy, screen),
498 + w = m->ww;
499 + if(showsystray && m == selmon)
500 + w -= getsystraywidth();
501 + m->barwin = XCreateWindow(dpy, root, m->wx, m->by, w, bh, 0, DefaultDepth(dpy, screen),
502 CopyFromParent, DefaultVisual(dpy, screen),
503 CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa);
504 XDefineCursor(dpy, m->barwin, cursor[CurNormal]);
505 + if(showsystray && m == selmon)
506 + XMapRaised(dpy, systray->win);
507 XMapRaised(dpy, m->barwin);
508 }
509 }
510 @@ -2051,6 +2250,107 @@
511 }
512
513 void
514 +updatesystrayicongeom(Client *i, int w, int h) {
515 + if(i) {
516 + i->h = bh;
517 + if(w == h)
518 + i->w = bh;
519 + else if(h == bh)
520 + i->w = w;
521 + else
522 + i->w = (int) ((float)bh * ((float)w / (float)h));
523 + applysizehints(i, &(i->x), &(i->y), &(i->w), &(i->h), False);
524 + /* force icons into the systray dimenons if they don't want to */
525 + if(i->h > bh) {
526 + if(i->w == i->h)
527 + i->w = bh;
528 + else
529 + i->w = (int) ((float)bh * ((float)i->w / (float)i->h));
530 + i->h = bh;
531 + }
532 + }
533 +}
534 +
535 +void
536 +updatesystrayiconstate(Client *i, XPropertyEvent *ev) {
537 + long flags;
538 + int code = 0;
539 +
540 + if(!showsystray || !i || ev->atom != xatom[XembedInfo] ||
541 + !(flags = getatomprop(i, xatom[XembedInfo])))
542 + return;
543 +
544 + if(flags & XEMBED_MAPPED && !i->tags) {
545 + i->tags = 1;
546 + code = XEMBED_WINDOW_ACTIVATE;
547 + XMapRaised(dpy, i->win);
548 + setclientstate(i, NormalState);
549 + }
550 + else if(!(flags & XEMBED_MAPPED) && i->tags) {
551 + i->tags = 0;
552 + code = XEMBED_WINDOW_DEACTIVATE;
553 + XUnmapWindow(dpy, i->win);
554 + setclientstate(i, WithdrawnState);
555 + }
556 + else
557 + return;
558 + sendevent(i->win, xatom[Xembed], StructureNotifyMask, CurrentTime, code, 0,
559 + systray->win, XEMBED_EMBEDDED_VERSION);
560 +}
561 +
562 +void
563 +updatesystray(void) {
564 + XSetWindowAttributes wa;
565 + Client *i;
566 + unsigned int x = selmon->mx + selmon->mw;
567 + unsigned int w = 1;
568 +
569 + if(!showsystray)
570 + return;
571 + if(!systray) {
572 + /* init systray */
573 + if(!(systray = (Systray *)calloc(1, sizeof(Systray))))
574 + die("fatal: could not malloc() %u bytes\n", sizeof(Systray));
575 + systray->win = XCreateSimpleWindow(dpy, root, x, selmon->by, w, bh, 0, 0, dc.sel[ColBG]);
576 + wa.event_mask = ButtonPressMask | ExposureMask;
577 + wa.override_redirect = True;
578 + wa.background_pixmap = ParentRelative;
579 + wa.background_pixel = dc.norm[ColBG];
580 + XSelectInput(dpy, systray->win, SubstructureNotifyMask);
581 + XChangeProperty(dpy, systray->win, netatom[NetSystemTrayOrientation], XA_CARDINAL, 32,
582 + PropModeReplace, (unsigned char *)&systrayorientation, 1);
583 + XChangeWindowAttributes(dpy, systray->win, CWEventMask|CWOverrideRedirect|CWBackPixel|CWBackPixmap, &wa);
584 + XMapRaised(dpy, systray->win);
585 + XSetSelectionOwner(dpy, netatom[NetSystemTray], systray->win, CurrentTime);
586 + if(XGetSelectionOwner(dpy, netatom[NetSystemTray]) == systray->win) {
587 + sendevent(root, xatom[Manager], StructureNotifyMask, CurrentTime, netatom[NetSystemTray], systray->win, 0, 0);
588 + XSync(dpy, False);
589 + }
590 + else {
591 + fprintf(stderr, "dwm: unable to obtain system tray.\n");
592 + free(systray);
593 + systray = NULL;
594 + return;
595 + }
596 + }
597 + for(w = 0, i = systray->icons; i; i = i->next) {
598 + XMapRaised(dpy, i->win);
599 + w += systrayspacing;
600 + XMoveResizeWindow(dpy, i->win, (i->x = w), 0, i->w, i->h);
601 + w += i->w;
602 + if(i->mon != selmon)
603 + i->mon = selmon;
604 + }
605 + w = w ? w + systrayspacing : 1;
606 + x -= w;
607 + XMoveResizeWindow(dpy, systray->win, x, selmon->by, w, bh);
608 + /* redraw background */
609 + XSetForeground(dpy, dc.gc, dc.norm[ColBG]);
610 + XFillRectangle(dpy, systray->win, dc.gc, 0, 0, w, bh);
611 + XSync(dpy, False);
612 +}
613 +
614 +void
615 updatewindowtype(Client *c) {
616 Atom state = getatomprop(c, netatom[NetWMState]);
617 Atom wtype = getatomprop(c, netatom[NetWMWindowType]);
618 @@ -2119,6 +2413,16 @@
619 return selmon;
620 }
621
622 +Client *
623 +wintosystrayicon(Window w) {
624 + Client *i = NULL;
625 +
626 + if(!showsystray || !w)
627 + return i;
628 + for(i = systray->icons; i && i->win != w; i = i->next) ;
629 + return i;
630 +}
631 +
632 /* There's no way to check accesses to destroyed windows, thus those cases are
633 * ignored (especially on UnmapNotify's). Other types of errors call Xlibs
634 * default error handler, which may call exit. */