dwm-status2d-systray-6.3.diff - sites - public wiki contents of suckless.org
(HTM) git clone git://git.suckless.org/sites
(DIR) Log
(DIR) Files
(DIR) Refs
---
dwm-status2d-systray-6.3.diff (28133B)
---
1 diff --git a/config.def.h b/config.def.h
2 index a2ac963..86fcc84 100644
3 --- a/config.def.h
4 +++ b/config.def.h
5 @@ -3,8 +3,13 @@
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 int showbar = 1; /* 0 means no bar */
10 -static const int topbar = 1; /* 0 means bottom bar */
11 +static const unsigned int systraypinning = 0; /* 0: sloppy systray follows selected monitor, >0: pin systray to monitor X */
12 +static const unsigned int systrayonleft = 0; /* 0: systray in the right corner, >0: systray on left of status text */
13 +static const unsigned int systrayspacing = 2; /* systray spacing */
14 +static const int systraypinningfailfirst = 1; /* 1: if pinning fails, display systray on the first monitor, False: display systray on the last monitor*/
15 +static const int showsystray = 1; /* 0 means no systray */
16 +static const int showbar = 1; /* 0 means no bar */
17 +static const int topbar = 1; /* 0 means bottom bar */
18 static const char *fonts[] = { "monospace:size=10" };
19 static const char dmenufont[] = "monospace:size=10";
20 static const char col_gray1[] = "#222222";
21 @@ -101,8 +106,8 @@ static Key keys[] = {
22 /* click can be ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, ClkClientWin, or ClkRootWin */
23 static Button buttons[] = {
24 /* click event mask button function argument */
25 - { ClkLtSymbol, 0, Button1, setlayout, {0} },
26 - { ClkLtSymbol, 0, Button3, setlayout, {.v = &layouts[2]} },
27 + { ClkTagBar, MODKEY, Button1, tag, {0} },
28 + { ClkTagBar, MODKEY, Button3, toggletag, {0} },
29 { ClkWinTitle, 0, Button2, zoom, {0} },
30 { ClkStatusText, 0, Button2, spawn, {.v = termcmd } },
31 { ClkClientWin, MODKEY, Button1, movemouse, {0} },
32 diff --git a/dwm.c b/dwm.c
33 index a96f33c..8153bfe 100644
34 --- a/dwm.c
35 +++ b/dwm.c
36 @@ -57,12 +57,27 @@
37 #define TAGMASK ((1 << LENGTH(tags)) - 1)
38 #define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad)
39
40 +#define SYSTEM_TRAY_REQUEST_DOCK 0
41 +/* XEMBED messages */
42 +#define XEMBED_EMBEDDED_NOTIFY 0
43 +#define XEMBED_WINDOW_ACTIVATE 1
44 +#define XEMBED_FOCUS_IN 4
45 +#define XEMBED_MODALITY_ON 10
46 +#define XEMBED_MAPPED (1 << 0)
47 +#define XEMBED_WINDOW_ACTIVATE 1
48 +#define XEMBED_WINDOW_DEACTIVATE 2
49 +#define VERSION_MAJOR 0
50 +#define VERSION_MINOR 0
51 +#define XEMBED_EMBEDDED_VERSION (VERSION_MAJOR << 16) | VERSION_MINOR
52 +
53 /* enums */
54 enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */
55 enum { SchemeNorm, SchemeSel }; /* color schemes */
56 enum { NetSupported, NetWMName, NetWMState, NetWMCheck,
57 + NetSystemTray, NetSystemTrayOP, NetSystemTrayOrientation, NetSystemTrayOrientationHorz,
58 NetWMFullscreen, NetActiveWindow, NetWMWindowType,
59 NetWMWindowTypeDialog, NetClientList, NetLast }; /* EWMH atoms */
60 +enum { Manager, Xembed, XembedInfo, XLast }; /* Xembed atoms */
61 enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */
62 enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle,
63 ClkClientWin, ClkRootWin, ClkLast }; /* clicks */
64 @@ -141,6 +156,12 @@ typedef struct {
65 int monitor;
66 } Rule;
67
68 +typedef struct Systray Systray;
69 +struct Systray {
70 + Window win;
71 + Client *icons;
72 +};
73 +
74 /* function declarations */
75 static void applyrules(Client *c);
76 static int applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact);
77 @@ -163,6 +184,7 @@ static void detachstack(Client *c);
78 static Monitor *dirtomon(int dir);
79 static void drawbar(Monitor *m);
80 static void drawbars(void);
81 +static int drawstatusbar(Monitor *m, int bh, char* text);
82 static void enternotify(XEvent *e);
83 static void expose(XEvent *e);
84 static void focus(Client *c);
85 @@ -172,6 +194,7 @@ static void focusstack(const Arg *arg);
86 static Atom getatomprop(Client *c, Atom prop);
87 static int getrootptr(int *x, int *y);
88 static long getstate(Window w);
89 +static unsigned int getsystraywidth();
90 static int gettextprop(Window w, Atom atom, char *text, unsigned int size);
91 static void grabbuttons(Client *c, int focused);
92 static void grabkeys(void);
93 @@ -189,13 +212,16 @@ static void pop(Client *);
94 static void propertynotify(XEvent *e);
95 static void quit(const Arg *arg);
96 static Monitor *recttomon(int x, int y, int w, int h);
97 +static void removesystrayicon(Client *i);
98 static void resize(Client *c, int x, int y, int w, int h, int interact);
99 +static void resizebarwin(Monitor *m);
100 static void resizeclient(Client *c, int x, int y, int w, int h);
101 static void resizemouse(const Arg *arg);
102 +static void resizerequest(XEvent *e);
103 static void restack(Monitor *m);
104 static void run(void);
105 static void scan(void);
106 -static int sendevent(Client *c, Atom proto);
107 +static int sendevent(Window w, Atom proto, int m, long d0, long d1, long d2, long d3, long d4);
108 static void sendmon(Client *c, Monitor *m);
109 static void setclientstate(Client *c, long state);
110 static void setfocus(Client *c);
111 @@ -207,6 +233,7 @@ static void seturgent(Client *c, int urg);
112 static void showhide(Client *c);
113 static void sigchld(int unused);
114 static void spawn(const Arg *arg);
115 +static Monitor *systraytomon(Monitor *m);
116 static void tag(const Arg *arg);
117 static void tagmon(const Arg *arg);
118 static void tile(Monitor *);
119 @@ -224,20 +251,25 @@ static int updategeom(void);
120 static void updatenumlockmask(void);
121 static void updatesizehints(Client *c);
122 static void updatestatus(void);
123 +static void updatesystray(void);
124 +static void updatesystrayicongeom(Client *i, int w, int h);
125 +static void updatesystrayiconstate(Client *i, XPropertyEvent *ev);
126 static void updatetitle(Client *c);
127 static void updatewindowtype(Client *c);
128 static void updatewmhints(Client *c);
129 static void view(const Arg *arg);
130 static Client *wintoclient(Window w);
131 static Monitor *wintomon(Window w);
132 +static Client *wintosystrayicon(Window w);
133 static int xerror(Display *dpy, XErrorEvent *ee);
134 static int xerrordummy(Display *dpy, XErrorEvent *ee);
135 static int xerrorstart(Display *dpy, XErrorEvent *ee);
136 static void zoom(const Arg *arg);
137
138 /* variables */
139 +static Systray *systray = NULL;
140 static const char broken[] = "broken";
141 -static char stext[256];
142 +static char stext[1024];
143 static int screen;
144 static int sw, sh; /* X display screen geometry width, height */
145 static int bh, blw = 0; /* bar geometry */
146 @@ -258,9 +290,10 @@ static void (*handler[LASTEvent]) (XEvent *) = {
147 [MapRequest] = maprequest,
148 [MotionNotify] = motionnotify,
149 [PropertyNotify] = propertynotify,
150 + [ResizeRequest] = resizerequest,
151 [UnmapNotify] = unmapnotify
152 };
153 -static Atom wmatom[WMLast], netatom[NetLast];
154 +static Atom wmatom[WMLast], netatom[NetLast], xatom[XLast];
155 static int running = 1;
156 static Cur *cursor[CurLast];
157 static Clr **scheme;
158 @@ -440,7 +473,7 @@ buttonpress(XEvent *e)
159 arg.ui = 1 << i;
160 } else if (ev->x < x + blw)
161 click = ClkLtSymbol;
162 - else if (ev->x > selmon->ww - (int)TEXTW(stext))
163 + else if (ev->x > selmon->ww - (int)TEXTW(stext) - getsystraywidth())
164 click = ClkStatusText;
165 else
166 click = ClkWinTitle;
167 @@ -483,9 +516,16 @@ cleanup(void)
168 XUngrabKey(dpy, AnyKey, AnyModifier, root);
169 while (mons)
170 cleanupmon(mons);
171 - for (i = 0; i < CurLast; i++)
172 +
173 + if (showsystray) {
174 + XUnmapWindow(dpy, systray->win);
175 + XDestroyWindow(dpy, systray->win);
176 + free(systray);
177 + }
178 +
179 + for (i = 0; i < CurLast; i++)
180 drw_cur_free(drw, cursor[i]);
181 - for (i = 0; i < LENGTH(colors); i++)
182 + for (i = 0; i < LENGTH(colors) + 1; i++)
183 free(scheme[i]);
184 XDestroyWindow(dpy, wmcheckwin);
185 drw_free(drw);
186 @@ -513,9 +553,58 @@ cleanupmon(Monitor *mon)
187 void
188 clientmessage(XEvent *e)
189 {
190 + XWindowAttributes wa;
191 + XSetWindowAttributes swa;
192 XClientMessageEvent *cme = &e->xclient;
193 Client *c = wintoclient(cme->window);
194
195 + if (showsystray && cme->window == systray->win && cme->message_type == netatom[NetSystemTrayOP]) {
196 + /* add systray icons */
197 + if (cme->data.l[1] == SYSTEM_TRAY_REQUEST_DOCK) {
198 + if (!(c = (Client *)calloc(1, sizeof(Client))))
199 + die("fatal: could not malloc() %u bytes\n", sizeof(Client));
200 + if (!(c->win = cme->data.l[2])) {
201 + free(c);
202 + return;
203 + }
204 + c->mon = selmon;
205 + c->next = systray->icons;
206 + systray->icons = c;
207 + if (!XGetWindowAttributes(dpy, c->win, &wa)) {
208 + /* use sane defaults */
209 + wa.width = bh;
210 + wa.height = bh;
211 + wa.border_width = 0;
212 + }
213 + c->x = c->oldx = c->y = c->oldy = 0;
214 + c->w = c->oldw = wa.width;
215 + c->h = c->oldh = wa.height;
216 + c->oldbw = wa.border_width;
217 + c->bw = 0;
218 + c->isfloating = True;
219 + /* reuse tags field as mapped status */
220 + c->tags = 1;
221 + updatesizehints(c);
222 + updatesystrayicongeom(c, wa.width, wa.height);
223 + XAddToSaveSet(dpy, c->win);
224 + XSelectInput(dpy, c->win, StructureNotifyMask | PropertyChangeMask | ResizeRedirectMask);
225 + XReparentWindow(dpy, c->win, systray->win, 0, 0);
226 + /* use parents background color */
227 + swa.background_pixel = scheme[SchemeNorm][ColBg].pixel;
228 + XChangeWindowAttributes(dpy, c->win, CWBackPixel, &swa);
229 + sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_EMBEDDED_NOTIFY, 0 , systray->win, XEMBED_EMBEDDED_VERSION);
230 + /* FIXME not sure if I have to send these events, too */
231 + sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_FOCUS_IN, 0 , systray->win, XEMBED_EMBEDDED_VERSION);
232 + sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_WINDOW_ACTIVATE, 0 , systray->win, XEMBED_EMBEDDED_VERSION);
233 + sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_MODALITY_ON, 0 , systray->win, XEMBED_EMBEDDED_VERSION);
234 + XSync(dpy, False);
235 + resizebarwin(selmon);
236 + updatesystray();
237 + setclientstate(c, NormalState);
238 + }
239 + return;
240 + }
241 +
242 if (!c)
243 return;
244 if (cme->message_type == netatom[NetWMState]) {
245 @@ -568,7 +657,7 @@ configurenotify(XEvent *e)
246 for (c = m->clients; c; c = c->next)
247 if (c->isfullscreen)
248 resizeclient(c, m->mx, m->my, m->mw, m->mh);
249 - XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, m->ww, bh);
250 + resizebarwin(m);
251 }
252 focus(NULL);
253 arrange(NULL);
254 @@ -653,6 +742,11 @@ destroynotify(XEvent *e)
255
256 if ((c = wintoclient(ev->window)))
257 unmanage(c, 1);
258 + else if ((c = wintosystrayicon(ev->window))) {
259 + removesystrayicon(c);
260 + resizebarwin(selmon);
261 + updatesystray();
262 + }
263 }
264
265 void
266 @@ -693,10 +787,119 @@ dirtomon(int dir)
267 return m;
268 }
269
270 +int
271 +drawstatusbar(Monitor *m, int bh, char* stext) {
272 + int ret, i, w, x, len;
273 + short isCode = 0;
274 + char *text;
275 + char *p;
276 +
277 + len = strlen(stext) + 1 ;
278 + if (!(text = (char*) malloc(sizeof(char)*len)))
279 + die("malloc");
280 + p = text;
281 + memcpy(text, stext, len);
282 +
283 + /* compute width of the status text */
284 + w = 0;
285 + i = -1;
286 + while (text[++i]) {
287 + if (text[i] == '^') {
288 + if (!isCode) {
289 + isCode = 1;
290 + text[i] = '\0';
291 + w += TEXTW(text) - lrpad;
292 + text[i] = '^';
293 + if (text[++i] == 'f')
294 + w += atoi(text + ++i);
295 + } else {
296 + isCode = 0;
297 + text = text + i + 1;
298 + i = -1;
299 + }
300 + }
301 + }
302 + if (!isCode)
303 + w += TEXTW(text) - lrpad;
304 + else
305 + isCode = 0;
306 + text = p;
307 +
308 + w += 2; /* 1px padding on both sides */
309 + ret = m->ww - w;
310 + x = m->ww - w - getsystraywidth();
311 +
312 + drw_setscheme(drw, scheme[LENGTH(colors)]);
313 + drw->scheme[ColFg] = scheme[SchemeNorm][ColFg];
314 + drw->scheme[ColBg] = scheme[SchemeNorm][ColBg];
315 + drw_rect(drw, x, 0, w, bh, 1, 1);
316 + x++;
317 +
318 + /* process status text */
319 + i = -1;
320 + while (text[++i]) {
321 + if (text[i] == '^' && !isCode) {
322 + isCode = 1;
323 +
324 + text[i] = '\0';
325 + w = TEXTW(text) - lrpad;
326 + drw_text(drw, x, 0, w, bh, 0, text, 0);
327 +
328 + x += w;
329 +
330 + /* process code */
331 + while (text[++i] != '^') {
332 + if (text[i] == 'c') {
333 + char buf[8];
334 + memcpy(buf, (char*)text+i+1, 7);
335 + buf[7] = '\0';
336 + drw_clr_create(drw, &drw->scheme[ColFg], buf);
337 + i += 7;
338 + } else if (text[i] == 'b') {
339 + char buf[8];
340 + memcpy(buf, (char*)text+i+1, 7);
341 + buf[7] = '\0';
342 + drw_clr_create(drw, &drw->scheme[ColBg], buf);
343 + i += 7;
344 + } else if (text[i] == 'd') {
345 + drw->scheme[ColFg] = scheme[SchemeNorm][ColFg];
346 + drw->scheme[ColBg] = scheme[SchemeNorm][ColBg];
347 + } else if (text[i] == 'r') {
348 + int rx = atoi(text + ++i);
349 + while (text[++i] != ',');
350 + int ry = atoi(text + ++i);
351 + while (text[++i] != ',');
352 + int rw = atoi(text + ++i);
353 + while (text[++i] != ',');
354 + int rh = atoi(text + ++i);
355 +
356 + drw_rect(drw, rx + x, ry, rw, rh, 1, 0);
357 + } else if (text[i] == 'f') {
358 + x += atoi(text + ++i);
359 + }
360 + }
361 +
362 + text = text + i + 1;
363 + i=-1;
364 + isCode = 0;
365 + }
366 + }
367 +
368 + if (!isCode) {
369 + w = TEXTW(text) - lrpad;
370 + drw_text(drw, x, 0, w, bh, 0, text, 0);
371 + }
372 +
373 + drw_setscheme(drw, scheme[SchemeNorm]);
374 + free(p);
375 +
376 + return ret;
377 +}
378 +
379 void
380 drawbar(Monitor *m)
381 {
382 - int x, w, tw = 0;
383 + int x, w, tw = 0, stw = 0;
384 int boxs = drw->fonts->h / 9;
385 int boxw = drw->fonts->h / 6 + 2;
386 unsigned int i, occ = 0, urg = 0;
387 @@ -705,13 +908,15 @@ drawbar(Monitor *m)
388 if (!m->showbar)
389 return;
390
391 + if(showsystray && m == systraytomon(m) && !systrayonleft)
392 + stw = getsystraywidth();
393 +
394 /* draw status first so it can be overdrawn by tags later */
395 if (m == selmon) { /* status is only drawn on selected monitor */
396 - drw_setscheme(drw, scheme[SchemeNorm]);
397 - tw = TEXTW(stext) - lrpad + 2; /* 2px right padding */
398 - drw_text(drw, m->ww - tw, 0, tw, bh, 0, stext, 0);
399 + tw = m->ww - drawstatusbar(m, bh, stext);
400 }
401
402 + resizebarwin(m);
403 for (c = m->clients; c; c = c->next) {
404 occ |= c->tags;
405 if (c->isurgent)
406 @@ -732,7 +937,7 @@ drawbar(Monitor *m)
407 drw_setscheme(drw, scheme[SchemeNorm]);
408 x = drw_text(drw, x, 0, w, bh, lrpad / 2, m->ltsymbol, 0);
409
410 - if ((w = m->ww - tw - x) > bh) {
411 + if ((w = m->ww - tw - stw - x) > bh) {
412 if (m->sel) {
413 drw_setscheme(drw, scheme[m == selmon ? SchemeSel : SchemeNorm]);
414 drw_text(drw, x, 0, w, bh, lrpad / 2, m->sel->name, 0);
415 @@ -743,7 +948,7 @@ drawbar(Monitor *m)
416 drw_rect(drw, x, 0, w, bh, 1, 1);
417 }
418 }
419 - drw_map(drw, m->barwin, 0, 0, m->ww, bh);
420 + drw_map(drw, m->barwin, 0, 0, m->ww - stw, bh);
421 }
422
423 void
424 @@ -780,8 +985,11 @@ expose(XEvent *e)
425 Monitor *m;
426 XExposeEvent *ev = &e->xexpose;
427
428 - if (ev->count == 0 && (m = wintomon(ev->window)))
429 + if (ev->count == 0 && (m = wintomon(ev->window))) {
430 drawbar(m);
431 + if (m == selmon)
432 + updatesystray();
433 + }
434 }
435
436 void
437 @@ -867,9 +1075,17 @@ getatomprop(Client *c, Atom prop)
438 unsigned char *p = NULL;
439 Atom da, atom = None;
440
441 - if (XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, XA_ATOM,
442 + /* FIXME getatomprop should return the number of items and a pointer to
443 + * the stored data instead of this workaround */
444 + Atom req = XA_ATOM;
445 + if (prop == xatom[XembedInfo])
446 + req = xatom[XembedInfo];
447 +
448 + if (XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, req,
449 &da, &di, &dl, &dl, &p) == Success && p) {
450 atom = *(Atom *)p;
451 + if (da == xatom[XembedInfo] && dl == 2)
452 + atom = ((Atom *)p)[1];
453 XFree(p);
454 }
455 return atom;
456 @@ -903,6 +1119,16 @@ getstate(Window w)
457 return result;
458 }
459
460 +unsigned int
461 +getsystraywidth()
462 +{
463 + unsigned int w = 0;
464 + Client *i;
465 + if(showsystray)
466 + for(i = systray->icons; i; w += i->w + systrayspacing, i = i->next) ;
467 + return w ? w + systrayspacing : 1;
468 +}
469 +
470 int
471 gettextprop(Window w, Atom atom, char *text, unsigned int size)
472 {
473 @@ -1007,7 +1233,8 @@ killclient(const Arg *arg)
474 {
475 if (!selmon->sel)
476 return;
477 - if (!sendevent(selmon->sel, wmatom[WMDelete])) {
478 +
479 + if (!sendevent(selmon->sel->win, wmatom[WMDelete], NoEventMask, wmatom[WMDelete], CurrentTime, 0 , 0, 0)) {
480 XGrabServer(dpy);
481 XSetErrorHandler(xerrordummy);
482 XSetCloseDownMode(dpy, DestroyAll);
483 @@ -1096,6 +1323,13 @@ maprequest(XEvent *e)
484 static XWindowAttributes wa;
485 XMapRequestEvent *ev = &e->xmaprequest;
486
487 + Client *i;
488 + if ((i = wintosystrayicon(ev->window))) {
489 + sendevent(i->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_WINDOW_ACTIVATE, 0, systray->win, XEMBED_EMBEDDED_VERSION);
490 + resizebarwin(selmon);
491 + updatesystray();
492 + }
493 +
494 if (!XGetWindowAttributes(dpy, ev->window, &wa))
495 return;
496 if (wa.override_redirect)
497 @@ -1219,7 +1453,18 @@ propertynotify(XEvent *e)
498 Window trans;
499 XPropertyEvent *ev = &e->xproperty;
500
501 - if ((ev->window == root) && (ev->atom == XA_WM_NAME))
502 + if ((c = wintosystrayicon(ev->window))) {
503 + if (ev->atom == XA_WM_NORMAL_HINTS) {
504 + updatesizehints(c);
505 + updatesystrayicongeom(c, c->w, c->h);
506 + }
507 + else
508 + updatesystrayiconstate(c, ev);
509 + resizebarwin(selmon);
510 + updatesystray();
511 + }
512 +
513 + if ((ev->window == root) && (ev->atom == XA_WM_NAME))
514 updatestatus();
515 else if (ev->state == PropertyDelete)
516 return; /* ignore */
517 @@ -1269,6 +1514,19 @@ recttomon(int x, int y, int w, int h)
518 return r;
519 }
520
521 +void
522 +removesystrayicon(Client *i)
523 +{
524 + Client **ii;
525 +
526 + if (!showsystray || !i)
527 + return;
528 + for (ii = &systray->icons; *ii && *ii != i; ii = &(*ii)->next);
529 + if (ii)
530 + *ii = i->next;
531 + free(i);
532 +}
533 +
534 void
535 resize(Client *c, int x, int y, int w, int h, int interact)
536 {
537 @@ -1276,6 +1534,14 @@ resize(Client *c, int x, int y, int w, int h, int interact)
538 resizeclient(c, x, y, w, h);
539 }
540
541 +void
542 +resizebarwin(Monitor *m) {
543 + unsigned int w = m->ww;
544 + if (showsystray && m == systraytomon(m) && !systrayonleft)
545 + w -= getsystraywidth();
546 + XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, w, bh);
547 +}
548 +
549 void
550 resizeclient(Client *c, int x, int y, int w, int h)
551 {
552 @@ -1348,6 +1614,19 @@ resizemouse(const Arg *arg)
553 }
554 }
555
556 +void
557 +resizerequest(XEvent *e)
558 +{
559 + XResizeRequestEvent *ev = &e->xresizerequest;
560 + Client *i;
561 +
562 + if ((i = wintosystrayicon(ev->window))) {
563 + updatesystrayicongeom(i, ev->width, ev->height);
564 + resizebarwin(selmon);
565 + updatesystray();
566 + }
567 +}
568 +
569 void
570 restack(Monitor *m)
571 {
572 @@ -1437,26 +1716,37 @@ setclientstate(Client *c, long state)
573 }
574
575 int
576 -sendevent(Client *c, Atom proto)
577 +sendevent(Window w, Atom proto, int mask, long d0, long d1, long d2, long d3, long d4)
578 {
579 int n;
580 - Atom *protocols;
581 + Atom *protocols, mt;
582 int exists = 0;
583 XEvent ev;
584
585 - if (XGetWMProtocols(dpy, c->win, &protocols, &n)) {
586 - while (!exists && n--)
587 - exists = protocols[n] == proto;
588 - XFree(protocols);
589 + if (proto == wmatom[WMTakeFocus] || proto == wmatom[WMDelete]) {
590 + mt = wmatom[WMProtocols];
591 + if (XGetWMProtocols(dpy, w, &protocols, &n)) {
592 + while (!exists && n--)
593 + exists = protocols[n] == proto;
594 + XFree(protocols);
595 + }
596 }
597 + else {
598 + exists = True;
599 + mt = proto;
600 + }
601 +
602 if (exists) {
603 ev.type = ClientMessage;
604 - ev.xclient.window = c->win;
605 - ev.xclient.message_type = wmatom[WMProtocols];
606 + ev.xclient.window = w;
607 + ev.xclient.message_type = mt;
608 ev.xclient.format = 32;
609 - ev.xclient.data.l[0] = proto;
610 - ev.xclient.data.l[1] = CurrentTime;
611 - XSendEvent(dpy, c->win, False, NoEventMask, &ev);
612 + ev.xclient.data.l[0] = d0;
613 + ev.xclient.data.l[1] = d1;
614 + ev.xclient.data.l[2] = d2;
615 + ev.xclient.data.l[3] = d3;
616 + ev.xclient.data.l[4] = d4;
617 + XSendEvent(dpy, w, False, mask, &ev);
618 }
619 return exists;
620 }
621 @@ -1470,7 +1760,7 @@ setfocus(Client *c)
622 XA_WINDOW, 32, PropModeReplace,
623 (unsigned char *) &(c->win), 1);
624 }
625 - sendevent(c, wmatom[WMTakeFocus]);
626 + sendevent(c->win, wmatom[WMTakeFocus], NoEventMask, wmatom[WMTakeFocus], CurrentTime, 0, 0, 0);
627 }
628
629 void
630 @@ -1558,22 +1848,32 @@ setup(void)
631 wmatom[WMState] = XInternAtom(dpy, "WM_STATE", False);
632 wmatom[WMTakeFocus] = XInternAtom(dpy, "WM_TAKE_FOCUS", False);
633 netatom[NetActiveWindow] = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", False);
634 - netatom[NetSupported] = XInternAtom(dpy, "_NET_SUPPORTED", False);
635 - netatom[NetWMName] = XInternAtom(dpy, "_NET_WM_NAME", False);
636 + netatom[NetSupported] = XInternAtom(dpy, "_NET_SUPPORTED", False);
637 + netatom[NetSystemTray] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_S0", False);
638 + netatom[NetSystemTrayOP] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_OPCODE", False);
639 + netatom[NetSystemTrayOrientation] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_ORIENTATION", False);
640 + netatom[NetSystemTrayOrientationHorz] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_ORIENTATION_HORZ", False);
641 + netatom[NetWMName] = XInternAtom(dpy, "_NET_WM_NAME", False);
642 netatom[NetWMState] = XInternAtom(dpy, "_NET_WM_STATE", False);
643 netatom[NetWMCheck] = XInternAtom(dpy, "_NET_SUPPORTING_WM_CHECK", False);
644 netatom[NetWMFullscreen] = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False);
645 netatom[NetWMWindowType] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False);
646 netatom[NetWMWindowTypeDialog] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DIALOG", False);
647 netatom[NetClientList] = XInternAtom(dpy, "_NET_CLIENT_LIST", False);
648 - /* init cursors */
649 + xatom[Manager] = XInternAtom(dpy, "MANAGER", False);
650 + xatom[Xembed] = XInternAtom(dpy, "_XEMBED", False);
651 + xatom[XembedInfo] = XInternAtom(dpy, "_XEMBED_INFO", False);
652 + /* init cursors */
653 cursor[CurNormal] = drw_cur_create(drw, XC_left_ptr);
654 cursor[CurResize] = drw_cur_create(drw, XC_sizing);
655 cursor[CurMove] = drw_cur_create(drw, XC_fleur);
656 /* init appearance */
657 - scheme = ecalloc(LENGTH(colors), sizeof(Clr *));
658 + scheme = ecalloc(LENGTH(colors) + 1, sizeof(Clr *));
659 + scheme[LENGTH(colors)] = drw_scm_create(drw, colors[0], 3);
660 for (i = 0; i < LENGTH(colors); i++)
661 scheme[i] = drw_scm_create(drw, colors[i], 3);
662 + /* init system tray */
663 + updatesystray();
664 /* init bars */
665 updatebars();
666 updatestatus();
667 @@ -1707,7 +2007,18 @@ togglebar(const Arg *arg)
668 {
669 selmon->showbar = !selmon->showbar;
670 updatebarpos(selmon);
671 - XMoveResizeWindow(dpy, selmon->barwin, selmon->wx, selmon->by, selmon->ww, bh);
672 + resizebarwin(selmon);
673 + if (showsystray) {
674 + XWindowChanges wc;
675 + if (!selmon->showbar)
676 + wc.y = -bh;
677 + else if (selmon->showbar) {
678 + wc.y = 0;
679 + if (!selmon->topbar)
680 + wc.y = selmon->mh - bh;
681 + }
682 + XConfigureWindow(dpy, systray->win, CWY, &wc);
683 + }
684 arrange(selmon);
685 }
686
687 @@ -1802,11 +2113,18 @@ unmapnotify(XEvent *e)
688 else
689 unmanage(c, 0);
690 }
691 + else if ((c = wintosystrayicon(ev->window))) {
692 + /* KLUDGE! sometimes icons occasionally unmap their windows, but do
693 + * _not_ destroy them. We map those windows back */
694 + XMapRaised(dpy, c->win);
695 + updatesystray();
696 + }
697 }
698
699 void
700 updatebars(void)
701 {
702 + unsigned int w;
703 Monitor *m;
704 XSetWindowAttributes wa = {
705 .override_redirect = True,
706 @@ -1817,10 +2135,15 @@ updatebars(void)
707 for (m = mons; m; m = m->next) {
708 if (m->barwin)
709 continue;
710 - m->barwin = XCreateWindow(dpy, root, m->wx, m->by, m->ww, bh, 0, DefaultDepth(dpy, screen),
711 + w = m->ww;
712 + if (showsystray && m == systraytomon(m))
713 + w -= getsystraywidth();
714 + m->barwin = XCreateWindow(dpy, root, m->wx, m->by, w, bh, 0, DefaultDepth(dpy, screen),
715 CopyFromParent, DefaultVisual(dpy, screen),
716 CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa);
717 XDefineCursor(dpy, m->barwin, cursor[CurNormal]->cursor);
718 + if (showsystray && m == systraytomon(m))
719 + XMapRaised(dpy, systray->win);
720 XMapRaised(dpy, m->barwin);
721 XSetClassHint(dpy, m->barwin, &ch);
722 }
723 @@ -1996,6 +2319,125 @@ updatestatus(void)
724 if (!gettextprop(root, XA_WM_NAME, stext, sizeof(stext)))
725 strcpy(stext, "dwm-"VERSION);
726 drawbar(selmon);
727 + updatesystray();
728 +}
729 +
730 +
731 +void
732 +updatesystrayicongeom(Client *i, int w, int h)
733 +{
734 + if (i) {
735 + i->h = bh;
736 + if (w == h)
737 + i->w = bh;
738 + else if (h == bh)
739 + i->w = w;
740 + else
741 + i->w = (int) ((float)bh * ((float)w / (float)h));
742 + applysizehints(i, &(i->x), &(i->y), &(i->w), &(i->h), False);
743 + /* force icons into the systray dimensions if they don't want to */
744 + if (i->h > bh) {
745 + if (i->w == i->h)
746 + i->w = bh;
747 + else
748 + i->w = (int) ((float)bh * ((float)i->w / (float)i->h));
749 + i->h = bh;
750 + }
751 + }
752 +}
753 +
754 +void
755 +updatesystrayiconstate(Client *i, XPropertyEvent *ev)
756 +{
757 + long flags;
758 + int code = 0;
759 +
760 + if (!showsystray || !i || ev->atom != xatom[XembedInfo] ||
761 + !(flags = getatomprop(i, xatom[XembedInfo])))
762 + return;
763 +
764 + if (flags & XEMBED_MAPPED && !i->tags) {
765 + i->tags = 1;
766 + code = XEMBED_WINDOW_ACTIVATE;
767 + XMapRaised(dpy, i->win);
768 + setclientstate(i, NormalState);
769 + }
770 + else if (!(flags & XEMBED_MAPPED) && i->tags) {
771 + i->tags = 0;
772 + code = XEMBED_WINDOW_DEACTIVATE;
773 + XUnmapWindow(dpy, i->win);
774 + setclientstate(i, WithdrawnState);
775 + }
776 + else
777 + return;
778 + sendevent(i->win, xatom[Xembed], StructureNotifyMask, CurrentTime, code, 0,
779 + systray->win, XEMBED_EMBEDDED_VERSION);
780 +}
781 +
782 +void
783 +updatesystray(void)
784 +{
785 + XSetWindowAttributes wa;
786 + XWindowChanges wc;
787 + Client *i;
788 + Monitor *m = systraytomon(NULL);
789 + unsigned int x = m->mx + m->mw;
790 + unsigned int sw = TEXTW(stext) - lrpad + systrayspacing;
791 + unsigned int w = 1;
792 +
793 + if (!showsystray)
794 + return;
795 + if (systrayonleft)
796 + x -= sw + lrpad / 2;
797 + if (!systray) {
798 + /* init systray */
799 + if (!(systray = (Systray *)calloc(1, sizeof(Systray))))
800 + die("fatal: could not malloc() %u bytes\n", sizeof(Systray));
801 + systray->win = XCreateSimpleWindow(dpy, root, x, m->by, w, bh, 0, 0, scheme[SchemeSel][ColBg].pixel);
802 + wa.event_mask = ButtonPressMask | ExposureMask;
803 + wa.override_redirect = True;
804 + wa.background_pixel = scheme[SchemeNorm][ColBg].pixel;
805 + XSelectInput(dpy, systray->win, SubstructureNotifyMask);
806 + XChangeProperty(dpy, systray->win, netatom[NetSystemTrayOrientation], XA_CARDINAL, 32,
807 + PropModeReplace, (unsigned char *)&netatom[NetSystemTrayOrientationHorz], 1);
808 + XChangeWindowAttributes(dpy, systray->win, CWEventMask|CWOverrideRedirect|CWBackPixel, &wa);
809 + XMapRaised(dpy, systray->win);
810 + XSetSelectionOwner(dpy, netatom[NetSystemTray], systray->win, CurrentTime);
811 + if (XGetSelectionOwner(dpy, netatom[NetSystemTray]) == systray->win) {
812 + sendevent(root, xatom[Manager], StructureNotifyMask, CurrentTime, netatom[NetSystemTray], systray->win, 0, 0);
813 + XSync(dpy, False);
814 + }
815 + else {
816 + fprintf(stderr, "dwm: unable to obtain system tray.\n");
817 + free(systray);
818 + systray = NULL;
819 + return;
820 + }
821 + }
822 + for (w = 0, i = systray->icons; i; i = i->next) {
823 + /* make sure the background color stays the same */
824 + wa.background_pixel = scheme[SchemeNorm][ColBg].pixel;
825 + XChangeWindowAttributes(dpy, i->win, CWBackPixel, &wa);
826 + XMapRaised(dpy, i->win);
827 + w += systrayspacing;
828 + i->x = w;
829 + XMoveResizeWindow(dpy, i->win, i->x, 0, i->w, i->h);
830 + w += i->w;
831 + if (i->mon != m)
832 + i->mon = m;
833 + }
834 + w = w ? w + systrayspacing : 1;
835 + x -= w;
836 + XMoveResizeWindow(dpy, systray->win, x, m->by, w, bh);
837 + wc.x = x; wc.y = m->by; wc.width = w; wc.height = bh;
838 + wc.stack_mode = Above; wc.sibling = m->barwin;
839 + XConfigureWindow(dpy, systray->win, CWX|CWY|CWWidth|CWHeight|CWSibling|CWStackMode, &wc);
840 + XMapWindow(dpy, systray->win);
841 + XMapSubwindows(dpy, systray->win);
842 + /* redraw background */
843 + XSetForeground(dpy, drw->gc, scheme[SchemeNorm][ColBg].pixel);
844 + XFillRectangle(dpy, systray->win, drw->gc, 0, 0, w, bh);
845 + XSync(dpy, False);
846 }
847
848 void
849 @@ -2063,6 +2505,16 @@ wintoclient(Window w)
850 return NULL;
851 }
852
853 +Client *
854 +wintosystrayicon(Window w) {
855 + Client *i = NULL;
856 +
857 + if (!showsystray || !w)
858 + return i;
859 + for (i = systray->icons; i && i->win != w; i = i->next) ;
860 + return i;
861 +}
862 +
863 Monitor *
864 wintomon(Window w)
865 {
866 @@ -2116,6 +2568,22 @@ xerrorstart(Display *dpy, XErrorEvent *ee)
867 return -1;
868 }
869
870 +Monitor *
871 +systraytomon(Monitor *m) {
872 + Monitor *t;
873 + int i, n;
874 + if(!systraypinning) {
875 + if(!m)
876 + return selmon;
877 + return m == selmon ? m : NULL;
878 + }
879 + for(n = 1, t = mons; t && t->next; n++, t = t->next) ;
880 + for(i = 1, t = mons; t && t->next && i < systraypinning; i++, t = t->next) ;
881 + if(systraypinningfailfirst && n < systraypinning)
882 + return mons;
883 + return t;
884 +}
885 +
886 void
887 zoom(const Arg *arg)
888 {