dotfiles

My beautiful configs and dotfiles managed by Nix' home-manager
Log | Files | Refs | README | LICENSE

dwm-statusbar.diff (27546B)


      1 diff --git a/dwm.1 b/dwm.1
      2 index 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.
     55 diff --git a/dwm.c b/dwm.c
     56 index 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);