sites

public wiki contents of suckless.org
git clone git://git.suckless.org/sites
Log | Files | Refs

dwm-6.1-systray.diff (22685B)


      1 diff -up a/config.def.h b/config.def.h
      2 --- a/config.def.h	2015-11-08 23:39:37.000000000 +0100
      3 +++ b/config.def.h	2019-02-08 16:20:00.296460181 +0100
      4 @@ -13,6 +13,10 @@ static const char selbgcolor[]      = "#
      5  static const char selfgcolor[]      = "#eeeeee";
      6  static const unsigned int borderpx  = 1;        /* border pixel of windows */
      7  static const unsigned int snap      = 32;       /* snap pixel */
      8 +static const unsigned int systraypinning = 0;   /* 0: sloppy systray follows selected monitor, >0: pin systray to monitor X */
      9 +static const unsigned int systrayspacing = 2;   /* systray spacing */
     10 +static const int systraypinningfailfirst = 1;   /* 1: if pinning fails, display systray on the first monitor, 0: display systray on the last monitor*/
     11 +static const int showsystray        = 1;        /* 0 means no systray */
     12  static const int showbar            = 1;        /* 0 means no bar */
     13  static const int topbar             = 1;        /* 0 means bottom bar */
     14  
     15 diff -up a/dwm.c b/dwm.c
     16 --- a/dwm.c	2015-11-08 23:39:37.000000000 +0100
     17 +++ b/dwm.c	2019-02-08 16:20:00.296460181 +0100
     18 @@ -57,12 +57,30 @@
     19  #define TAGMASK                 ((1 << LENGTH(tags)) - 1)
     20  #define TEXTW(X)                (drw_text(drw, 0, 0, 0, 0, (X), 0) + drw->fonts[0]->h)
     21  
     22 +#define SYSTEM_TRAY_REQUEST_DOCK    0
     23 +#define _NET_SYSTEM_TRAY_ORIENTATION_HORZ 0
     24 +
     25 +/* XEMBED messages */
     26 +#define XEMBED_EMBEDDED_NOTIFY      0
     27 +#define XEMBED_WINDOW_ACTIVATE      1
     28 +#define XEMBED_FOCUS_IN             4
     29 +#define XEMBED_MODALITY_ON         10
     30 +
     31 +#define XEMBED_MAPPED              (1 << 0)
     32 +#define XEMBED_WINDOW_ACTIVATE      1
     33 +#define XEMBED_WINDOW_DEACTIVATE    2
     34 +
     35 +#define VERSION_MAJOR               0
     36 +#define VERSION_MINOR               0
     37 +#define XEMBED_EMBEDDED_VERSION (VERSION_MAJOR << 16) | VERSION_MINOR
     38 +
     39  /* enums */
     40  enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */
     41  enum { SchemeNorm, SchemeSel, SchemeLast }; /* color schemes */
     42 -enum { NetSupported, NetWMName, NetWMState,
     43 -       NetWMFullscreen, NetActiveWindow, NetWMWindowType,
     44 -       NetWMWindowTypeDialog, NetClientList, NetLast }; /* EWMH atoms */
     45 +enum { NetSupported, NetSystemTray, NetSystemTrayOP, NetSystemTrayOrientation,
     46 +	   NetWMName, NetWMState, NetWMFullscreen, NetActiveWindow, NetWMWindowType,
     47 +	   NetWMWindowTypeDialog, NetClientList, NetLast }; /* EWMH atoms */
     48 +enum { Manager, Xembed, XembedInfo, XLast }; /* Xembed atoms */
     49  enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */
     50  enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle,
     51         ClkClientWin, ClkRootWin, ClkLast }; /* clicks */
     52 @@ -141,6 +159,12 @@ typedef struct {
     53  	int monitor;
     54  } Rule;
     55  
     56 +typedef struct Systray   Systray;
     57 +struct Systray {
     58 +	Window win;
     59 +	Client *icons;
     60 +};
     61 +
     62  /* function declarations */
     63  static void applyrules(Client *c);
     64  static int applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact);
     65 @@ -170,8 +194,10 @@ static void focus(Client *c);
     66  static void focusin(XEvent *e);
     67  static void focusmon(const Arg *arg);
     68  static void focusstack(const Arg *arg);
     69 +static Atom getatomprop(Client *c, Atom prop);
     70  static int getrootptr(int *x, int *y);
     71  static long getstate(Window w);
     72 +static unsigned int getsystraywidth();
     73  static int gettextprop(Window w, Atom atom, char *text, unsigned int size);
     74  static void grabbuttons(Client *c, int focused);
     75  static void grabkeys(void);
     76 @@ -189,13 +215,16 @@ static void pop(Client *);
     77  static void propertynotify(XEvent *e);
     78  static void quit(const Arg *arg);
     79  static Monitor *recttomon(int x, int y, int w, int h);
     80 +static void removesystrayicon(Client *i);
     81  static void resize(Client *c, int x, int y, int w, int h, int interact);
     82 +static void resizebarwin(Monitor *m);
     83  static void resizeclient(Client *c, int x, int y, int w, int h);
     84  static void resizemouse(const Arg *arg);
     85 +static void resizerequest(XEvent *e);
     86  static void restack(Monitor *m);
     87  static void run(void);
     88  static void scan(void);
     89 -static int sendevent(Client *c, Atom proto);
     90 +static int sendevent(Window w, Atom proto, int m, long d0, long d1, long d2, long d3, long d4);
     91  static void sendmon(Client *c, Monitor *m);
     92  static void setclientstate(Client *c, long state);
     93  static void setfocus(Client *c);
     94 @@ -206,6 +235,7 @@ static void setup(void);
     95  static void showhide(Client *c);
     96  static void sigchld(int unused);
     97  static void spawn(const Arg *arg);
     98 +static Monitor *systraytomon(Monitor *m);
     99  static void tag(const Arg *arg);
    100  static void tagmon(const Arg *arg);
    101  static void tile(Monitor *);
    102 @@ -223,18 +253,24 @@ static void updateclientlist(void);
    103  static void updatenumlockmask(void);
    104  static void updatesizehints(Client *c);
    105  static void updatestatus(void);
    106 +static void updatesystray(void);
    107 +static void updatesystrayicongeom(Client *i, int w, int h);
    108 +static void updatesystrayiconstate(Client *i, XPropertyEvent *ev);
    109  static void updatewindowtype(Client *c);
    110  static void updatetitle(Client *c);
    111  static void updatewmhints(Client *c);
    112  static void view(const Arg *arg);
    113  static Client *wintoclient(Window w);
    114  static Monitor *wintomon(Window w);
    115 +static Client *wintosystrayicon(Window w);
    116  static int xerror(Display *dpy, XErrorEvent *ee);
    117  static int xerrordummy(Display *dpy, XErrorEvent *ee);
    118  static int xerrorstart(Display *dpy, XErrorEvent *ee);
    119  static void zoom(const Arg *arg);
    120  
    121  /* variables */
    122 +static Systray *systray = NULL;
    123 +static unsigned long systrayorientation = _NET_SYSTEM_TRAY_ORIENTATION_HORZ;
    124  static const char broken[] = "broken";
    125  static char stext[256];
    126  static int screen;
    127 @@ -256,9 +292,10 @@ static void (*handler[LASTEvent]) (XEven
    128  	[MapRequest] = maprequest,
    129  	[MotionNotify] = motionnotify,
    130  	[PropertyNotify] = propertynotify,
    131 +	[ResizeRequest] = resizerequest,
    132  	[UnmapNotify] = unmapnotify
    133  };
    134 -static Atom wmatom[WMLast], netatom[NetLast];
    135 +static Atom wmatom[WMLast], netatom[NetLast], xatom[XLast];
    136  static int running = 1;
    137  static Cur *cursor[CurLast];
    138  static ClrScheme scheme[SchemeLast];
    139 @@ -438,7 +475,7 @@ buttonpress(XEvent *e)
    140  			arg.ui = 1 << i;
    141  		} else if (ev->x < x + blw)
    142  			click = ClkLtSymbol;
    143 -		else if (ev->x > selmon->ww - TEXTW(stext))
    144 +		else if (ev->x > selmon->ww - TEXTW(stext) - getsystraywidth())
    145  			click = ClkStatusText;
    146  		else
    147  			click = ClkWinTitle;
    148 @@ -479,6 +516,11 @@ cleanup(void)
    149  	XUngrabKey(dpy, AnyKey, AnyModifier, root);
    150  	while (mons)
    151  		cleanupmon(mons);
    152 +	if (showsystray) {
    153 +		XUnmapWindow(dpy, systray->win);
    154 +		XDestroyWindow(dpy, systray->win);
    155 +		free(systray);
    156 +	}
    157  	for (i = 0; i < CurLast; i++)
    158  		drw_cur_free(drw, cursor[i]);
    159  	for (i = 0; i < SchemeLast; i++) {
    160 @@ -524,9 +566,50 @@ clearurgent(Client *c)
    161  void
    162  clientmessage(XEvent *e)
    163  {
    164 +	XWindowAttributes wa;
    165 +	XSetWindowAttributes swa;
    166  	XClientMessageEvent *cme = &e->xclient;
    167  	Client *c = wintoclient(cme->window);
    168  
    169 +	if (showsystray && cme->window == systray->win && cme->message_type == netatom[NetSystemTrayOP]) {
    170 +		/* add systray icons */
    171 +		if (cme->data.l[1] == SYSTEM_TRAY_REQUEST_DOCK) {
    172 +			if (!(c = (Client *)calloc(1, sizeof(Client))))
    173 +				die("fatal: could not malloc() %u bytes\n", sizeof(Client));
    174 +			c->win = cme->data.l[2];
    175 +			c->mon = selmon;
    176 +			c->next = systray->icons;
    177 +			systray->icons = c;
    178 +			XGetWindowAttributes(dpy, c->win, &wa);
    179 +			c->x = c->oldx = c->y = c->oldy = 0;
    180 +			c->w = c->oldw = wa.width;
    181 +			c->h = c->oldh = wa.height;
    182 +			c->oldbw = wa.border_width;
    183 +			c->bw = 0;
    184 +			c->isfloating = True;
    185 +			/* reuse tags field as mapped status */
    186 +			c->tags = 1;
    187 +			updatesizehints(c);
    188 +			updatesystrayicongeom(c, wa.width, wa.height);
    189 +			XAddToSaveSet(dpy, c->win);
    190 +			XSelectInput(dpy, c->win, StructureNotifyMask | PropertyChangeMask | ResizeRedirectMask);
    191 +			XReparentWindow(dpy, c->win, systray->win, 0, 0);
    192 +			/* use parents background color */
    193 +			swa.background_pixel  = scheme[SchemeNorm].bg->pix;
    194 +			XChangeWindowAttributes(dpy, c->win, CWBackPixel, &swa);
    195 +			sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_EMBEDDED_NOTIFY, 0 , systray->win, XEMBED_EMBEDDED_VERSION);
    196 +			/* FIXME not sure if I have to send these events, too */
    197 +			sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_FOCUS_IN, 0 , systray->win, XEMBED_EMBEDDED_VERSION);
    198 +			sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_WINDOW_ACTIVATE, 0 , systray->win, XEMBED_EMBEDDED_VERSION);
    199 +			sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_MODALITY_ON, 0 , systray->win, XEMBED_EMBEDDED_VERSION);
    200 +			XSync(dpy, False);
    201 +			resizebarwin(selmon);
    202 +			updatesystray();
    203 +			setclientstate(c, NormalState);
    204 +		}
    205 +		return;
    206 +	}
    207 +
    208  	if (!c)
    209  		return;
    210  	if (cme->message_type == netatom[NetWMState]) {
    211 @@ -576,8 +659,7 @@ configurenotify(XEvent *e)
    212  		if (updategeom() || dirty) {
    213  			drw_resize(drw, sw, bh);
    214  			updatebars();
    215 -			for (m = mons; m; m = m->next)
    216 -				XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, m->ww, bh);
    217 +			resizebarwin(m);
    218  			focus(NULL);
    219  			arrange(NULL);
    220  		}
    221 @@ -661,6 +743,11 @@ destroynotify(XEvent *e)
    222  
    223  	if ((c = wintoclient(ev->window)))
    224  		unmanage(c, 1);
    225 +	else if ((c = wintosystrayicon(ev->window))) {
    226 +		removesystrayicon(c);
    227 +		resizebarwin(selmon);
    228 +		updatesystray();
    229 +	}
    230  }
    231  
    232  void
    233 @@ -710,6 +797,7 @@ drawbar(Monitor *m)
    234  
    235  	dx = (drw->fonts[0]->ascent + drw->fonts[0]->descent + 2) / 4;
    236  
    237 +	resizebarwin(m);
    238  	for (c = m->clients; c; c = c->next) {
    239  		occ |= c->tags;
    240  		if (c->isurgent)
    241 @@ -732,6 +820,9 @@ drawbar(Monitor *m)
    242  	if (m == selmon) { /* status is only drawn on selected monitor */
    243  		w = TEXTW(stext);
    244  		x = m->ww - w;
    245 +		if (showsystray && m == systraytomon(m)) {
    246 +			x -= getsystraywidth();
    247 +		}
    248  		if (x < xx) {
    249  			x = xx;
    250  			w = m->ww - xx;
    251 @@ -760,6 +851,7 @@ drawbars(void)
    252  
    253  	for (m = mons; m; m = m->next)
    254  		drawbar(m);
    255 +	updatesystray();
    256  }
    257  
    258  void
    259 @@ -787,8 +879,11 @@ expose(XEvent *e)
    260  	Monitor *m;
    261  	XExposeEvent *ev = &e->xexpose;
    262  
    263 -	if (ev->count == 0 && (m = wintomon(ev->window)))
    264 +	if (ev->count == 0 && (m = wintomon(ev->window))) {
    265  		drawbar(m);
    266 +		if (m == selmon)
    267 +			updatesystray();
    268 +	}
    269  }
    270  
    271  void
    272 @@ -875,10 +970,17 @@ getatomprop(Client *c, Atom prop)
    273  	unsigned long dl;
    274  	unsigned char *p = NULL;
    275  	Atom da, atom = None;
    276 +	/* FIXME getatomprop should return the number of items and a pointer to
    277 +	 * the stored data instead of this workaround */
    278 +	Atom req = XA_ATOM;
    279 +	if (prop == xatom[XembedInfo])
    280 +		req = xatom[XembedInfo];
    281  
    282 -	if (XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, XA_ATOM,
    283 +	if (XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, req,
    284  	                      &da, &di, &dl, &dl, &p) == Success && p) {
    285  		atom = *(Atom *)p;
    286 +		if (da == xatom[XembedInfo] && dl == 2)
    287 +			atom = ((Atom *)p)[1];
    288  		XFree(p);
    289  	}
    290  	return atom;
    291 @@ -912,6 +1014,15 @@ getstate(Window w)
    292  	return result;
    293  }
    294  
    295 +unsigned int
    296 +getsystraywidth() {
    297 +	unsigned int w = 0;
    298 +	Client *i;
    299 +	if (showsystray)
    300 +		for (i = systray->icons; i; w += i->w + systrayspacing, i = i->next);
    301 +	return w ? w + systrayspacing : 1;
    302 +}
    303 +
    304  int
    305  gettextprop(Window w, Atom atom, char *text, unsigned int size)
    306  {
    307 @@ -1018,7 +1129,7 @@ killclient(const Arg *arg)
    308  {
    309  	if (!selmon->sel)
    310  		return;
    311 -	if (!sendevent(selmon->sel, wmatom[WMDelete])) {
    312 +	if (!sendevent(selmon->sel->win, wmatom[WMDelete], NoEventMask, wmatom[WMDelete], CurrentTime, 0 , 0, 0)) {
    313  		XGrabServer(dpy);
    314  		XSetErrorHandler(xerrordummy);
    315  		XSetCloseDownMode(dpy, DestroyAll);
    316 @@ -1105,6 +1216,12 @@ maprequest(XEvent *e)
    317  {
    318  	static XWindowAttributes wa;
    319  	XMapRequestEvent *ev = &e->xmaprequest;
    320 +	Client *i;
    321 +	if ((i = wintosystrayicon(ev->window))) {
    322 +		sendevent(i->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_WINDOW_ACTIVATE, 0, systray->win, XEMBED_EMBEDDED_VERSION);
    323 +		resizebarwin(selmon);
    324 +		updatesystray();
    325 +	}
    326  
    327  	if (!XGetWindowAttributes(dpy, ev->window, &wa))
    328  		return;
    329 @@ -1232,6 +1349,16 @@ propertynotify(XEvent *e)
    330  	Window trans;
    331  	XPropertyEvent *ev = &e->xproperty;
    332  
    333 +	if ((c = wintosystrayicon(ev->window))) {
    334 +		if (ev->atom == XA_WM_NORMAL_HINTS) {
    335 +			updatesizehints(c);
    336 +			updatesystrayicongeom(c, c->w, c->h);
    337 +		}
    338 +		else
    339 +			updatesystrayiconstate(c, ev);
    340 +		resizebarwin(selmon);
    341 +		updatesystray();
    342 +	}
    343  	if ((ev->window == root) && (ev->atom == XA_WM_NAME))
    344  		updatestatus();
    345  	else if (ev->state == PropertyDelete)
    346 @@ -1283,6 +1410,19 @@ recttomon(int x, int y, int w, int h)
    347  }
    348  
    349  void
    350 +removesystrayicon(Client *i) {
    351 +	Client **ii;
    352 +
    353 +	if (!showsystray || !i)
    354 +		return;
    355 +	for (ii = &systray->icons; *ii && *ii != i; ii = &(*ii)->next);
    356 +	if (ii)
    357 +		*ii = i->next;
    358 +	free(i);
    359 +}
    360 +
    361 +
    362 +void
    363  resize(Client *c, int x, int y, int w, int h, int interact)
    364  {
    365  	if (applysizehints(c, &x, &y, &w, &h, interact))
    366 @@ -1290,6 +1430,14 @@ resize(Client *c, int x, int y, int w, i
    367  }
    368  
    369  void
    370 +resizebarwin(Monitor *m) {
    371 +	unsigned int w = m->ww;
    372 +	if (showsystray && m == systraytomon(m))
    373 +		w -= getsystraywidth();
    374 +	XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, w, bh);
    375 +}
    376 +
    377 +void
    378  resizeclient(Client *c, int x, int y, int w, int h)
    379  {
    380  	XWindowChanges wc;
    381 @@ -1362,6 +1510,18 @@ resizemouse(const Arg *arg)
    382  }
    383  
    384  void
    385 +resizerequest(XEvent *e) {
    386 +	XResizeRequestEvent *ev = &e->xresizerequest;
    387 +	Client *i;
    388 +
    389 +	if ((i = wintosystrayicon(ev->window))) {
    390 +		updatesystrayicongeom(i, ev->width, ev->height);
    391 +		resizebarwin(selmon);
    392 +		updatesystray();
    393 +	}
    394 +}
    395 +
    396 +void
    397  restack(Monitor *m)
    398  {
    399  	Client *c;
    400 @@ -1450,26 +1610,36 @@ setclientstate(Client *c, long state)
    401  }
    402  
    403  int
    404 -sendevent(Client *c, Atom proto)
    405 +sendevent(Window w, Atom proto, int mask, long d0, long d1, long d2, long d3, long d4)
    406  {
    407  	int n;
    408 -	Atom *protocols;
    409 +	Atom *protocols, mt;
    410  	int exists = 0;
    411  	XEvent ev;
    412  
    413 -	if (XGetWMProtocols(dpy, c->win, &protocols, &n)) {
    414 -		while (!exists && n--)
    415 -			exists = protocols[n] == proto;
    416 -		XFree(protocols);
    417 +	if (proto == wmatom[WMTakeFocus] || proto == wmatom[WMDelete]) {
    418 +		mt = wmatom[WMProtocols];
    419 +		if (XGetWMProtocols(dpy, w, &protocols, &n)) {
    420 +			while(!exists && n--)
    421 +				exists = protocols[n] == proto;
    422 +			XFree(protocols);
    423 +		}
    424 +	}
    425 +	else {
    426 +		exists = True;
    427 +		mt = proto;
    428  	}
    429  	if (exists) {
    430  		ev.type = ClientMessage;
    431 -		ev.xclient.window = c->win;
    432 -		ev.xclient.message_type = wmatom[WMProtocols];
    433 +		ev.xclient.window = w;
    434 +		ev.xclient.message_type = mt;
    435  		ev.xclient.format = 32;
    436 -		ev.xclient.data.l[0] = proto;
    437 -		ev.xclient.data.l[1] = CurrentTime;
    438 -		XSendEvent(dpy, c->win, False, NoEventMask, &ev);
    439 +		ev.xclient.data.l[0] = d0;
    440 +		ev.xclient.data.l[1] = d1;
    441 +		ev.xclient.data.l[2] = d2;
    442 +		ev.xclient.data.l[3] = d3;
    443 +		ev.xclient.data.l[4] = d4;
    444 +		XSendEvent(dpy, w, False, mask, &ev);
    445  	}
    446  	return exists;
    447  }
    448 @@ -1483,7 +1653,7 @@ setfocus(Client *c)
    449  		                XA_WINDOW, 32, PropModeReplace,
    450  		                (unsigned char *) &(c->win), 1);
    451  	}
    452 -	sendevent(c, wmatom[WMTakeFocus]);
    453 +	sendevent(c->win, wmatom[WMTakeFocus], NoEventMask, wmatom[WMTakeFocus], CurrentTime, 0, 0, 0);
    454  }
    455  
    456  void
    457 @@ -1569,12 +1739,18 @@ setup(void)
    458  	wmatom[WMTakeFocus] = XInternAtom(dpy, "WM_TAKE_FOCUS", False);
    459  	netatom[NetActiveWindow] = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", False);
    460  	netatom[NetSupported] = XInternAtom(dpy, "_NET_SUPPORTED", False);
    461 +	netatom[NetSystemTray] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_S0", False);
    462 +	netatom[NetSystemTrayOP] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_OPCODE", False);
    463 +	netatom[NetSystemTrayOrientation] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_ORIENTATION", False);
    464  	netatom[NetWMName] = XInternAtom(dpy, "_NET_WM_NAME", False);
    465  	netatom[NetWMState] = XInternAtom(dpy, "_NET_WM_STATE", False);
    466  	netatom[NetWMFullscreen] = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False);
    467  	netatom[NetWMWindowType] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False);
    468  	netatom[NetWMWindowTypeDialog] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DIALOG", False);
    469  	netatom[NetClientList] = XInternAtom(dpy, "_NET_CLIENT_LIST", False);
    470 +	xatom[Manager] = XInternAtom(dpy, "MANAGER", False);
    471 +	xatom[Xembed] = XInternAtom(dpy, "_XEMBED", False);
    472 +	xatom[XembedInfo] = XInternAtom(dpy, "_XEMBED_INFO", False);
    473  	/* init cursors */
    474  	cursor[CurNormal] = drw_cur_create(drw, XC_left_ptr);
    475  	cursor[CurResize] = drw_cur_create(drw, XC_sizing);
    476 @@ -1586,6 +1762,8 @@ setup(void)
    477  	scheme[SchemeSel].border = drw_clr_create(drw, selbordercolor);
    478  	scheme[SchemeSel].bg = drw_clr_create(drw, selbgcolor);
    479  	scheme[SchemeSel].fg = drw_clr_create(drw, selfgcolor);
    480 +	/* init system tray */
    481 +	updatesystray();
    482  	/* init bars */
    483  	updatebars();
    484  	updatestatus();
    485 @@ -1645,6 +1823,22 @@ spawn(const Arg *arg)
    486  	}
    487  }
    488  
    489 +Monitor *
    490 +systraytomon(Monitor *m) {
    491 +	Monitor *t;
    492 +	int i, n;
    493 +	if (!systraypinning) {
    494 +		if (!m)
    495 +			return selmon;
    496 +		return m == selmon ? m : NULL;
    497 +	}
    498 +	for (n = 1, t = mons; t && t->next; n++, t = t->next);
    499 +	for (i = 1, t = mons; t && t->next && i < systraypinning; i++, t = t->next);
    500 +	if (systraypinningfailfirst && n < systraypinning)
    501 +		return mons;
    502 +	return t;
    503 +}
    504 +
    505  void
    506  tag(const Arg *arg)
    507  {
    508 @@ -1694,7 +1888,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 @@ -1790,11 +1995,17 @@ unmapnotify(XEvent *e)
    529  		else
    530  			unmanage(c, 0);
    531  	}
    532 +	else if ((c = wintosystrayicon(ev->window))) {
    533 +		removesystrayicon(c);
    534 +		resizebarwin(selmon);
    535 +		updatesystray();
    536 +	}
    537  }
    538  
    539  void
    540  updatebars(void)
    541  {
    542 +	unsigned int w;
    543  	Monitor *m;
    544  	XSetWindowAttributes wa = {
    545  		.override_redirect = True,
    546 @@ -1804,10 +2015,15 @@ updatebars(void)
    547  	for (m = mons; m; m = m->next) {
    548  		if (m->barwin)
    549  			continue;
    550 -		m->barwin = XCreateWindow(dpy, root, m->wx, m->by, m->ww, bh, 0, DefaultDepth(dpy, screen),
    551 +		w = m->ww;
    552 +		if (showsystray && m == systraytomon(m))
    553 +			w -= getsystraywidth();
    554 +		m->barwin = XCreateWindow(dpy, root, m->wx, m->by, w, bh, 0, DefaultDepth(dpy, screen),
    555  		                          CopyFromParent, DefaultVisual(dpy, screen),
    556  		                          CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa);
    557  		XDefineCursor(dpy, m->barwin, cursor[CurNormal]->cursor);
    558 +		if (showsystray && m == systraytomon(m))
    559 +			XMapRaised(dpy, systray->win);
    560  		XMapRaised(dpy, m->barwin);
    561  	}
    562  }
    563 @@ -1998,6 +2214,117 @@ updatestatus(void)
    564  }
    565  
    566  void
    567 +updatesystrayicongeom(Client *i, int w, int h) {
    568 +	if (i) {
    569 +		i->h = bh;
    570 +		if (w == h)
    571 +			i->w = bh;
    572 +		else if (h == bh)
    573 +			i->w = w;
    574 +		else
    575 +			i->w = (int) ((float)bh * ((float)w / (float)h));
    576 +		applysizehints(i, &(i->x), &(i->y), &(i->w), &(i->h), False);
    577 +		/* force icons into the systray dimenons if they don't want to */
    578 +		if (i->h > bh) {
    579 +			if (i->w == i->h)
    580 +				i->w = bh;
    581 +			else
    582 +				i->w = (int) ((float)bh * ((float)i->w / (float)i->h));
    583 +			i->h = bh;
    584 +		}
    585 +	}
    586 +}
    587 +
    588 +void
    589 +updatesystrayiconstate(Client *i, XPropertyEvent *ev) {
    590 +	long flags;
    591 +	int code = 0;
    592 +
    593 +	if (!showsystray || !i || ev->atom != xatom[XembedInfo] ||
    594 +			!(flags = getatomprop(i, xatom[XembedInfo])))
    595 +		return;
    596 +
    597 +	if (flags & XEMBED_MAPPED && !i->tags) {
    598 +		i->tags = 1;
    599 +		code = XEMBED_WINDOW_ACTIVATE;
    600 +		XMapRaised(dpy, i->win);
    601 +		setclientstate(i, NormalState);
    602 +	}
    603 +	else if (!(flags & XEMBED_MAPPED) && i->tags) {
    604 +		i->tags = 0;
    605 +		code = XEMBED_WINDOW_DEACTIVATE;
    606 +		XUnmapWindow(dpy, i->win);
    607 +		setclientstate(i, WithdrawnState);
    608 +	}
    609 +	else
    610 +		return;
    611 +	sendevent(i->win, xatom[Xembed], StructureNotifyMask, CurrentTime, code, 0,
    612 +			systray->win, XEMBED_EMBEDDED_VERSION);
    613 +}
    614 +
    615 +void
    616 +updatesystray(void) {
    617 +	XSetWindowAttributes wa;
    618 +	XWindowChanges wc;
    619 +	Client *i;
    620 +	Monitor *m = systraytomon(NULL);
    621 +	unsigned int x = m->mx + m->mw;
    622 +	unsigned int w = 1;
    623 +
    624 +	if (!showsystray)
    625 +		return;
    626 +	if (!systray) {
    627 +		/* init systray */
    628 +		if (!(systray = (Systray *)calloc(1, sizeof(Systray))))
    629 +			die("fatal: could not malloc() %u bytes\n", sizeof(Systray));
    630 +		systray->win = XCreateSimpleWindow(dpy, root, x, m->by, w, bh, 0, 0, scheme[SchemeSel].bg->pix);
    631 +		wa.event_mask        = ButtonPressMask | ExposureMask;
    632 +		wa.override_redirect = True;
    633 +		wa.background_pixel  = scheme[SchemeNorm].bg->pix;
    634 +		XSelectInput(dpy, systray->win, SubstructureNotifyMask);
    635 +		XChangeProperty(dpy, systray->win, netatom[NetSystemTrayOrientation], XA_CARDINAL, 32,
    636 +				PropModeReplace, (unsigned char *)&systrayorientation, 1);
    637 +		XChangeWindowAttributes(dpy, systray->win, CWEventMask|CWOverrideRedirect|CWBackPixel, &wa);
    638 +		XMapRaised(dpy, systray->win);
    639 +		XSetSelectionOwner(dpy, netatom[NetSystemTray], systray->win, CurrentTime);
    640 +		if (XGetSelectionOwner(dpy, netatom[NetSystemTray]) == systray->win) {
    641 +			sendevent(root, xatom[Manager], StructureNotifyMask, CurrentTime, netatom[NetSystemTray], systray->win, 0, 0);
    642 +			XSync(dpy, False);
    643 +		}
    644 +		else {
    645 +			fprintf(stderr, "dwm: unable to obtain system tray.\n");
    646 +			free(systray);
    647 +			systray = NULL;
    648 +			return;
    649 +		}
    650 +	}
    651 +	for (w = 0, i = systray->icons; i; i = i->next) {
    652 +		/* make sure the background color stays the same */
    653 +		wa.background_pixel  = scheme[SchemeNorm].bg->pix;
    654 +		XChangeWindowAttributes(dpy, i->win, CWBackPixel, &wa);
    655 +		XMapRaised(dpy, i->win);
    656 +		w += systrayspacing;
    657 +		i->x = w;
    658 +		XMoveResizeWindow(dpy, i->win, i->x, 0, i->w, i->h);
    659 +		w += i->w;
    660 +		if (i->mon != m)
    661 +			i->mon = m;
    662 +	}
    663 +	w = w ? w + systrayspacing : 1;
    664 +	x -= w;
    665 +	XMoveResizeWindow(dpy, systray->win, x, m->by, w, bh);
    666 +	wc.x = x; wc.y = m->by; wc.width = w; wc.height = bh;
    667 +	wc.stack_mode = Above; wc.sibling = m->barwin;
    668 +	XConfigureWindow(dpy, systray->win, CWX|CWY|CWWidth|CWHeight|CWSibling|CWStackMode, &wc);
    669 +	XMapWindow(dpy, systray->win);
    670 +	XMapSubwindows(dpy, systray->win);
    671 +	/* redraw background */
    672 +	XSetForeground(dpy, drw->gc, scheme[SchemeNorm].bg->pix);
    673 +	XFillRectangle(dpy, systray->win, drw->gc, 0, 0, w, bh);
    674 +	XSync(dpy, False);
    675 +}
    676 +
    677 +void
    678  updatewindowtype(Client *c)
    679  {
    680  	Atom state = getatomprop(c, netatom[NetWMState]);
    681 @@ -2070,6 +2397,16 @@ wintomon(Window w)
    682  	return selmon;
    683  }
    684  
    685 +Client *
    686 +wintosystrayicon(Window w) {
    687 +	Client *i = NULL;
    688 +
    689 +	if (!showsystray || !w)
    690 +		return i;
    691 +	for (i = systray->icons; i && i->win != w; i = i->next);
    692 +	return i;
    693 +}
    694 +
    695  /* There's no way to check accesses to destroyed windows, thus those cases are
    696   * ignored (especially on UnmapNotify's).  Other types of errors call Xlibs
    697   * default error handler, which may call exit.  */