dwm-statusbar.diff (27546B) download
1diff --git a/dwm.1 b/dwm.1
2index ddc8321..53cabd8 100644
3--- a/dwm.1
4+++ b/dwm.1
5@@ -30,6 +30,14 @@ top left corner. The tags which are applied to one or more windows are
6 indicated with an empty square in the top left corner.
7 .P
8 dwm draws a small border around windows to indicate the focus state.
9+.P
10+On start, dwm can start additional programs that may be specified in two special
11+shell scripts (see the FILES section below), autostart_blocking.sh and
12+autostart.sh. The former is executed first and dwm will wait for its
13+termination before starting. The latter is executed in the background before
14+dwm enters its handler loop.
15+.P
16+Either of these files may be omitted.
17 .SH OPTIONS
18 .TP
19 .B \-v
20@@ -92,6 +100,12 @@ Sets monocle layout.
21 .B Mod1\-space
22 Toggles between current and previous layout.
23 .TP
24+.B Mod1\-Control\-,
25+Cycles backwards in layout list.
26+.TP
27+.B Mod1\-Control\-.
28+Cycles forwards in layout list.
29+.TP
30 .B Mod1\-j
31 Focus next window.
32 .TP
33@@ -152,6 +166,21 @@ Toggles focused window between floating and tiled state.
34 .TP
35 .B Mod1\-Button3
36 Resize focused window while dragging. Tiled windows will be toggled to the floating state.
37+.SH FILES
38+The files containing programs to be started along with dwm are searched for in
39+the following directories:
40+.IP "1. $XDG_DATA_HOME/dwm"
41+.IP "2. $HOME/.local/share/dwm"
42+.IP "3. $HOME/.dwm"
43+.P
44+The first existing directory is scanned for any of the autostart files below.
45+.TP 15
46+autostart.sh
47+This file is started as a shell background process before dwm enters its handler
48+loop.
49+.TP 15
50+autostart_blocking.sh
51+This file is started before any autostart.sh; dwm waits for its termination.
52 .SH CUSTOMIZATION
53 dwm is customized by creating a custom config.h and (re)compiling the source
54 code. This keeps it fast, secure and simple.
55diff --git a/dwm.c b/dwm.c
56index f1d86b2..07d2672 100644
57--- a/dwm.c
58+++ b/dwm.c
59@@ -29,6 +29,7 @@
60 #include <string.h>
61 #include <unistd.h>
62 #include <sys/types.h>
63+#include <sys/stat.h>
64 #include <sys/wait.h>
65 #include <X11/cursorfont.h>
66 #include <X11/keysym.h>
67@@ -57,12 +58,28 @@
68 #define TAGMASK ((1 << LENGTH(tags)) - 1)
69 #define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad)
70
71+#define SYSTEM_TRAY_REQUEST_DOCK 0
72+/* XEMBED messages */
73+#define XEMBED_EMBEDDED_NOTIFY 0
74+#define XEMBED_WINDOW_ACTIVATE 1
75+#define XEMBED_FOCUS_IN 4
76+#define XEMBED_MODALITY_ON 10
77+#define XEMBED_MAPPED (1 << 0)
78+#define XEMBED_WINDOW_ACTIVATE 1
79+#define XEMBED_WINDOW_DEACTIVATE 2
80+#define VERSION_MAJOR 0
81+#define VERSION_MINOR 0
82+#define XEMBED_EMBEDDED_VERSION (VERSION_MAJOR << 16) | VERSION_MINOR
83+
84+
85 /* enums */
86 enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */
87 enum { SchemeNorm, SchemeSel }; /* color schemes */
88 enum { NetSupported, NetWMName, NetWMState, NetWMCheck,
89+ NetSystemTray, NetSystemTrayOP, NetSystemTrayOrientation, NetSystemTrayOrientationHorz,
90 NetWMFullscreen, NetActiveWindow, NetWMWindowType,
91 NetWMWindowTypeDialog, NetClientList, NetLast }; /* EWMH atoms */
92+enum { Manager, Xembed, XembedInfo, XLast }; /* Xembed atoms */
93 enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */
94 enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle,
95 ClkClientWin, ClkRootWin, ClkLast }; /* clicks */
96@@ -141,6 +158,12 @@ typedef struct {
97 int monitor;
98 } Rule;
99
100+typedef struct Systray Systray;
101+struct Systray {
102+ Window win;
103+ Client *icons;
104+};
105+
106 /* function declarations */
107 static void applyrules(Client *c);
108 static int applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact);
109@@ -157,6 +180,7 @@ static void configure(Client *c);
110 static void configurenotify(XEvent *e);
111 static void configurerequest(XEvent *e);
112 static Monitor *createmon(void);
113+static void cyclelayout(const Arg *arg);
114 static void destroynotify(XEvent *e);
115 static void detach(Client *c);
116 static void detachstack(Client *c);
117@@ -172,6 +196,7 @@ static void focusstack(const Arg *arg);
118 static Atom getatomprop(Client *c, Atom prop);
119 static int getrootptr(int *x, int *y);
120 static long getstate(Window w);
121+static unsigned int getsystraywidth();
122 static int gettextprop(Window w, Atom atom, char *text, unsigned int size);
123 static void grabbuttons(Client *c, int focused);
124 static void grabkeys(void);
125@@ -189,13 +214,17 @@ static void pop(Client *c);
126 static void propertynotify(XEvent *e);
127 static void quit(const Arg *arg);
128 static Monitor *recttomon(int x, int y, int w, int h);
129+static void removesystrayicon(Client *i);
130 static void resize(Client *c, int x, int y, int w, int h, int interact);
131+static void resizebarwin(Monitor *m);
132 static void resizeclient(Client *c, int x, int y, int w, int h);
133 static void resizemouse(const Arg *arg);
134+static void resizerequest(XEvent *e);
135 static void restack(Monitor *m);
136 static void run(void);
137+static void runautostart(void);
138 static void scan(void);
139-static int sendevent(Client *c, Atom proto);
140+static int sendevent(Window w, Atom proto, int m, long d0, long d1, long d2, long d3, long d4);
141 static void sendmon(Client *c, Monitor *m);
142 static void setclientstate(Client *c, long state);
143 static void setfocus(Client *c);
144@@ -206,6 +235,8 @@ static void setup(void);
145 static void seturgent(Client *c, int urg);
146 static void showhide(Client *c);
147 static void spawn(const Arg *arg);
148+static int statuswidth(void);
149+static Monitor *systraytomon(Monitor *m);
150 static void tag(const Arg *arg);
151 static void tagmon(const Arg *arg);
152 static void tile(Monitor *m);
153@@ -223,18 +254,23 @@ static int updategeom(void);
154 static void updatenumlockmask(void);
155 static void updatesizehints(Client *c);
156 static void updatestatus(void);
157+static void updatesystray(void);
158+static void updatesystrayicongeom(Client *i, int w, int h);
159+static void updatesystrayiconstate(Client *i, XPropertyEvent *ev);
160 static void updatetitle(Client *c);
161 static void updatewindowtype(Client *c);
162 static void updatewmhints(Client *c);
163 static void view(const Arg *arg);
164 static Client *wintoclient(Window w);
165 static Monitor *wintomon(Window w);
166+static Client *wintosystrayicon(Window w);
167 static int xerror(Display *dpy, XErrorEvent *ee);
168 static int xerrordummy(Display *dpy, XErrorEvent *ee);
169 static int xerrorstart(Display *dpy, XErrorEvent *ee);
170 static void zoom(const Arg *arg);
171
172 /* variables */
173+static Systray *systray = NULL;
174 static const char broken[] = "broken";
175 static char stext[256];
176 static int screen;
177@@ -257,9 +293,10 @@ static void (*handler[LASTEvent]) (XEvent *) = {
178 [MapRequest] = maprequest,
179 [MotionNotify] = motionnotify,
180 [PropertyNotify] = propertynotify,
181+ [ResizeRequest] = resizerequest,
182 [UnmapNotify] = unmapnotify
183 };
184-static Atom wmatom[WMLast], netatom[NetLast];
185+static Atom wmatom[WMLast], netatom[NetLast], xatom[XLast];
186 static int running = 1;
187 static Cur *cursor[CurLast];
188 static Clr **scheme;
189@@ -441,7 +478,7 @@ buttonpress(XEvent *e)
190 arg.ui = 1 << i;
191 } else if (ev->x < x + TEXTW(selmon->ltsymbol))
192 click = ClkLtSymbol;
193- else if (ev->x > selmon->ww - (int)TEXTW(stext))
194+ else if (ev->x > selmon->ww - (int)TEXTW(stext) - getsystraywidth())
195 click = ClkStatusText;
196 else
197 click = ClkWinTitle;
198@@ -484,6 +521,13 @@ cleanup(void)
199 XUngrabKey(dpy, AnyKey, AnyModifier, root);
200 while (mons)
201 cleanupmon(mons);
202+
203+ if (showsystray) {
204+ XUnmapWindow(dpy, systray->win);
205+ XDestroyWindow(dpy, systray->win);
206+ free(systray);
207+ }
208+
209 for (i = 0; i < CurLast; i++)
210 drw_cur_free(drw, cursor[i]);
211 for (i = 0; i < LENGTH(colors); i++)
212@@ -515,9 +559,58 @@ cleanupmon(Monitor *mon)
213 void
214 clientmessage(XEvent *e)
215 {
216+ XWindowAttributes wa;
217+ XSetWindowAttributes swa;
218 XClientMessageEvent *cme = &e->xclient;
219 Client *c = wintoclient(cme->window);
220
221+ if (showsystray && cme->window == systray->win && cme->message_type == netatom[NetSystemTrayOP]) {
222+ /* add systray icons */
223+ if (cme->data.l[1] == SYSTEM_TRAY_REQUEST_DOCK) {
224+ if (!(c = (Client *)calloc(1, sizeof(Client))))
225+ die("fatal: could not malloc() %u bytes\n", sizeof(Client));
226+ if (!(c->win = cme->data.l[2])) {
227+ free(c);
228+ return;
229+ }
230+ c->mon = selmon;
231+ c->next = systray->icons;
232+ systray->icons = c;
233+ if (!XGetWindowAttributes(dpy, c->win, &wa)) {
234+ /* use sane defaults */
235+ wa.width = bh;
236+ wa.height = bh;
237+ wa.border_width = 0;
238+ }
239+ c->x = c->oldx = c->y = c->oldy = 0;
240+ c->w = c->oldw = wa.width;
241+ c->h = c->oldh = wa.height;
242+ c->oldbw = wa.border_width;
243+ c->bw = 0;
244+ c->isfloating = True;
245+ /* reuse tags field as mapped status */
246+ c->tags = 1;
247+ updatesizehints(c);
248+ updatesystrayicongeom(c, wa.width, wa.height);
249+ XAddToSaveSet(dpy, c->win);
250+ XSelectInput(dpy, c->win, StructureNotifyMask | PropertyChangeMask | ResizeRedirectMask);
251+ XReparentWindow(dpy, c->win, systray->win, 0, 0);
252+ /* use parents background color */
253+ swa.background_pixel = scheme[SchemeNorm][ColBg].pixel;
254+ XChangeWindowAttributes(dpy, c->win, CWBackPixel, &swa);
255+ sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_EMBEDDED_NOTIFY, 0 , systray->win, XEMBED_EMBEDDED_VERSION);
256+ /* FIXME not sure if I have to send these events, too */
257+ sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_FOCUS_IN, 0 , systray->win, XEMBED_EMBEDDED_VERSION);
258+ sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_WINDOW_ACTIVATE, 0 , systray->win, XEMBED_EMBEDDED_VERSION);
259+ sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_MODALITY_ON, 0 , systray->win, XEMBED_EMBEDDED_VERSION);
260+ XSync(dpy, False);
261+ resizebarwin(selmon);
262+ updatesystray();
263+ setclientstate(c, NormalState);
264+ }
265+ return;
266+ }
267+
268 if (!c)
269 return;
270 if (cme->message_type == netatom[NetWMState]) {
271@@ -570,7 +663,7 @@ configurenotify(XEvent *e)
272 for (c = m->clients; c; c = c->next)
273 if (c->isfullscreen)
274 resizeclient(c, m->mx, m->my, m->mw, m->mh);
275- XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, m->ww, bh);
276+ resizebarwin(m);
277 }
278 focus(NULL);
279 arrange(NULL);
280@@ -647,6 +740,23 @@ createmon(void)
281 return m;
282 }
283
284+void
285+cyclelayout(const Arg *arg) {
286+ Layout *l;
287+ for(l = (Layout *)layouts; l != selmon->lt[selmon->sellt]; l++);
288+ if(arg->i > 0) {
289+ if(l->symbol && (l + 1)->symbol)
290+ setlayout(&((Arg) { .v = (l + 1) }));
291+ else
292+ setlayout(&((Arg) { .v = layouts }));
293+ } else {
294+ if(l != layouts && (l - 1)->symbol)
295+ setlayout(&((Arg) { .v = (l - 1) }));
296+ else
297+ setlayout(&((Arg) { .v = &layouts[LENGTH(layouts) - 2] }));
298+ }
299+}
300+
301 void
302 destroynotify(XEvent *e)
303 {
304@@ -655,6 +765,11 @@ destroynotify(XEvent *e)
305
306 if ((c = wintoclient(ev->window)))
307 unmanage(c, 1);
308+ else if ((c = wintosystrayicon(ev->window))) {
309+ removesystrayicon(c);
310+ resizebarwin(selmon);
311+ updatesystray();
312+ }
313 }
314
315 void
316@@ -695,13 +810,33 @@ dirtomon(int dir)
317 return m;
318 }
319
320+int
321+statuswidth(void) {
322+ char* ts;
323+ int tw = 0;
324+
325+ tw = TEXTW(stext);
326+ for (ts = stext; *ts; ts++) {
327+ if ((unsigned int)*ts <= LENGTH(colors))
328+ tw += lrpad;
329+ }
330+
331+// tw -= lrpad;
332+
333+ return tw;
334+}
335+
336 void
337 drawbar(Monitor *m)
338 {
339- int x, w, tw = 0;
340+ int x, w, tw = 0, stw = 0;
341 int boxs = drw->fonts->h / 9;
342 int boxw = drw->fonts->h / 6 + 2;
343 unsigned int i, occ = 0, urg = 0;
344+ char *ts = stext;
345+ char *tp = stext;
346+ int tx = 0;
347+ char ctmp;
348 Client *c;
349
350 if (!m->showbar)
351@@ -710,8 +845,19 @@ drawbar(Monitor *m)
352 /* draw status first so it can be overdrawn by tags later */
353 if (m == selmon) { /* status is only drawn on selected monitor */
354 drw_setscheme(drw, scheme[SchemeNorm]);
355- tw = TEXTW(stext) - lrpad + 2; /* 2px right padding */
356- drw_text(drw, m->ww - tw, 0, tw, bh, 0, stext, 0);
357+ tw = statuswidth();
358+ ts = stext;
359+ while (1) {
360+ if ((unsigned int)*ts > LENGTH(colors)) { ts++; continue ; }
361+ ctmp = *ts;
362+ *ts = '\0';
363+ drw_text(drw, m->ww - tw + tx - getsystraywidth(), 0, tw - tx, bh, lrpad / 2, tp, 0);
364+ tx += TEXTW(tp);
365+ if (ctmp == '\0') { break; }
366+ drw_setscheme(drw, scheme[(unsigned int)(ctmp-1)]);
367+ *ts = ctmp;
368+ tp = ++ts;
369+ }
370 }
371
372 for (c = m->clients; c; c = c->next) {
373@@ -734,7 +880,7 @@ drawbar(Monitor *m)
374 drw_setscheme(drw, scheme[SchemeNorm]);
375 x = drw_text(drw, x, 0, w, bh, lrpad / 2, m->ltsymbol, 0);
376
377- if ((w = m->ww - tw - x) > bh) {
378+ if ((w = m->ww - tw - stw - x - getsystraywidth()) > bh) {
379 if (m->sel) {
380 drw_setscheme(drw, scheme[m == selmon ? SchemeSel : SchemeNorm]);
381 drw_text(drw, x, 0, w, bh, lrpad / 2, m->sel->name, 0);
382@@ -745,7 +891,7 @@ drawbar(Monitor *m)
383 drw_rect(drw, x, 0, w, bh, 1, 1);
384 }
385 }
386- drw_map(drw, m->barwin, 0, 0, m->ww, bh);
387+ drw_map(drw, m->barwin, 0, 0, m->ww - stw, bh);
388 }
389
390 void
391@@ -782,8 +928,11 @@ expose(XEvent *e)
392 Monitor *m;
393 XExposeEvent *ev = &e->xexpose;
394
395- if (ev->count == 0 && (m = wintomon(ev->window)))
396+ if (ev->count == 0 && (m = wintomon(ev->window))) {
397 drawbar(m);
398+ if (m == selmon)
399+ updatesystray();
400+ }
401 }
402
403 void
404@@ -869,14 +1018,32 @@ getatomprop(Client *c, Atom prop)
405 unsigned char *p = NULL;
406 Atom da, atom = None;
407
408- if (XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, XA_ATOM,
409+ /* FIXME getatomprop should return the number of items and a pointer to
410+ * the stored data instead of this workaround */
411+ Atom req = XA_ATOM;
412+ if (prop == xatom[XembedInfo])
413+ req = xatom[XembedInfo];
414+
415+ if (XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, req,
416 &da, &di, &dl, &dl, &p) == Success && p) {
417 atom = *(Atom *)p;
418+ if (da == xatom[XembedInfo] && dl == 2)
419+ atom = ((Atom *)p)[1];
420 XFree(p);
421 }
422 return atom;
423 }
424
425+unsigned int
426+getsystraywidth()
427+{
428+ unsigned int w = 0;
429+ Client *i;
430+ if(showsystray)
431+ for(i = systray->icons; i; w += i->w + systrayspacing, i = i->next) ;
432+ return w ? w + systrayspacing : 1;
433+}
434+
435 int
436 getrootptr(int *x, int *y)
437 {
438@@ -1017,7 +1184,8 @@ killclient(const Arg *arg)
439 {
440 if (!selmon->sel)
441 return;
442- if (!sendevent(selmon->sel, wmatom[WMDelete])) {
443+
444+ if (!sendevent(selmon->sel->win, wmatom[WMDelete], NoEventMask, wmatom[WMDelete], CurrentTime, 0 , 0, 0)) {
445 XGrabServer(dpy);
446 XSetErrorHandler(xerrordummy);
447 XSetCloseDownMode(dpy, DestroyAll);
448@@ -1104,6 +1272,13 @@ maprequest(XEvent *e)
449 static XWindowAttributes wa;
450 XMapRequestEvent *ev = &e->xmaprequest;
451
452+ Client *i;
453+ if ((i = wintosystrayicon(ev->window))) {
454+ sendevent(i->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_WINDOW_ACTIVATE, 0, systray->win, XEMBED_EMBEDDED_VERSION);
455+ resizebarwin(selmon);
456+ updatesystray();
457+ }
458+
459 if (!XGetWindowAttributes(dpy, ev->window, &wa) || wa.override_redirect)
460 return;
461 if (!wintoclient(ev->window))
462@@ -1225,6 +1400,17 @@ propertynotify(XEvent *e)
463 Window trans;
464 XPropertyEvent *ev = &e->xproperty;
465
466+ if ((c = wintosystrayicon(ev->window))) {
467+ if (ev->atom == XA_WM_NORMAL_HINTS) {
468+ updatesizehints(c);
469+ updatesystrayicongeom(c, c->w, c->h);
470+ }
471+ else
472+ updatesystrayiconstate(c, ev);
473+ resizebarwin(selmon);
474+ updatesystray();
475+ }
476+
477 if ((ev->window == root) && (ev->atom == XA_WM_NAME))
478 updatestatus();
479 else if (ev->state == PropertyDelete)
480@@ -1275,6 +1461,19 @@ recttomon(int x, int y, int w, int h)
481 return r;
482 }
483
484+void
485+removesystrayicon(Client *i)
486+{
487+ Client **ii;
488+
489+ if (!showsystray || !i)
490+ return;
491+ for (ii = &systray->icons; *ii && *ii != i; ii = &(*ii)->next);
492+ if (ii)
493+ *ii = i->next;
494+ free(i);
495+}
496+
497 void
498 resize(Client *c, int x, int y, int w, int h, int interact)
499 {
500@@ -1282,6 +1481,14 @@ resize(Client *c, int x, int y, int w, int h, int interact)
501 resizeclient(c, x, y, w, h);
502 }
503
504+void
505+resizebarwin(Monitor *m) {
506+ unsigned int w = m->ww;
507+ if (showsystray && m == systraytomon(m) && !systrayonleft)
508+ w -= getsystraywidth();
509+ XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, w, bh);
510+}
511+
512 void
513 resizeclient(Client *c, int x, int y, int w, int h)
514 {
515@@ -1297,6 +1504,19 @@ resizeclient(Client *c, int x, int y, int w, int h)
516 XSync(dpy, False);
517 }
518
519+void
520+resizerequest(XEvent *e)
521+{
522+ XResizeRequestEvent *ev = &e->xresizerequest;
523+ Client *i;
524+
525+ if ((i = wintosystrayicon(ev->window))) {
526+ updatesystrayicongeom(i, ev->width, ev->height);
527+ resizebarwin(selmon);
528+ updatesystray();
529+ }
530+}
531+
532 void
533 resizemouse(const Arg *arg)
534 {
535@@ -1390,6 +1610,39 @@ run(void)
536 handler[ev.type](&ev); /* call handler */
537 }
538
539+void
540+runautostart(void)
541+{
542+ char rcpath[PATH_MAX];
543+ char* home;
544+ pid_t pid;
545+
546+ if ((home = getenv("HOME")) == NULL) {
547+ home = "/";
548+ }
549+
550+ snprintf(rcpath, sizeof(rcpath), "%s/%s", home, dwmrc);
551+ if (access(rcpath, X_OK) != 0)
552+ return;
553+
554+again:
555+ if ((pid = fork()) == -1) {
556+ fprintf(stderr, "error: unable to fork for autostart, retrying...\n");
557+ sleep(3);
558+ goto again;
559+ }
560+
561+ if (pid == 0) { // child
562+
563+ execl(rcpath, rcpath, NULL);
564+ _exit(1);
565+ }
566+
567+ if (waitpid(pid, NULL, 0) == -1) {
568+ fprintf(stderr, "warn: unable to wait for autostart, probably still alive\n");
569+ }
570+}
571+
572 void
573 scan(void)
574 {
575@@ -1443,26 +1696,37 @@ setclientstate(Client *c, long state)
576 }
577
578 int
579-sendevent(Client *c, Atom proto)
580+sendevent(Window w, Atom proto, int mask, long d0, long d1, long d2, long d3, long d4)
581 {
582 int n;
583- Atom *protocols;
584+ Atom *protocols, mt;
585 int exists = 0;
586 XEvent ev;
587
588- if (XGetWMProtocols(dpy, c->win, &protocols, &n)) {
589- while (!exists && n--)
590- exists = protocols[n] == proto;
591- XFree(protocols);
592+ if (proto == wmatom[WMTakeFocus] || proto == wmatom[WMDelete]) {
593+ mt = wmatom[WMProtocols];
594+ if (XGetWMProtocols(dpy, w, &protocols, &n)) {
595+ while (!exists && n--)
596+ exists = protocols[n] == proto;
597+ XFree(protocols);
598+ }
599 }
600+ else {
601+ exists = True;
602+ mt = proto;
603+ }
604+
605 if (exists) {
606 ev.type = ClientMessage;
607- ev.xclient.window = c->win;
608- ev.xclient.message_type = wmatom[WMProtocols];
609+ ev.xclient.window = w;
610+ ev.xclient.message_type = mt;
611 ev.xclient.format = 32;
612- ev.xclient.data.l[0] = proto;
613- ev.xclient.data.l[1] = CurrentTime;
614- XSendEvent(dpy, c->win, False, NoEventMask, &ev);
615+ ev.xclient.data.l[0] = d0;
616+ ev.xclient.data.l[1] = d1;
617+ ev.xclient.data.l[2] = d2;
618+ ev.xclient.data.l[3] = d3;
619+ ev.xclient.data.l[4] = d4;
620+ XSendEvent(dpy, w, False, mask, &ev);
621 }
622 return exists;
623 }
624@@ -1476,7 +1740,7 @@ setfocus(Client *c)
625 XA_WINDOW, 32, PropModeReplace,
626 (unsigned char *) &(c->win), 1);
627 }
628- sendevent(c, wmatom[WMTakeFocus]);
629+ sendevent(c->win, wmatom[WMTakeFocus], NoEventMask, wmatom[WMTakeFocus], CurrentTime, 0, 0, 0);
630 }
631
632 void
633@@ -1561,8 +1825,8 @@ setup(void)
634 drw = drw_create(dpy, screen, root, sw, sh);
635 if (!drw_fontset_create(drw, fonts, LENGTH(fonts)))
636 die("no fonts could be loaded.");
637- lrpad = drw->fonts->h;
638- bh = drw->fonts->h + 2;
639+ lrpad = drw->fonts->h + horizpadbar;
640+ bh = drw->fonts->h + vertpadbar;
641 updategeom();
642 /* init atoms */
643 utf8string = XInternAtom(dpy, "UTF8_STRING", False);
644@@ -1572,6 +1836,10 @@ setup(void)
645 wmatom[WMTakeFocus] = XInternAtom(dpy, "WM_TAKE_FOCUS", False);
646 netatom[NetActiveWindow] = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", False);
647 netatom[NetSupported] = XInternAtom(dpy, "_NET_SUPPORTED", False);
648+ netatom[NetSystemTray] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_S0", False);
649+ netatom[NetSystemTrayOP] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_OPCODE", False);
650+ netatom[NetSystemTrayOrientation] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_ORIENTATION", False);
651+ netatom[NetSystemTrayOrientationHorz] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_ORIENTATION_HORZ", False);
652 netatom[NetWMName] = XInternAtom(dpy, "_NET_WM_NAME", False);
653 netatom[NetWMState] = XInternAtom(dpy, "_NET_WM_STATE", False);
654 netatom[NetWMCheck] = XInternAtom(dpy, "_NET_SUPPORTING_WM_CHECK", False);
655@@ -1579,6 +1847,9 @@ setup(void)
656 netatom[NetWMWindowType] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False);
657 netatom[NetWMWindowTypeDialog] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DIALOG", False);
658 netatom[NetClientList] = XInternAtom(dpy, "_NET_CLIENT_LIST", False);
659+ xatom[Manager] = XInternAtom(dpy, "MANAGER", False);
660+ xatom[Xembed] = XInternAtom(dpy, "_XEMBED", False);
661+ xatom[XembedInfo] = XInternAtom(dpy, "_XEMBED_INFO", False);
662 /* init cursors */
663 cursor[CurNormal] = drw_cur_create(drw, XC_left_ptr);
664 cursor[CurResize] = drw_cur_create(drw, XC_sizing);
665@@ -1587,6 +1858,8 @@ setup(void)
666 scheme = ecalloc(LENGTH(colors), sizeof(Clr *));
667 for (i = 0; i < LENGTH(colors); i++)
668 scheme[i] = drw_scm_create(drw, colors[i], 3);
669+ /* init system tray */
670+ updatesystray();
671 /* init bars */
672 updatebars();
673 updatestatus();
674@@ -1649,8 +1922,6 @@ spawn(const Arg *arg)
675 {
676 struct sigaction sa;
677
678- if (arg->v == dmenucmd)
679- dmenumon[0] = '0' + selmon->num;
680 if (fork() == 0) {
681 if (dpy)
682 close(ConnectionNumber(dpy));
683@@ -1717,7 +1988,18 @@ togglebar(const Arg *arg)
684 {
685 selmon->showbar = !selmon->showbar;
686 updatebarpos(selmon);
687- XMoveResizeWindow(dpy, selmon->barwin, selmon->wx, selmon->by, selmon->ww, bh);
688+ resizebarwin(selmon);
689+ if (showsystray) {
690+ XWindowChanges wc;
691+ if (!selmon->showbar)
692+ wc.y = -bh;
693+ else if (selmon->showbar) {
694+ wc.y = 0;
695+ if (!selmon->topbar)
696+ wc.y = selmon->mh - bh;
697+ }
698+ XConfigureWindow(dpy, systray->win, CWY, &wc);
699+ }
700 arrange(selmon);
701 }
702
703@@ -1813,11 +2095,18 @@ unmapnotify(XEvent *e)
704 else
705 unmanage(c, 0);
706 }
707+ else if ((c = wintosystrayicon(ev->window))) {
708+ /* KLUDGE! sometimes icons occasionally unmap their windows, but do
709+ * _not_ destroy them. We map those windows back */
710+ XMapRaised(dpy, c->win);
711+ updatesystray();
712+ }
713 }
714
715 void
716 updatebars(void)
717 {
718+ unsigned int w;
719 Monitor *m;
720 XSetWindowAttributes wa = {
721 .override_redirect = True,
722@@ -1828,10 +2117,15 @@ updatebars(void)
723 for (m = mons; m; m = m->next) {
724 if (m->barwin)
725 continue;
726- m->barwin = XCreateWindow(dpy, root, m->wx, m->by, m->ww, bh, 0, DefaultDepth(dpy, screen),
727+ w = m->ww;
728+ if (showsystray && m == systraytomon(m))
729+ w -= getsystraywidth();
730+ m->barwin = XCreateWindow(dpy, root, m->wx, m->by, w, bh, 0, DefaultDepth(dpy, screen),
731 CopyFromParent, DefaultVisual(dpy, screen),
732 CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa);
733 XDefineCursor(dpy, m->barwin, cursor[CurNormal]->cursor);
734+ if (showsystray && m == systraytomon(m))
735+ XMapRaised(dpy, systray->win);
736 XMapRaised(dpy, m->barwin);
737 XSetClassHint(dpy, m->barwin, &ch);
738 }
739@@ -1851,7 +2145,7 @@ updatebarpos(Monitor *m)
740 }
741
742 void
743-updateclientlist()
744+updateclientlist(void)
745 {
746 Client *c;
747 Monitor *m;
748@@ -2008,6 +2302,125 @@ updatestatus(void)
749 if (!gettextprop(root, XA_WM_NAME, stext, sizeof(stext)))
750 strcpy(stext, "dwm-"VERSION);
751 drawbar(selmon);
752+ updatesystray();
753+}
754+
755+
756+void
757+updatesystrayicongeom(Client *i, int w, int h)
758+{
759+ if (i) {
760+ i->h = bh;
761+ if (w == h)
762+ i->w = bh;
763+ else if (h == bh)
764+ i->w = w;
765+ else
766+ i->w = (int) ((float)bh * ((float)w / (float)h));
767+ applysizehints(i, &(i->x), &(i->y), &(i->w), &(i->h), False);
768+ /* force icons into the systray dimensions if they don't want to */
769+ if (i->h > bh) {
770+ if (i->w == i->h)
771+ i->w = bh;
772+ else
773+ i->w = (int) ((float)bh * ((float)i->w / (float)i->h));
774+ i->h = bh;
775+ }
776+ }
777+}
778+
779+void
780+updatesystrayiconstate(Client *i, XPropertyEvent *ev)
781+{
782+ long flags;
783+ int code = 0;
784+
785+ if (!showsystray || !i || ev->atom != xatom[XembedInfo] ||
786+ !(flags = getatomprop(i, xatom[XembedInfo])))
787+ return;
788+
789+ if (flags & XEMBED_MAPPED && !i->tags) {
790+ i->tags = 1;
791+ code = XEMBED_WINDOW_ACTIVATE;
792+ XMapRaised(dpy, i->win);
793+ setclientstate(i, NormalState);
794+ }
795+ else if (!(flags & XEMBED_MAPPED) && i->tags) {
796+ i->tags = 0;
797+ code = XEMBED_WINDOW_DEACTIVATE;
798+ XUnmapWindow(dpy, i->win);
799+ setclientstate(i, WithdrawnState);
800+ }
801+ else
802+ return;
803+ sendevent(i->win, xatom[Xembed], StructureNotifyMask, CurrentTime, code, 0,
804+ systray->win, XEMBED_EMBEDDED_VERSION);
805+}
806+
807+void
808+updatesystray(void)
809+{
810+ XSetWindowAttributes wa;
811+ XWindowChanges wc;
812+ Client *i;
813+ Monitor *m = systraytomon(NULL);
814+ unsigned int x = m->mx + m->mw;
815+ unsigned int sw = TEXTW(stext) - lrpad + systrayspacing;
816+ unsigned int w = 1;
817+
818+ if (!showsystray)
819+ return;
820+ if (systrayonleft)
821+ x -= sw + lrpad / 2;
822+ if (!systray) {
823+ /* init systray */
824+ if (!(systray = (Systray *)calloc(1, sizeof(Systray))))
825+ die("fatal: could not malloc() %u bytes\n", sizeof(Systray));
826+ systray->win = XCreateSimpleWindow(dpy, root, x, m->by, w, bh, 0, 0, scheme[SchemeSel][ColBg].pixel);
827+ wa.event_mask = ButtonPressMask | ExposureMask;
828+ wa.override_redirect = True;
829+ wa.background_pixel = scheme[SchemeNorm][ColBg].pixel;
830+ XSelectInput(dpy, systray->win, SubstructureNotifyMask);
831+ XChangeProperty(dpy, systray->win, netatom[NetSystemTrayOrientation], XA_CARDINAL, 32,
832+ PropModeReplace, (unsigned char *)&netatom[NetSystemTrayOrientationHorz], 1);
833+ XChangeWindowAttributes(dpy, systray->win, CWEventMask|CWOverrideRedirect|CWBackPixel, &wa);
834+ XMapRaised(dpy, systray->win);
835+ XSetSelectionOwner(dpy, netatom[NetSystemTray], systray->win, CurrentTime);
836+ if (XGetSelectionOwner(dpy, netatom[NetSystemTray]) == systray->win) {
837+ sendevent(root, xatom[Manager], StructureNotifyMask, CurrentTime, netatom[NetSystemTray], systray->win, 0, 0);
838+ XSync(dpy, False);
839+ }
840+ else {
841+ fprintf(stderr, "dwm: unable to obtain system tray.\n");
842+ free(systray);
843+ systray = NULL;
844+ return;
845+ }
846+ }
847+ for (w = 0, i = systray->icons; i; i = i->next) {
848+ /* make sure the background color stays the same */
849+ wa.background_pixel = scheme[SchemeNorm][ColBg].pixel;
850+ XChangeWindowAttributes(dpy, i->win, CWBackPixel, &wa);
851+ XMapRaised(dpy, i->win);
852+ w += systrayspacing;
853+ i->x = w;
854+ XMoveResizeWindow(dpy, i->win, i->x, 0, i->w, i->h);
855+ w += i->w;
856+ if (i->mon != m)
857+ i->mon = m;
858+ }
859+ w = w ? w + systrayspacing : 1;
860+ x -= w;
861+ XMoveResizeWindow(dpy, systray->win, x, m->by, w, bh);
862+ wc.x = x; wc.y = m->by; wc.width = w; wc.height = bh;
863+ wc.stack_mode = Above; wc.sibling = m->barwin;
864+ XConfigureWindow(dpy, systray->win, CWX|CWY|CWWidth|CWHeight|CWSibling|CWStackMode, &wc);
865+ XMapWindow(dpy, systray->win);
866+ XMapSubwindows(dpy, systray->win);
867+ /* redraw background */
868+ XSetForeground(dpy, drw->gc, scheme[SchemeNorm][ColBg].pixel);
869+ XFillRectangle(dpy, systray->win, drw->gc, 0, 0, w, bh);
870+ XSync(dpy, False);
871 }
872
873 void
874@@ -2075,6 +2488,16 @@ wintoclient(Window w)
875 return NULL;
876 }
877
878+Client *
879+wintosystrayicon(Window w) {
880+ Client *i = NULL;
881+
882+ if (!showsystray || !w)
883+ return i;
884+ for (i = systray->icons; i && i->win != w; i = i->next) ;
885+ return i;
886+}
887+
888 Monitor *
889 wintomon(Window w)
890 {
891@@ -2128,6 +2551,22 @@ xerrorstart(Display *dpy, XErrorEvent *ee)
892 return -1;
893 }
894
895+Monitor *
896+systraytomon(Monitor *m) {
897+ Monitor *t;
898+ int i, n;
899+ if(!systraypinning) {
900+ if(!m)
901+ return selmon;
902+ return m == selmon ? m : NULL;
903+ }
904+ for(n = 1, t = mons; t && t->next; n++, t = t->next) ;
905+ for(i = 1, t = mons; t && t->next && i < systraypinning; i++, t = t->next) ;
906+ if(systraypinningfailfirst && n < systraypinning)
907+ return mons;
908+ return t;
909+}
910+
911 void
912 zoom(const Arg *arg)
913 {
914@@ -2158,6 +2597,7 @@ main(int argc, char *argv[])
915 die("pledge");
916 #endif /* __OpenBSD__ */
917 scan();
918+ runautostart();
919 run();
920 cleanup();
921 XCloseDisplay(dpy);