[download]

local/src/dwm/dwm.c

   1 /* See LICENSE file for copyright and license details.
   2  *
   3  * dynamic window manager is designed like any other X client as well. It is
   4  * driven through handling X events. In contrast to other X clients, a window
   5  * manager selects for SubstructureRedirectMask on the root window, to receive
   6  * events about window (dis-)appearance. Only one X connection at a time is
   7  * allowed to select for this event mask.
   8  *
   9  * The event handlers of dwm are organized in an array which is accessed
  10  * whenever a new event has been fetched. This allows event dispatching
  11  * in O(1) time.
  12  *
  13  * Each child of the root window is called a client, except windows which have
  14  * set the override_redirect flag. Clients are organized in a linked client
  15  * list on each monitor, the focus history is remembered through a stack list
  16  * on each monitor. Each client contains a bit array to indicate the tags of a
  17  * client.
  18  *
  19  * Keys and tagging rules are organized as arrays and defined in config.h.
  20  *
  21  * To understand everything else, start reading main().
  22  */
  23 #include <errno.h>
  24 #include <locale.h>
  25 #include <signal.h>
  26 #include <stdarg.h>
  27 #include <stdio.h>
  28 #include <stdlib.h>
  29 #include <string.h>
  30 #include <unistd.h>
  31 #include <sys/types.h>
  32 #include <sys/wait.h>
  33 #include <X11/cursorfont.h>
  34 #include <X11/keysym.h>
  35 #include <X11/Xatom.h>
  36 #include <X11/Xlib.h>
  37 #include <X11/Xproto.h>
  38 #include <X11/Xutil.h>
  39 #ifdef XINERAMA
  40 #include <X11/extensions/Xinerama.h>
  41 #endif /* XINERAMA */
  42 #include <X11/Xft/Xft.h>
  43 
  44 #include "drw.h"
  45 #include "util.h"
  46 
  47 /* macros */
  48 #define BUTTONMASK              (ButtonPressMask|ButtonReleaseMask)
  49 #define CLEANMASK(mask)         (mask & ~(numlockmask|LockMask) & (ShiftMask|ControlMask|Mod1Mask|Mod2Mask|Mod3Mask|Mod4Mask|Mod5Mask))
  50 #define INTERSECT(x,y,w,h,m)    (MAX(0, MIN((x)+(w),(m)->wx+(m)->ww) - MAX((x),(m)->wx)) \
  51                                * MAX(0, MIN((y)+(h),(m)->wy+(m)->wh) - MAX((y),(m)->wy)))
  52 #define ISVISIBLE(C)            ((C->tags & C->mon->tagset[C->mon->seltags]))
  53 #define LENGTH(X)               (sizeof X / sizeof X[0])
  54 #define MOUSEMASK               (BUTTONMASK|PointerMotionMask)
  55 #define WIDTH(X)                ((X)->w + 2 * (X)->bw)
  56 #define HEIGHT(X)               ((X)->h + 2 * (X)->bw)
  57 #define TAGMASK                 ((1 << LENGTH(tags)) - 1)
  58 #define TEXTW(X)                (drw_fontset_getwidth(drw, (X)) + lrpad)
  59 
  60 /* enums */
  61 enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */
  62 enum { SchemeNorm, SchemeSel }; /* color schemes */
  63 enum { NetSupported, NetWMName, NetWMState, NetWMCheck,
  64 #if PATCH_SYSTRAY /* {{{ */
  65        NetSystemTray, NetSystemTrayOP, NetSystemTrayOrientation, NetSystemTrayOrientationHorz,
  66 #endif /* }}} */
  67        NetWMFullscreen, NetActiveWindow, NetWMWindowType,
  68        NetWMWindowTypeDialog, NetClientList, NetLast }; /* EWMH atoms */
  69 enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */
  70 enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle,
  71        ClkClientWin, ClkRootWin, ClkLast }; /* clicks */
  72 
  73 typedef union {
  74 	int i;
  75 	unsigned int ui;
  76 	float f;
  77 	const void *v;
  78 } Arg;
  79 
  80 typedef struct {
  81 	unsigned int click;
  82 	unsigned int mask;
  83 	unsigned int button;
  84 	void (*func)(const Arg *arg);
  85 	const Arg arg;
  86 } Button;
  87 
  88 typedef struct Monitor Monitor;
  89 typedef struct Client Client;
  90 struct Client {
  91 	char name[256];
  92 	float mina, maxa;
  93 	int x, y, w, h;
  94 	int oldx, oldy, oldw, oldh;
  95 	int basew, baseh, incw, inch, maxw, maxh, minw, minh;
  96 	int bw, oldbw;
  97 	unsigned int tags;
  98 	int isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen;
  99 	Client *next;
 100 	Client *snext;
 101 	Monitor *mon;
 102 	Window win;
 103 };
 104 
 105 typedef struct {
 106 	unsigned int mod;
 107 	KeySym keysym;
 108 	void (*func)(const Arg *);
 109 	const Arg arg;
 110 } Key;
 111 
 112 typedef struct {
 113 	const char *symbol;
 114 	void (*arrange)(Monitor *);
 115 } Layout;
 116 
 117 #if PATCH_PERTAG /* {{{ */
 118 typedef struct Pertag Pertag;
 119 #endif /* }}} */
 120 struct Monitor {
 121 	char ltsymbol[16];
 122 	float mfact;
 123 	int nmaster;
 124 	int num;
 125 	int by;               /* bar geometry */
 126 #if PATCH_AWESOMEBAR      /* {{{ */
 127 	int btw;              /* width of tasks portion of bar */
 128 	int bt;               /* number of tasks */
 129 #endif                    /* }}} */
 130 	int mx, my, mw, mh;   /* screen size */
 131 	int wx, wy, ww, wh;   /* window area  */
 132 	unsigned int seltags;
 133 	unsigned int sellt;
 134 	unsigned int tagset[2];
 135 	int showbar;
 136 	int topbar;
 137 	Client *clients;
 138 	Client *sel;
 139 	Client *stack;
 140 	Monitor *next;
 141 	Window barwin;
 142 	const Layout *lt[2];
 143 #if PATCH_PERTAG /* {{{ */
 144 	Pertag *pertag;
 145 #endif /* }}} */
 146 };
 147 
 148 typedef struct {
 149 	const char *class;
 150 	const char *instance;
 151 	const char *title;
 152 	unsigned int tags;
 153 	int isfloating;
 154 	int monitor;
 155 } Rule;
 156 
 157 /* function declarations */
 158 static void applyrules(Client *c);
 159 static int applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact);
 160 static void arrange(Monitor *m);
 161 static void arrangemon(Monitor *m);
 162 static void attach(Client *c);
 163 static void attachstack(Client *c);
 164 static void buttonpress(XEvent *e);
 165 static void checkotherwm(void);
 166 static void cleanup(void);
 167 static void cleanupmon(Monitor *mon);
 168 static void clientmessage(XEvent *e);
 169 static void configure(Client *c);
 170 static void configurenotify(XEvent *e);
 171 static void configurerequest(XEvent *e);
 172 static Monitor *createmon(void);
 173 static void destroynotify(XEvent *e);
 174 static void detach(Client *c);
 175 static void detachstack(Client *c);
 176 static Monitor *dirtomon(int dir);
 177 static void drawbar(Monitor *m);
 178 static void drawbars(void);
 179 static void enternotify(XEvent *e);
 180 static void expose(XEvent *e);
 181 static void focus(Client *c);
 182 static void focusin(XEvent *e);
 183 static void focusmon(const Arg *arg);
 184 static void focusstack(const Arg *arg);
 185 static Atom getatomprop(Client *c, Atom prop);
 186 static int getrootptr(int *x, int *y);
 187 static long getstate(Window w);
 188 static int gettextprop(Window w, Atom atom, char *text, unsigned int size);
 189 static void grabbuttons(Client *c, int focused);
 190 static void grabkeys(void);
 191 static void incnmaster(const Arg *arg);
 192 static void keypress(XEvent *e);
 193 static void killclient(const Arg *arg);
 194 static void manage(Window w, XWindowAttributes *wa);
 195 static void mappingnotify(XEvent *e);
 196 static void maprequest(XEvent *e);
 197 static void monocle(Monitor *m);
 198 static void motionnotify(XEvent *e);
 199 static void movemouse(const Arg *arg);
 200 static Client *nexttiled(Client *c);
 201 static void pop(Client *);
 202 static void propertynotify(XEvent *e);
 203 static void quit(const Arg *arg);
 204 static Monitor *recttomon(int x, int y, int w, int h);
 205 static void resize(Client *c, int x, int y, int w, int h, int interact);
 206 static void resizeclient(Client *c, int x, int y, int w, int h);
 207 static void resizemouse(const Arg *arg);
 208 static void restack(Monitor *m);
 209 static void run(void);
 210 static void scan(void);
 211 #if PATCH_SYSTRAY /* {{{ */
 212 static int sendevent(Window w, Atom proto, int m, long d0, long d1, long d2, long d3, long d4);
 213 #else /* }}} */
 214 static int sendevent(Client *c, Atom proto);
 215 #endif
 216 static void sendmon(Client *c, Monitor *m);
 217 static void setclientstate(Client *c, long state);
 218 static void setfocus(Client *c);
 219 static void setfullscreen(Client *c, int fullscreen);
 220 static void setlayout(const Arg *arg);
 221 static void setmfact(const Arg *arg);
 222 static void setup(void);
 223 static void seturgent(Client *c, int urg);
 224 static void showhide(Client *c);
 225 static void sigchld(int unused);
 226 static void spawn(const Arg *arg);
 227 static void tag(const Arg *arg);
 228 static void tagmon(const Arg *arg);
 229 static void tile(Monitor *);
 230 static void togglebar(const Arg *arg);
 231 static void togglefloating(const Arg *arg);
 232 static void toggletag(const Arg *arg);
 233 static void toggleview(const Arg *arg);
 234 static void unfocus(Client *c, int setfocus);
 235 static void unmanage(Client *c, int destroyed);
 236 static void unmapnotify(XEvent *e);
 237 static void updatebarpos(Monitor *m);
 238 static void updatebars(void);
 239 static void updateclientlist(void);
 240 static int updategeom(void);
 241 static void updatenumlockmask(void);
 242 static void updatesizehints(Client *c);
 243 static void updatestatus(void);
 244 static void updatetitle(Client *c);
 245 static void updatewindowtype(Client *c);
 246 static void updatewmhints(Client *c);
 247 static void view(const Arg *arg);
 248 static Client *wintoclient(Window w);
 249 static Monitor *wintomon(Window w);
 250 static int xerror(Display *dpy, XErrorEvent *ee);
 251 static int xerrordummy(Display *dpy, XErrorEvent *ee);
 252 static int xerrorstart(Display *dpy, XErrorEvent *ee);
 253 static void zoom(const Arg *arg);
 254 
 255 #if PATCH_DECORATION_HINTS /* {{{ */
 256 #define MWM_HINTS_FLAGS_FIELD       0
 257 #define MWM_HINTS_DECORATIONS_FIELD 2
 258 #define MWM_HINTS_DECORATIONS       (1 << 1)
 259 #define MWM_DECOR_ALL               (1 << 0)
 260 #define MWM_DECOR_BORDER            (1 << 1)
 261 #define MWM_DECOR_TITLE             (1 << 3)
 262 
 263 static Atom motifatom;
 264 static void updatemotifhints(Client *c);
 265 #endif /* }}} */
 266 #if PATCH_WARP /* {{{ */
 267 static void warp(const Client *c);
 268 #endif /* }}} */
 269 #if PATCH_AWESOMEBAR /* {{{ */
 270 static void togglewin(const Arg *arg);
 271 static void killwin(const Arg *arg);
 272 static void zoomwin(const Arg *arg);
 273 #endif /* }}} */
 274 #if PATCH_SYSTRAY /* {{{ */
 275 #define SYSTEM_TRAY_REQUEST_DOCK    0
 276 
 277 #define XEMBED_EMBEDDED_NOTIFY      0
 278 #define XEMBED_WINDOW_ACTIVATE      1
 279 #define XEMBED_FOCUS_IN             4
 280 #define XEMBED_MODALITY_ON         10
 281 
 282 #define XEMBED_MAPPED              (1 << 0)
 283 #define XEMBED_WINDOW_ACTIVATE      1
 284 #define XEMBED_WINDOW_DEACTIVATE    2
 285 
 286 #define VERSION_MAJOR               0
 287 #define VERSION_MINOR               0
 288 #define XEMBED_EMBEDDED_VERSION (VERSION_MAJOR << 16) | VERSION_MINOR
 289 
 290 enum { Manager, Xembed, XembedInfo, XLast };
 291 typedef struct Systray   Systray;
 292 struct Systray {
 293 	Window win;
 294 	Client *icons;
 295 };
 296 static Systray *systray =  NULL;
 297 static Atom xatom[XLast];
 298 
 299 static unsigned int getsystraywidth();
 300 static void removesystrayicon(Client *i);
 301 static void resizebarwin(Monitor *m);
 302 static void resizerequest(XEvent *e);
 303 static Monitor *systraytomon(Monitor *m);
 304 static void updatesystray(void);
 305 static void updatesystrayicongeom(Client *i, int w, int h);
 306 static void updatesystrayiconstate(Client *i, XPropertyEvent *ev);
 307 static Client *wintosystrayicon(Window w);
 308 #endif /* }}} */
 309 
 310 /* variables */
 311 static const char broken[] = "broken";
 312 static char stext[256];
 313 static int screen;
 314 static int sw, sh;           /* X display screen geometry width, height */
 315 static int bh, blw = 0;      /* bar geometry */
 316 static int lrpad;            /* sum of left and right padding for text */
 317 static int (*xerrorxlib)(Display *, XErrorEvent *);
 318 static unsigned int numlockmask = 0;
 319 static void (*handler[LASTEvent]) (XEvent *) = {
 320 	[ButtonPress] = buttonpress,
 321 	[ClientMessage] = clientmessage,
 322 	[ConfigureRequest] = configurerequest,
 323 	[ConfigureNotify] = configurenotify,
 324 	[DestroyNotify] = destroynotify,
 325 	[EnterNotify] = enternotify,
 326 	[Expose] = expose,
 327 	[FocusIn] = focusin,
 328 	[KeyPress] = keypress,
 329 	[MappingNotify] = mappingnotify,
 330 	[MapRequest] = maprequest,
 331 	[MotionNotify] = motionnotify,
 332 	[PropertyNotify] = propertynotify,
 333 #if PATCH_SYSTRAY /* {{{ */
 334 	[ResizeRequest] = resizerequest,
 335 #endif /* }}} */
 336 	[UnmapNotify] = unmapnotify
 337 };
 338 static Atom wmatom[WMLast], netatom[NetLast];
 339 static int running = 1;
 340 static Cur *cursor[CurLast];
 341 static Clr **scheme;
 342 static Display *dpy;
 343 static Drw *drw;
 344 static Monitor *mons, *selmon;
 345 static Window root, wmcheckwin;
 346 
 347 /* configuration, allows nested code to access above variables */
 348 #include "config.h"
 349 
 350 #if PATCH_PERTAG /* {{{ */
 351 struct Pertag {
 352 	unsigned int curtag, prevtag; /* current and previous tag */
 353 	int nmasters[LENGTH(tags) + 1]; /* number of windows in master area */
 354 	float mfacts[LENGTH(tags) + 1]; /* mfacts per tag */
 355 	unsigned int sellts[LENGTH(tags) + 1]; /* selected layouts */
 356 	const Layout *ltidxs[LENGTH(tags) + 1][2]; /* matrix of tags and layouts indexes  */
 357 	int showbars[LENGTH(tags) + 1]; /* display bar for the current tag */
 358 };
 359 #endif /* }}} */
 360 
 361 /* compile-time check if all tags fit into an unsigned int bit array. */
 362 struct NumTags { char limitexceeded[LENGTH(tags) > 31 ? -1 : 1]; };
 363 
 364 /* function implementations */
 365 void
 366 applyrules(Client *c)
 367 {
 368 	const char *class, *instance;
 369 	unsigned int i;
 370 	const Rule *r;
 371 	Monitor *m;
 372 	XClassHint ch = { NULL, NULL };
 373 
 374 	/* rule matching */
 375 	c->isfloating = 0;
 376 	c->tags = 0;
 377 	XGetClassHint(dpy, c->win, &ch);
 378 	class    = ch.res_class ? ch.res_class : broken;
 379 	instance = ch.res_name  ? ch.res_name  : broken;
 380 
 381 	for (i = 0; i < LENGTH(rules); i++) {
 382 		r = &rules[i];
 383 		if ((!r->title || strstr(c->name, r->title))
 384 		&& (!r->class || strstr(class, r->class))
 385 		&& (!r->instance || strstr(instance, r->instance)))
 386 		{
 387 			c->isfloating = r->isfloating;
 388 			c->tags |= r->tags;
 389 			for (m = mons; m && m->num != r->monitor; m = m->next);
 390 			if (m)
 391 				c->mon = m;
 392 		}
 393 	}
 394 	if (ch.res_class)
 395 		XFree(ch.res_class);
 396 	if (ch.res_name)
 397 		XFree(ch.res_name);
 398 	c->tags = c->tags & TAGMASK ? c->tags & TAGMASK : c->mon->tagset[c->mon->seltags];
 399 }
 400 
 401 int
 402 applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact)
 403 {
 404 	int baseismin;
 405 	Monitor *m = c->mon;
 406 
 407 	/* set minimum possible */
 408 	*w = MAX(1, *w);
 409 	*h = MAX(1, *h);
 410 	if (interact) {
 411 		if (*x > sw)
 412 			*x = sw - WIDTH(c);
 413 		if (*y > sh)
 414 			*y = sh - HEIGHT(c);
 415 		if (*x + *w + 2 * c->bw < 0)
 416 			*x = 0;
 417 		if (*y + *h + 2 * c->bw < 0)
 418 			*y = 0;
 419 	} else {
 420 		if (*x >= m->wx + m->ww)
 421 			*x = m->wx + m->ww - WIDTH(c);
 422 		if (*y >= m->wy + m->wh)
 423 			*y = m->wy + m->wh - HEIGHT(c);
 424 		if (*x + *w + 2 * c->bw <= m->wx)
 425 			*x = m->wx;
 426 		if (*y + *h + 2 * c->bw <= m->wy)
 427 			*y = m->wy;
 428 	}
 429 	if (*h < bh)
 430 		*h = bh;
 431 	if (*w < bh)
 432 		*w = bh;
 433 	if (resizehints || c->isfloating || !c->mon->lt[c->mon->sellt]->arrange) {
 434 		/* see last two sentences in ICCCM 4.1.2.3 */
 435 		baseismin = c->basew == c->minw && c->baseh == c->minh;
 436 		if (!baseismin) { /* temporarily remove base dimensions */
 437 			*w -= c->basew;
 438 			*h -= c->baseh;
 439 		}
 440 		/* adjust for aspect limits */
 441 		if (c->mina > 0 && c->maxa > 0) {
 442 			if (c->maxa < (float)*w / *h)
 443 				*w = *h * c->maxa + 0.5;
 444 			else if (c->mina < (float)*h / *w)
 445 				*h = *w * c->mina + 0.5;
 446 		}
 447 		if (baseismin) { /* increment calculation requires this */
 448 			*w -= c->basew;
 449 			*h -= c->baseh;
 450 		}
 451 		/* adjust for increment value */
 452 		if (c->incw)
 453 			*w -= *w % c->incw;
 454 		if (c->inch)
 455 			*h -= *h % c->inch;
 456 		/* restore base dimensions */
 457 		*w = MAX(*w + c->basew, c->minw);
 458 		*h = MAX(*h + c->baseh, c->minh);
 459 		if (c->maxw)
 460 			*w = MIN(*w, c->maxw);
 461 		if (c->maxh)
 462 			*h = MIN(*h, c->maxh);
 463 #if PATCH_FLOATINGABOVE /* {{{ */
 464 		Atom prop = (c->isfloating || !c->mon->lt[c->mon->sellt]->arrange ?
 465 		XInternAtom(dpy, "_NET_WM_STATE_ABOVE", False) : None);
 466 		XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32,
 467 		PropModeReplace, (unsigned char*)&prop, True);
 468 #endif /* }}} */
 469 	}
 470 	return *x != c->x || *y != c->y || *w != c->w || *h != c->h;
 471 }
 472 
 473 void
 474 arrange(Monitor *m)
 475 {
 476 	if (m)
 477 		showhide(m->stack);
 478 	else for (m = mons; m; m = m->next)
 479 		showhide(m->stack);
 480 	if (m) {
 481 		arrangemon(m);
 482 		restack(m);
 483 	} else for (m = mons; m; m = m->next)
 484 		arrangemon(m);
 485 }
 486 
 487 void
 488 arrangemon(Monitor *m)
 489 {
 490 	strncpy(m->ltsymbol, m->lt[m->sellt]->symbol, sizeof m->ltsymbol);
 491 	if (m->lt[m->sellt]->arrange)
 492 		m->lt[m->sellt]->arrange(m);
 493 }
 494 
 495 void
 496 attach(Client *c)
 497 {
 498 	c->next = c->mon->clients;
 499 	c->mon->clients = c;
 500 }
 501 
 502 void
 503 attachstack(Client *c)
 504 {
 505 	c->snext = c->mon->stack;
 506 	c->mon->stack = c;
 507 }
 508 
 509 void
 510 buttonpress(XEvent *e)
 511 {
 512 	unsigned int i, x, click;
 513 	Arg arg = {0};
 514 	Client *c;
 515 	Monitor *m;
 516 	XButtonPressedEvent *ev = &e->xbutton;
 517 
 518 	click = ClkRootWin;
 519 	/* focus monitor if necessary */
 520 	if ((m = wintomon(ev->window)) && m != selmon) {
 521 		unfocus(selmon->sel, 1);
 522 		selmon = m;
 523 		focus(NULL);
 524 	}
 525 	if (ev->window == selmon->barwin) {
 526 		i = x = 0;
 527 		do
 528 			x += TEXTW(tags[i]);
 529 		while (ev->x >= x && ++i < LENGTH(tags));
 530 		if (i < LENGTH(tags)) {
 531 			click = ClkTagBar;
 532 			arg.ui = 1 << i;
 533 		} else if (ev->x < x + blw)
 534 			click = ClkLtSymbol;
 535 #if PATCH_SYSTRAY /* {{{ */
 536 		else if (ev->x > selmon->ww - (int)TEXTW(stext) - getsystraywidth())
 537 #else /* }}} */
 538 		else if (ev->x > selmon->ww - (int)TEXTW(stext))
 539 #endif
 540 			click = ClkStatusText;
 541 #if PATCH_AWESOMEBAR /* {{{ */
 542 		else {
 543 			x += blw;
 544 			c = m->clients;
 545 
 546 			if (c) {
 547 				do {
 548 					if (!ISVISIBLE(c))
 549 						continue;
 550 					else
 551 						x += (1.0 / (double)m->bt) * m->btw;
 552 				} while (ev->x > x && (c = c->next));
 553 
 554 				click = ClkWinTitle;
 555 				arg.v = c;
 556 			}
 557 		}
 558 #else /* }}} */
 559 		else
 560 			click = ClkWinTitle;
 561 #endif
 562 	} else if ((c = wintoclient(ev->window))) {
 563 		focus(c);
 564 		restack(selmon);
 565 		XAllowEvents(dpy, ReplayPointer, CurrentTime);
 566 		click = ClkClientWin;
 567 	}
 568 	for (i = 0; i < LENGTH(buttons); i++)
 569 		if (click == buttons[i].click && buttons[i].func && buttons[i].button == ev->button
 570 		&& CLEANMASK(buttons[i].mask) == CLEANMASK(ev->state))
 571 #if PATCH_AWESOMEBAR /* {{{ */
 572 			buttons[i].func((click == ClkTagBar || click == ClkWinTitle) &&
 573 					buttons[i].arg.i == 0 ? &arg : &buttons[i].arg);
 574 #else /* }}} */
 575 			buttons[i].func(click == ClkTagBar && buttons[i].arg.i == 0 ? &arg : &buttons[i].arg);
 576 #endif
 577 }
 578 
 579 void
 580 checkotherwm(void)
 581 {
 582 	xerrorxlib = XSetErrorHandler(xerrorstart);
 583 	/* this causes an error if some other window manager is running */
 584 	XSelectInput(dpy, DefaultRootWindow(dpy), SubstructureRedirectMask);
 585 	XSync(dpy, False);
 586 	XSetErrorHandler(xerror);
 587 	XSync(dpy, False);
 588 }
 589 
 590 void
 591 cleanup(void)
 592 {
 593 	Arg a = {.ui = ~0};
 594 	Layout foo = { "", NULL };
 595 	Monitor *m;
 596 	size_t i;
 597 
 598 	view(&a);
 599 	selmon->lt[selmon->sellt] = &foo;
 600 	for (m = mons; m; m = m->next)
 601 		while (m->stack)
 602 			unmanage(m->stack, 0);
 603 	XUngrabKey(dpy, AnyKey, AnyModifier, root);
 604 	while (mons)
 605 		cleanupmon(mons);
 606 #if PATCH_SYSTRAY /* {{{ */
 607 	XUnmapWindow(dpy, systray->win);
 608 	// XDestroyWindow(dpy, systray->win);
 609 	free(systray);
 610 #endif /* }}} */
 611 	for (i = 0; i < CurLast; i++)
 612 		drw_cur_free(drw, cursor[i]);
 613 	for (i = 0; i < LENGTH(colors); i++)
 614 		free(scheme[i]);
 615 	XDestroyWindow(dpy, wmcheckwin);
 616 	drw_free(drw);
 617 	XSync(dpy, False);
 618 	XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime);
 619 	XDeleteProperty(dpy, root, netatom[NetActiveWindow]);
 620 }
 621 
 622 void
 623 cleanupmon(Monitor *mon)
 624 {
 625 	Monitor *m;
 626 
 627 	if (mon == mons)
 628 		mons = mons->next;
 629 	else {
 630 		for (m = mons; m && m->next != mon; m = m->next);
 631 		m->next = mon->next;
 632 	}
 633 	XUnmapWindow(dpy, mon->barwin);
 634 	XDestroyWindow(dpy, mon->barwin);
 635 	free(mon);
 636 }
 637 
 638 void
 639 clientmessage(XEvent *e)
 640 {
 641 	XClientMessageEvent *cme = &e->xclient;
 642 	Client *c = wintoclient(cme->window);
 643 
 644 #if PATCH_SYSTRAY /* {{{ */
 645 	if (cme->window == systray->win && cme->message_type == netatom[NetSystemTrayOP]) {
 646 		XWindowAttributes wa;
 647 		XSetWindowAttributes swa;
 648 		/* add systray icons */
 649 		if (cme->data.l[1] == SYSTEM_TRAY_REQUEST_DOCK) {
 650 			if (!(c = (Client *)calloc(1, sizeof(Client))))
 651 				die("fatal: could not malloc() %u bytes\n", sizeof(Client));
 652 			if (!(c->win = cme->data.l[2])) {
 653 				free(c);
 654 				return;
 655 			}
 656 			c->mon = selmon;
 657 			c->next = systray->icons;
 658 			systray->icons = c;
 659 			if (!XGetWindowAttributes(dpy, c->win, &wa)) {
 660 				/* use sane defaults */
 661 				wa.width = bh;
 662 				wa.height = bh;
 663 				wa.border_width = 0;
 664 			}
 665 			c->x = c->oldx = c->y = c->oldy = 0;
 666 			c->w = c->oldw = wa.width;
 667 			c->h = c->oldh = wa.height;
 668 			c->oldbw = wa.border_width;
 669 			c->bw = 0;
 670 			c->isfloating = True;
 671 			/* reuse tags field as mapped status */
 672 			c->tags = 1;
 673 			updatesizehints(c);
 674 			updatesystrayicongeom(c, wa.width, wa.height);
 675 			XAddToSaveSet(dpy, c->win);
 676 			XSelectInput(dpy, c->win, StructureNotifyMask | PropertyChangeMask | ResizeRedirectMask);
 677 			XReparentWindow(dpy, c->win, systray->win, 0, 0);
 678 			/* use parents background color */
 679 			swa.background_pixel  = scheme[SchemeNorm][ColBg].pixel;
 680 			XChangeWindowAttributes(dpy, c->win, CWBackPixel, &swa);
 681 			sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_EMBEDDED_NOTIFY, 0 , systray->win, XEMBED_EMBEDDED_VERSION);
 682 			/* FIXME not sure if I have to send these events, too */
 683 			sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_FOCUS_IN, 0 , systray->win, XEMBED_EMBEDDED_VERSION);
 684 			sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_WINDOW_ACTIVATE, 0 , systray->win, XEMBED_EMBEDDED_VERSION);
 685 			sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_MODALITY_ON, 0 , systray->win, XEMBED_EMBEDDED_VERSION);
 686 			XSync(dpy, False);
 687 			resizebarwin(selmon);
 688 			updatesystray();
 689 			setclientstate(c, NormalState);
 690 		}
 691 		return;
 692 	}
 693 #endif /* }}} */
 694 	if (!c)
 695 		return;
 696 	if (cme->message_type == netatom[NetWMState]) {
 697 		if (cme->data.l[1] == netatom[NetWMFullscreen]
 698 		|| cme->data.l[2] == netatom[NetWMFullscreen])
 699 			setfullscreen(c, (cme->data.l[0] == 1 /* _NET_WM_STATE_ADD    */
 700 				|| (cme->data.l[0] == 2 /* _NET_WM_STATE_TOGGLE */ && !c->isfullscreen)));
 701 	} else if (cme->message_type == netatom[NetActiveWindow]) {
 702 #if PATCH_FOCUSONNETACTIVE /* {{{ */
 703 		unsigned int i;
 704 		for (i = 0; i < LENGTH(tags) && !((1 << i) & c->tags); i++);
 705 		if (i < LENGTH(tags)) {
 706 			const Arg a = {.ui = 1 << i};
 707 			selmon = c->mon;
 708 			view(&a);
 709 			focus(c);
 710 			restack(selmon);
 711 		}
 712 #else /* }}} */
 713 		if (c != selmon->sel && !c->isurgent)
 714 			seturgent(c, 1);
 715 #endif
 716 	}
 717 }
 718 
 719 void
 720 configure(Client *c)
 721 {
 722 	XConfigureEvent ce;
 723 
 724 	ce.type = ConfigureNotify;
 725 	ce.display = dpy;
 726 	ce.event = c->win;
 727 	ce.window = c->win;
 728 	ce.x = c->x;
 729 	ce.y = c->y;
 730 	ce.width = c->w;
 731 	ce.height = c->h;
 732 	ce.border_width = c->bw;
 733 	ce.above = None;
 734 	ce.override_redirect = False;
 735 	XSendEvent(dpy, c->win, False, StructureNotifyMask, (XEvent *)&ce);
 736 }
 737 
 738 void
 739 configurenotify(XEvent *e)
 740 {
 741 	Monitor *m;
 742 	Client *c;
 743 	XConfigureEvent *ev = &e->xconfigure;
 744 	int dirty;
 745 
 746 	/* TODO: updategeom handling sucks, needs to be simplified */
 747 	if (ev->window == root) {
 748 		dirty = (sw != ev->width || sh != ev->height);
 749 		sw = ev->width;
 750 		sh = ev->height;
 751 		if (updategeom() || dirty) {
 752 			drw_resize(drw, sw, bh);
 753 			updatebars();
 754 			for (m = mons; m; m = m->next) {
 755 				for (c = m->clients; c; c = c->next)
 756 					if (c->isfullscreen)
 757 						resizeclient(c, m->mx, m->my, m->mw, m->mh);
 758 #if PATCH_SYSTRAY /* {{{ */
 759 				resizebarwin(m);
 760 #else /* }}} */
 761 				XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, m->ww, bh);
 762 #endif
 763 			}
 764 			focus(NULL);
 765 			arrange(NULL);
 766 		}
 767 	}
 768 }
 769 
 770 void
 771 configurerequest(XEvent *e)
 772 {
 773 	Client *c;
 774 	Monitor *m;
 775 	XConfigureRequestEvent *ev = &e->xconfigurerequest;
 776 	XWindowChanges wc;
 777 
 778 	if ((c = wintoclient(ev->window))) {
 779 		if (ev->value_mask & CWBorderWidth)
 780 			c->bw = ev->border_width;
 781 		else if (c->isfloating || !selmon->lt[selmon->sellt]->arrange) {
 782 			m = c->mon;
 783 			if (ev->value_mask & CWX) {
 784 				c->oldx = c->x;
 785 				c->x = m->mx + ev->x;
 786 			}
 787 			if (ev->value_mask & CWY) {
 788 				c->oldy = c->y;
 789 				c->y = m->my + ev->y;
 790 			}
 791 			if (ev->value_mask & CWWidth) {
 792 				c->oldw = c->w;
 793 				c->w = ev->width;
 794 			}
 795 			if (ev->value_mask & CWHeight) {
 796 				c->oldh = c->h;
 797 				c->h = ev->height;
 798 			}
 799 			if ((c->x + c->w) > m->mx + m->mw && c->isfloating)
 800 				c->x = m->mx + (m->mw / 2 - WIDTH(c) / 2); /* center in x direction */
 801 			if ((c->y + c->h) > m->my + m->mh && c->isfloating)
 802 				c->y = m->my + (m->mh / 2 - HEIGHT(c) / 2); /* center in y direction */
 803 			if ((ev->value_mask & (CWX|CWY)) && !(ev->value_mask & (CWWidth|CWHeight)))
 804 				configure(c);
 805 			if (ISVISIBLE(c))
 806 				XMoveResizeWindow(dpy, c->win, c->x, c->y, c->w, c->h);
 807 		} else
 808 			configure(c);
 809 	} else {
 810 		wc.x = ev->x;
 811 		wc.y = ev->y;
 812 		wc.width = ev->width;
 813 		wc.height = ev->height;
 814 		wc.border_width = ev->border_width;
 815 		wc.sibling = ev->above;
 816 		wc.stack_mode = ev->detail;
 817 		XConfigureWindow(dpy, ev->window, ev->value_mask, &wc);
 818 	}
 819 	XSync(dpy, False);
 820 }
 821 
 822 Monitor *
 823 createmon(void)
 824 {
 825 	Monitor *m;
 826 
 827 	m = ecalloc(1, sizeof(Monitor));
 828 	m->tagset[0] = m->tagset[1] = 1;
 829 	m->mfact = mfact;
 830 	m->nmaster = nmaster;
 831 	m->showbar = showbar;
 832 	m->topbar = topbar;
 833 	m->lt[0] = &layouts[0];
 834 	m->lt[1] = &layouts[1 % LENGTH(layouts)];
 835 	strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol);
 836 #if PATCH_PERTAG /* {{{ */
 837 	m->pertag = ecalloc(1, sizeof(Pertag));
 838 	m->pertag->curtag = m->pertag->prevtag = 1;
 839 
 840 	for (int i = 0; i <= LENGTH(tags); i++) {
 841 		m->pertag->nmasters[i] = m->nmaster;
 842 		m->pertag->mfacts[i] = m->mfact;
 843 
 844 		m->pertag->ltidxs[i][0] = m->lt[0];
 845 		m->pertag->ltidxs[i][1] = m->lt[1];
 846 		m->pertag->sellts[i] = m->sellt;
 847 
 848 		m->pertag->showbars[i] = m->showbar;
 849 	}
 850 #endif /* }}} */
 851 	return m;
 852 }
 853 
 854 void
 855 destroynotify(XEvent *e)
 856 {
 857 	Client *c;
 858 	XDestroyWindowEvent *ev = &e->xdestroywindow;
 859 
 860 	if ((c = wintoclient(ev->window)))
 861 		unmanage(c, 1);
 862 #if PATCH_SYSTRAY /* {{{ */
 863 	else if ((c = wintosystrayicon(ev->window))) {
 864 		removesystrayicon(c);
 865 		resizebarwin(selmon);
 866 		updatesystray();
 867 	}
 868 #endif /* }}} */
 869 }
 870 
 871 void
 872 detach(Client *c)
 873 {
 874 	Client **tc;
 875 
 876 	for (tc = &c->mon->clients; *tc && *tc != c; tc = &(*tc)->next);
 877 	*tc = c->next;
 878 }
 879 
 880 void
 881 detachstack(Client *c)
 882 {
 883 	Client **tc, *t;
 884 
 885 	for (tc = &c->mon->stack; *tc && *tc != c; tc = &(*tc)->snext);
 886 	*tc = c->snext;
 887 
 888 	if (c == c->mon->sel) {
 889 		for (t = c->mon->stack; t && !ISVISIBLE(t); t = t->snext);
 890 		c->mon->sel = t;
 891 	}
 892 }
 893 
 894 Monitor *
 895 dirtomon(int dir)
 896 {
 897 	Monitor *m = NULL;
 898 
 899 	if (dir > 0) {
 900 		if (!(m = selmon->next))
 901 			m = mons;
 902 	} else if (selmon == mons)
 903 		for (m = mons; m->next; m = m->next);
 904 	else
 905 		for (m = mons; m->next != selmon; m = m->next);
 906 	return m;
 907 }
 908 
 909 void
 910 drawbar(Monitor *m)
 911 {
 912 	int x, w, tw = 0;
 913 	int boxs = drw->fonts->h / 9;
 914 	int boxw = drw->fonts->h / 6 + 2;
 915 	unsigned int i, occ = 0, urg = 0;
 916 	Client *c;
 917 #if PATCH_AWESOMEBAR /* {{{ */
 918 	int n = 0;
 919 #endif /* }}} */
 920 #if PATCH_SYSTRAY /* {{{ */
 921 	int stw = 0;
 922 
 923 	if(m == systraytomon(m))
 924 		stw = getsystraywidth();
 925 
 926 	if (m == selmon) {
 927 		drw_setscheme(drw, scheme[SchemeNorm]);
 928 		tw = TEXTW(stext) - lrpad / 2 + 2;
 929 		drw_text(drw, m->ww - tw - stw, 0, tw, bh, lrpad / 2 - 2, stext, 0);
 930 	}
 931 	resizebarwin(m);
 932 #else /* }}} */
 933 	/* draw status first so it can be overdrawn by tags later */
 934 	if (m == selmon) { /* status is only drawn on selected monitor */
 935 		drw_setscheme(drw, scheme[SchemeNorm]);
 936 		tw = TEXTW(stext) - lrpad + 2; /* 2px right padding */
 937 		drw_text(drw, m->ww - tw, 0, tw, bh, 0, stext, 0);
 938 	}
 939 #endif
 940 
 941 	for (c = m->clients; c; c = c->next) {
 942 #if PATCH_AWESOMEBAR /* {{{ */
 943 		if (ISVISIBLE(c)) n++;
 944 #endif /* }}} */
 945 		occ |= c->tags;
 946 		if (c->isurgent)
 947 			urg |= c->tags;
 948 	}
 949 	x = 0;
 950 	for (i = 0; i < LENGTH(tags); i++) {
 951 		w = TEXTW(tags[i]);
 952 		drw_setscheme(drw, scheme[m->tagset[m->seltags] & 1 << i ? SchemeSel : SchemeNorm]);
 953 		drw_text(drw, x, 0, w, bh, lrpad / 2, tags[i], urg & 1 << i);
 954 		if (occ & 1 << i)
 955 			drw_rect(drw, x + boxs, boxs, boxw, boxw,
 956 				m == selmon && selmon->sel && selmon->sel->tags & 1 << i,
 957 				urg & 1 << i);
 958 		x += w;
 959 	}
 960 	w = blw = TEXTW(m->ltsymbol);
 961 	drw_setscheme(drw, scheme[SchemeNorm]);
 962 	x = drw_text(drw, x, 0, w, bh, lrpad / 2, m->ltsymbol, 0);
 963 
 964 #if PATCH_SYSTRAY /* {{{ */
 965 	if ((w = m->ww - tw - stw - x) > bh) {
 966 #else /* }}} */
 967 	if ((w = m->ww - tw - x) > bh) {
 968 #endif
 969 		if (m->sel) {
 970 #if PATCH_NOSCHEMESEL /* {{{ */
 971 			drw_setscheme(drw, scheme[SchemeNorm]);
 972 #else /* }}} */
 973 			drw_setscheme(drw, scheme[m == selmon ? SchemeSel : SchemeNorm]);
 974 #endif
 975 #if PATCH_AWESOMEBAR /* #elif PATCH_CENTEREDWINDOWNAME {{{ */
 976 			if (n > 0) {
 977 				int remainder = w % n;
 978 				int tabw = (1.0 / (double)n) * w + 1;
 979 				for (c = m->clients; c; c = c->next) {
 980 					if (!ISVISIBLE(c)) continue;
 981 					drw_setscheme(drw, scheme[m->sel == c ? SchemeSel : SchemeNorm]);
 982 	#if PATCH_NOSCHEMESEL /* {{{ */
 983 					if (n == 1) drw_setscheme(drw, scheme[SchemeNorm]);
 984 	#endif /* }}} */
 985 					if (remainder >= 0) {
 986 						if (remainder == 0)
 987 							tabw--;
 988 						remainder--;
 989 					}
 990 	#if PATCH_CENTEREDWINDOWNAME /* {{{ */
 991 					int mid = tabw / 2 - TEXTW(c->name) / 2;
 992 		#if PATCH_NOSCHEMESEL /* {{{ */
 993 					if (n == 1) mid = (m->ww - TEXTW(c->name)) / 2 - x;
 994 		#endif /* }}} */
 995 					mid = mid >= lrpad / 2 ? mid : lrpad / 2;
 996 					drw_text(drw, x, 0, tabw, bh, mid, c->name, 0);
 997 					if (c->isfloating)
 998 						drw_rect(drw, x + boxs + mid - 6, boxs, boxw, boxw, c->isfixed, 0);
 999 	#else /* }}} */
1000 					drw_text(drw, x, 0, tabw, bh, lrpad / 2, c->name, 0);
1001 					if (c->isfloating)
1002 						drw_rect(drw, x + boxs, boxs, boxw, boxw, c->isfixed, 0);
1003 	#endif
1004 					x += tabw;
1005 				}
1006 			}
1007 #elif PATCH_CENTEREDWINDOWNAME
1008 			int mid = (m->ww - TEXTW(m->sel->name)) / 2 - x;
1009 			mid = mid >= lrpad / 2 ? mid : lrpad / 2;
1010 			drw_text(drw, x, 0, w, bh, mid, m->sel->name, 0);
1011 			if (m->sel->isfloating)
1012 				drw_rect(drw, x + boxs + mid - 6, boxs, boxw, boxw, m->sel->isfixed, 0);
1013 #else /* }}} */
1014 			drw_text(drw, x, 0, w, bh, lrpad / 2, m->sel->name, 0);
1015 			if (m->sel->isfloating)
1016 				drw_rect(drw, x + boxs, boxs, boxw, boxw, m->sel->isfixed, 0);
1017 #endif
1018 		} else {
1019 			drw_setscheme(drw, scheme[SchemeNorm]);
1020 			drw_rect(drw, x, 0, w, bh, 1, 1);
1021 		}
1022 	}
1023 #if PATCH_AWESOMEBAR /* {{{ */
1024 	m->bt  = n;
1025 	m->btw = w;
1026 #endif /* }}} */
1027 #if PATCH_SYSTRAY /* {{{ */
1028 	drw_map(drw, m->barwin, 0, 0, m->ww - stw, bh);
1029 #else /* }}} */
1030 	drw_map(drw, m->barwin, 0, 0, m->ww, bh);
1031 #endif
1032 }
1033 
1034 void
1035 drawbars(void)
1036 {
1037 	Monitor *m;
1038 
1039 	for (m = mons; m; m = m->next)
1040 		drawbar(m);
1041 }
1042 
1043 void
1044 enternotify(XEvent *e)
1045 {
1046 	Client *c;
1047 	Monitor *m;
1048 	XCrossingEvent *ev = &e->xcrossing;
1049 
1050 	if ((ev->mode != NotifyNormal || ev->detail == NotifyInferior) && ev->window != root)
1051 		return;
1052 	c = wintoclient(ev->window);
1053 	m = c ? c->mon : wintomon(ev->window);
1054 	if (m != selmon) {
1055 		unfocus(selmon->sel, 1);
1056 		selmon = m;
1057 	} else if (!c || c == selmon->sel)
1058 		return;
1059 	focus(c);
1060 }
1061 
1062 void
1063 expose(XEvent *e)
1064 {
1065 	Monitor *m;
1066 	XExposeEvent *ev = &e->xexpose;
1067 
1068 #if PATCH_SYSTRAY /* {{{ */
1069 	if (ev->count == 0 && (m = wintomon(ev->window))) {
1070 		drawbar(m);
1071 		if (m == selmon)
1072 			updatesystray();
1073 	}
1074 #else /* }}} */
1075 	if (ev->count == 0 && (m = wintomon(ev->window)))
1076 		drawbar(m);
1077 #endif
1078 }
1079 
1080 void
1081 focus(Client *c)
1082 {
1083 	if (!c || !ISVISIBLE(c))
1084 		for (c = selmon->stack; c && !ISVISIBLE(c); c = c->snext);
1085 	if (selmon->sel && selmon->sel != c)
1086 		unfocus(selmon->sel, 0);
1087 	if (c) {
1088 		if (c->mon != selmon)
1089 			selmon = c->mon;
1090 		if (c->isurgent)
1091 			seturgent(c, 0);
1092 		detachstack(c);
1093 		attachstack(c);
1094 		grabbuttons(c, 1);
1095 		XSetWindowBorder(dpy, c->win, scheme[SchemeSel][ColBorder].pixel);
1096 		setfocus(c);
1097 	} else {
1098 		XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime);
1099 		XDeleteProperty(dpy, root, netatom[NetActiveWindow]);
1100 	}
1101 	selmon->sel = c;
1102 #if PATCH_FULLSCREENFOCUS /* {{{ */
1103 	for (c = selmon->clients; c && ISVISIBLE(c); c = c->next)
1104 		if (c->isfullscreen){
1105 			selmon->sel = c;
1106 			break;
1107 		}
1108 #endif /* }}} */
1109 	drawbars();
1110 }
1111 
1112 /* there are some broken focus acquiring clients needing extra handling */
1113 void
1114 focusin(XEvent *e)
1115 {
1116 	XFocusChangeEvent *ev = &e->xfocus;
1117 
1118 	if (selmon->sel && ev->window != selmon->sel->win)
1119 		setfocus(selmon->sel);
1120 }
1121 
1122 void
1123 focusmon(const Arg *arg)
1124 {
1125 	Monitor *m;
1126 
1127 	if (!mons->next)
1128 		return;
1129 	if ((m = dirtomon(arg->i)) == selmon)
1130 		return;
1131 	unfocus(selmon->sel, 0);
1132 	selmon = m;
1133 	focus(NULL);
1134 #if PATCH_WARP /* {{{ */
1135 	warp(selmon->sel);
1136 #endif /* }}} */
1137 }
1138 
1139 void
1140 focusstack(const Arg *arg)
1141 {
1142 	Client *c = NULL, *i;
1143 
1144 	if (!selmon->sel)
1145 		return;
1146 	if (arg->i > 0) {
1147 		for (c = selmon->sel->next; c && !ISVISIBLE(c); c = c->next);
1148 		if (!c)
1149 			for (c = selmon->clients; c && !ISVISIBLE(c); c = c->next);
1150 	} else {
1151 		for (i = selmon->clients; i != selmon->sel; i = i->next)
1152 			if (ISVISIBLE(i))
1153 				c = i;
1154 		if (!c)
1155 			for (; i; i = i->next)
1156 				if (ISVISIBLE(i))
1157 					c = i;
1158 	}
1159 	if (c) {
1160 		focus(c);
1161 		restack(selmon);
1162 	}
1163 }
1164 
1165 Atom
1166 getatomprop(Client *c, Atom prop)
1167 {
1168 	int di;
1169 	unsigned long dl;
1170 	unsigned char *p = NULL;
1171 	Atom da, atom = None;
1172 
1173 #if PATCH_SYSTRAY /* {{{ */
1174 	Atom req = XA_ATOM;
1175 	if (prop == xatom[XembedInfo])
1176 		req = xatom[XembedInfo];
1177 
1178 	if (XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, req,
1179 		&da, &di, &dl, &dl, &p) == Success && p) {
1180 		atom = *(Atom *)p;
1181 		if (da == xatom[XembedInfo] && dl == 2)
1182 			atom = ((Atom *)p)[1];
1183 		XFree(p);
1184 	}
1185 #else /* }}} */
1186 	if (XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, XA_ATOM,
1187 		&da, &di, &dl, &dl, &p) == Success && p) {
1188 		atom = *(Atom *)p;
1189 		XFree(p);
1190 	}
1191 #endif
1192 	return atom;
1193 }
1194 
1195 int
1196 getrootptr(int *x, int *y)
1197 {
1198 	int di;
1199 	unsigned int dui;
1200 	Window dummy;
1201 
1202 	return XQueryPointer(dpy, root, &dummy, &dummy, x, y, &di, &di, &dui);
1203 }
1204 
1205 long
1206 getstate(Window w)
1207 {
1208 	int format;
1209 	long result = -1;
1210 	unsigned char *p = NULL;
1211 	unsigned long n, extra;
1212 	Atom real;
1213 
1214 	if (XGetWindowProperty(dpy, w, wmatom[WMState], 0L, 2L, False, wmatom[WMState],
1215 		&real, &format, &n, &extra, (unsigned char **)&p) != Success)
1216 		return -1;
1217 	if (n != 0)
1218 		result = *p;
1219 	XFree(p);
1220 	return result;
1221 }
1222 
1223 int
1224 gettextprop(Window w, Atom atom, char *text, unsigned int size)
1225 {
1226 	char **list = NULL;
1227 	int n;
1228 	XTextProperty name;
1229 
1230 	if (!text || size == 0)
1231 		return 0;
1232 	text[0] = '\0';
1233 	if (!XGetTextProperty(dpy, w, &name, atom) || !name.nitems)
1234 		return 0;
1235 	if (name.encoding == XA_STRING)
1236 		strncpy(text, (char *)name.value, size - 1);
1237 	else {
1238 		if (XmbTextPropertyToTextList(dpy, &name, &list, &n) >= Success && n > 0 && *list) {
1239 			strncpy(text, *list, size - 1);
1240 			XFreeStringList(list);
1241 		}
1242 	}
1243 	text[size - 1] = '\0';
1244 	XFree(name.value);
1245 	return 1;
1246 }
1247 
1248 void
1249 grabbuttons(Client *c, int focused)
1250 {
1251 	updatenumlockmask();
1252 	{
1253 		unsigned int i, j;
1254 		unsigned int modifiers[] = { 0, LockMask, numlockmask, numlockmask|LockMask };
1255 		XUngrabButton(dpy, AnyButton, AnyModifier, c->win);
1256 		if (!focused)
1257 			XGrabButton(dpy, AnyButton, AnyModifier, c->win, False,
1258 				BUTTONMASK, GrabModeSync, GrabModeSync, None, None);
1259 		for (i = 0; i < LENGTH(buttons); i++)
1260 			if (buttons[i].click == ClkClientWin)
1261 				for (j = 0; j < LENGTH(modifiers); j++)
1262 					XGrabButton(dpy, buttons[i].button,
1263 						buttons[i].mask | modifiers[j],
1264 						c->win, False, BUTTONMASK,
1265 						GrabModeAsync, GrabModeSync, None, None);
1266 	}
1267 }
1268 
1269 void
1270 grabkeys(void)
1271 {
1272 	updatenumlockmask();
1273 	{
1274 		unsigned int i, j;
1275 		unsigned int modifiers[] = { 0, LockMask, numlockmask, numlockmask|LockMask };
1276 		KeyCode code;
1277 
1278 		XUngrabKey(dpy, AnyKey, AnyModifier, root);
1279 		for (i = 0; i < LENGTH(keys); i++)
1280 			if ((code = XKeysymToKeycode(dpy, keys[i].keysym)))
1281 				for (j = 0; j < LENGTH(modifiers); j++)
1282 					XGrabKey(dpy, code, keys[i].mod | modifiers[j], root,
1283 						True, GrabModeAsync, GrabModeAsync);
1284 	}
1285 }
1286 
1287 void
1288 incnmaster(const Arg *arg)
1289 {
1290 #if PATCH_PERTAG /* {{{ */
1291 	selmon->nmaster = selmon->pertag->nmasters[selmon->pertag->curtag] = MAX(selmon->nmaster + arg->i, 0);
1292 #else
1293 	selmon->nmaster = MAX(selmon->nmaster + arg->i, 0);
1294 #endif /* }}} */
1295 	arrange(selmon);
1296 }
1297 
1298 #ifdef XINERAMA
1299 static int
1300 isuniquegeom(XineramaScreenInfo *unique, size_t n, XineramaScreenInfo *info)
1301 {
1302 	while (n--)
1303 		if (unique[n].x_org == info->x_org && unique[n].y_org == info->y_org
1304 		&& unique[n].width == info->width && unique[n].height == info->height)
1305 			return 0;
1306 	return 1;
1307 }
1308 #endif /* XINERAMA */
1309 
1310 void
1311 keypress(XEvent *e)
1312 {
1313 	unsigned int i;
1314 	KeySym keysym;
1315 	XKeyEvent *ev;
1316 
1317 	ev = &e->xkey;
1318 	keysym = XKeycodeToKeysym(dpy, (KeyCode)ev->keycode, 0);
1319 	for (i = 0; i < LENGTH(keys); i++)
1320 		if (keysym == keys[i].keysym
1321 		&& CLEANMASK(keys[i].mod) == CLEANMASK(ev->state)
1322 		&& keys[i].func)
1323 			keys[i].func(&(keys[i].arg));
1324 }
1325 
1326 void
1327 killclient(const Arg *arg)
1328 {
1329 	if (!selmon->sel)
1330 		return;
1331 #if PATCH_SYSTRAY /* {{{ */
1332 	if (!sendevent(selmon->sel->win, wmatom[WMDelete], NoEventMask, wmatom[WMDelete], CurrentTime, 0 , 0, 0)) {
1333 #else /* }}} */
1334 	if (!sendevent(selmon->sel, wmatom[WMDelete])) {
1335 #endif
1336 		XGrabServer(dpy);
1337 		XSetErrorHandler(xerrordummy);
1338 		XSetCloseDownMode(dpy, DestroyAll);
1339 		XKillClient(dpy, selmon->sel->win);
1340 		XSync(dpy, False);
1341 		XSetErrorHandler(xerror);
1342 		XUngrabServer(dpy);
1343 	}
1344 }
1345 
1346 void
1347 manage(Window w, XWindowAttributes *wa)
1348 {
1349 	Client *c, *t = NULL;
1350 	Window trans = None;
1351 	XWindowChanges wc;
1352 
1353 	c = ecalloc(1, sizeof(Client));
1354 	c->win = w;
1355 	/* geometry */
1356 	c->x = c->oldx = wa->x;
1357 	c->y = c->oldy = wa->y;
1358 	c->w = c->oldw = wa->width;
1359 	c->h = c->oldh = wa->height;
1360 	c->oldbw = wa->border_width;
1361 
1362 	updatetitle(c);
1363 	if (XGetTransientForHint(dpy, w, &trans) && (t = wintoclient(trans))) {
1364 		c->mon = t->mon;
1365 		c->tags = t->tags;
1366 	} else {
1367 		c->mon = selmon;
1368 		applyrules(c);
1369 	}
1370 
1371 	if (c->x + WIDTH(c) > c->mon->mx + c->mon->mw)
1372 		c->x = c->mon->mx + c->mon->mw - WIDTH(c);
1373 	if (c->y + HEIGHT(c) > c->mon->my + c->mon->mh)
1374 		c->y = c->mon->my + c->mon->mh - HEIGHT(c);
1375 	c->x = MAX(c->x, c->mon->mx);
1376 	/* only fix client y-offset, if the client center might cover the bar */
1377 	c->y = MAX(c->y, ((c->mon->by == c->mon->my) && (c->x + (c->w / 2) >= c->mon->wx)
1378 		&& (c->x + (c->w / 2) < c->mon->wx + c->mon->ww)) ? bh : c->mon->my);
1379 	c->bw = borderpx;
1380 
1381 	wc.border_width = c->bw;
1382 	XConfigureWindow(dpy, w, CWBorderWidth, &wc);
1383 	XSetWindowBorder(dpy, w, scheme[SchemeNorm][ColBorder].pixel);
1384 	configure(c); /* propagates border_width, if size doesn't change */
1385 	updatewindowtype(c);
1386 	updatesizehints(c);
1387 	updatewmhints(c);
1388 #if PATCH_DECORATION_HINTS /* {{{ */
1389 	updatemotifhints(c);
1390 #endif /* }}} */
1391 #if PATCH_ALWAYSCENTER /* {{{ */
1392 	if (c->x < snap) c->x = c->mon->mx + (c->mon->mw - WIDTH(c) ) / 2;
1393 	if (c->y < snap) c->y = c->mon->my + (c->mon->mh - HEIGHT(c)) / 2;
1394 #endif /* }}} */
1395 	XSelectInput(dpy, w, EnterWindowMask|FocusChangeMask|PropertyChangeMask|StructureNotifyMask);
1396 	grabbuttons(c, 0);
1397 	if (!c->isfloating)
1398 		c->isfloating = c->oldstate = trans != None || c->isfixed;
1399 	if (c->isfloating)
1400 		XRaiseWindow(dpy, c->win);
1401 	attach(c);
1402 	attachstack(c);
1403 	XChangeProperty(dpy, root, netatom[NetClientList], XA_WINDOW, 32, PropModeAppend,
1404 		(unsigned char *) &(c->win), 1);
1405 	XMoveResizeWindow(dpy, c->win, c->x + 2 * sw, c->y, c->w, c->h); /* some windows require this */
1406 	setclientstate(c, NormalState);
1407 	if (c->mon == selmon)
1408 		unfocus(selmon->sel, 0);
1409 	c->mon->sel = c;
1410 	arrange(c->mon);
1411 	XMapWindow(dpy, c->win);
1412 	focus(NULL);
1413 }
1414 
1415 void
1416 mappingnotify(XEvent *e)
1417 {
1418 	XMappingEvent *ev = &e->xmapping;
1419 
1420 	XRefreshKeyboardMapping(ev);
1421 	if (ev->request == MappingKeyboard)
1422 		grabkeys();
1423 }
1424 
1425 void
1426 maprequest(XEvent *e)
1427 {
1428 	static XWindowAttributes wa;
1429 	XMapRequestEvent *ev = &e->xmaprequest;
1430 
1431 #if PATCH_SYSTRAY /* {{{ */
1432 	Client *i;
1433 	if ((i = wintosystrayicon(ev->window))) {
1434 		sendevent(i->win, netatom[Xembed], StructureNotifyMask, CurrentTime,
1435 		XEMBED_WINDOW_ACTIVATE, 0, systray->win, XEMBED_EMBEDDED_VERSION);
1436 		resizebarwin(selmon);
1437 		updatesystray();
1438 	}
1439 #endif /* }}} */
1440 	if (!XGetWindowAttributes(dpy, ev->window, &wa))
1441 		return;
1442 	if (wa.override_redirect)
1443 		return;
1444 	if (!wintoclient(ev->window))
1445 		manage(ev->window, &wa);
1446 }
1447 
1448 void
1449 monocle(Monitor *m)
1450 {
1451 	unsigned int n = 0;
1452 	Client *c;
1453 
1454 	for (c = m->clients; c; c = c->next)
1455 		if (ISVISIBLE(c))
1456 			n++;
1457 	if (n > 0) /* override layout symbol */
1458 		snprintf(m->ltsymbol, sizeof m->ltsymbol, "[%d]", n);
1459 	for (c = nexttiled(m->clients); c; c = nexttiled(c->next))
1460 		resize(c, m->wx, m->wy, m->ww - 2 * c->bw, m->wh - 2 * c->bw, 0);
1461 }
1462 
1463 void
1464 motionnotify(XEvent *e)
1465 {
1466 	static Monitor *mon = NULL;
1467 	Monitor *m;
1468 	XMotionEvent *ev = &e->xmotion;
1469 
1470 	if (ev->window != root)
1471 		return;
1472 	if ((m = recttomon(ev->x_root, ev->y_root, 1, 1)) != mon && mon) {
1473 		unfocus(selmon->sel, 1);
1474 		selmon = m;
1475 		focus(NULL);
1476 	}
1477 	mon = m;
1478 }
1479 
1480 void
1481 movemouse(const Arg *arg)
1482 {
1483 	int x, y, ocx, ocy, nx, ny;
1484 	Client *c;
1485 	Monitor *m;
1486 	XEvent ev;
1487 	Time lasttime = 0;
1488 
1489 	if (!(c = selmon->sel))
1490 		return;
1491 	if (c->isfullscreen) /* no support moving fullscreen windows by mouse */
1492 		return;
1493 	restack(selmon);
1494 	ocx = c->x;
1495 	ocy = c->y;
1496 	if (XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync,
1497 		None, cursor[CurMove]->cursor, CurrentTime) != GrabSuccess)
1498 		return;
1499 	if (!getrootptr(&x, &y))
1500 		return;
1501 	do {
1502 		XMaskEvent(dpy, MOUSEMASK|ExposureMask|SubstructureRedirectMask, &ev);
1503 		switch(ev.type) {
1504 		case ConfigureRequest:
1505 		case Expose:
1506 		case MapRequest:
1507 			handler[ev.type](&ev);
1508 			break;
1509 		case MotionNotify:
1510 			if ((ev.xmotion.time - lasttime) <= (1000 / 60))
1511 				continue;
1512 			lasttime = ev.xmotion.time;
1513 
1514 			nx = ocx + (ev.xmotion.x - x);
1515 			ny = ocy + (ev.xmotion.y - y);
1516 			if (abs(selmon->wx - nx) < snap)
1517 				nx = selmon->wx;
1518 			else if (abs((selmon->wx + selmon->ww) - (nx + WIDTH(c))) < snap)
1519 				nx = selmon->wx + selmon->ww - WIDTH(c);
1520 			if (abs(selmon->wy - ny) < snap)
1521 				ny = selmon->wy;
1522 			else if (abs((selmon->wy + selmon->wh) - (ny + HEIGHT(c))) < snap)
1523 				ny = selmon->wy + selmon->wh - HEIGHT(c);
1524 			if (!c->isfloating && selmon->lt[selmon->sellt]->arrange
1525 			&& (abs(nx - c->x) > snap || abs(ny - c->y) > snap))
1526 				togglefloating(NULL);
1527 			if (!selmon->lt[selmon->sellt]->arrange || c->isfloating)
1528 				resize(c, nx, ny, c->w, c->h, 1);
1529 			break;
1530 		}
1531 	} while (ev.type != ButtonRelease);
1532 	XUngrabPointer(dpy, CurrentTime);
1533 	if ((m = recttomon(c->x, c->y, c->w, c->h)) != selmon) {
1534 		sendmon(c, m);
1535 		selmon = m;
1536 		focus(NULL);
1537 	}
1538 }
1539 
1540 Client *
1541 nexttiled(Client *c)
1542 {
1543 	for (; c && (c->isfloating || !ISVISIBLE(c)); c = c->next);
1544 	return c;
1545 }
1546 
1547 void
1548 pop(Client *c)
1549 {
1550 	detach(c);
1551 	attach(c);
1552 	focus(c);
1553 	arrange(c->mon);
1554 }
1555 
1556 void
1557 propertynotify(XEvent *e)
1558 {
1559 	Client *c;
1560 	Window trans;
1561 	XPropertyEvent *ev = &e->xproperty;
1562 
1563 #if PATCH_SYSTRAY /* {{{ */
1564 	if ((c = wintosystrayicon(ev->window))) {
1565 		if (ev->atom == XA_WM_NORMAL_HINTS) {
1566 			updatesizehints(c);
1567 			updatesystrayicongeom(c, c->w, c->h);
1568 		} else
1569 			updatesystrayiconstate(c, ev);
1570 		resizebarwin(selmon);
1571 		updatesystray();
1572 	}
1573 #endif /* }}} */
1574 	if ((ev->window == root) && (ev->atom == XA_WM_NAME))
1575 		updatestatus();
1576 	else if (ev->state == PropertyDelete)
1577 		return; /* ignore */
1578 	else if ((c = wintoclient(ev->window))) {
1579 		switch(ev->atom) {
1580 		default: break;
1581 		case XA_WM_TRANSIENT_FOR:
1582 			if (!c->isfloating && (XGetTransientForHint(dpy, c->win, &trans)) &&
1583 				(c->isfloating = (wintoclient(trans)) != NULL))
1584 				arrange(c->mon);
1585 			break;
1586 		case XA_WM_NORMAL_HINTS:
1587 			updatesizehints(c);
1588 			break;
1589 		case XA_WM_HINTS:
1590 			updatewmhints(c);
1591 			drawbars();
1592 			break;
1593 		}
1594 		if (ev->atom == XA_WM_NAME || ev->atom == netatom[NetWMName]) {
1595 			updatetitle(c);
1596 			if (c == c->mon->sel)
1597 				drawbar(c->mon);
1598 		}
1599 		if (ev->atom == netatom[NetWMWindowType])
1600 			updatewindowtype(c);
1601 #if PATCH_DECORATION_HINTS /* {{{ */
1602 		if (ev->atom == motifatom)
1603 			updatemotifhints(c);
1604 #endif /* }}} */
1605 	}
1606 }
1607 
1608 void
1609 quit(const Arg *arg)
1610 {
1611 	running = 0;
1612 }
1613 
1614 Monitor *
1615 recttomon(int x, int y, int w, int h)
1616 {
1617 	Monitor *m, *r = selmon;
1618 	int a, area = 0;
1619 
1620 	for (m = mons; m; m = m->next)
1621 		if ((a = INTERSECT(x, y, w, h, m)) > area) {
1622 			area = a;
1623 			r = m;
1624 		}
1625 	return r;
1626 }
1627 
1628 void
1629 resize(Client *c, int x, int y, int w, int h, int interact)
1630 {
1631 	if (applysizehints(c, &x, &y, &w, &h, interact))
1632 		resizeclient(c, x, y, w, h);
1633 }
1634 
1635 void
1636 resizeclient(Client *c, int x, int y, int w, int h)
1637 {
1638 	XWindowChanges wc;
1639 
1640 	c->oldx = c->x; c->x = wc.x = x;
1641 	c->oldy = c->y; c->y = wc.y = y;
1642 	c->oldw = c->w; c->w = wc.width = w;
1643 	c->oldh = c->h; c->h = wc.height = h;
1644 	wc.border_width = c->bw;
1645 	XConfigureWindow(dpy, c->win, CWX|CWY|CWWidth|CWHeight|CWBorderWidth, &wc);
1646 	configure(c);
1647 	XSync(dpy, False);
1648 }
1649 
1650 void
1651 resizemouse(const Arg *arg)
1652 {
1653 	int ocx, ocy, nw, nh;
1654 	Client *c;
1655 	Monitor *m;
1656 	XEvent ev;
1657 	Time lasttime = 0;
1658 
1659 	if (!(c = selmon->sel))
1660 		return;
1661 	if (c->isfullscreen) /* no support resizing fullscreen windows by mouse */
1662 		return;
1663 	restack(selmon);
1664 	ocx = c->x;
1665 	ocy = c->y;
1666 	if (XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync,
1667 		None, cursor[CurResize]->cursor, CurrentTime) != GrabSuccess)
1668 		return;
1669 
1670 #if PATCH_RESIZECORNERS /* {{{ */
1671 	int horizcorner, vertcorner;
1672 	int ocx2, ocy2, nx, ny;
1673 	int di;
1674 	unsigned int dui;
1675 	Window dummy;
1676 	ocx2 = c->x + c->w;
1677 	ocy2 = c->y + c->h;
1678 	if (!XQueryPointer (dpy, c->win, &dummy, &dummy, &di, &di, &nx, &ny, &dui))
1679 		return;
1680 	horizcorner = nx < c->w / 2;
1681 	vertcorner  = ny < c->h / 2;
1682 	XWarpPointer (dpy, None, c->win, 0, 0, 0, 0,
1683 			horizcorner ? (-c->bw) : (c->w + c->bw -1),
1684 			vertcorner  ? (-c->bw) : (c->h + c->bw -1));
1685 #else /* }}} */
1686 	XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->bw - 1, c->h + c->bw - 1);
1687 #endif
1688 	do {
1689 		XMaskEvent(dpy, MOUSEMASK|ExposureMask|SubstructureRedirectMask, &ev);
1690 		switch(ev.type) {
1691 		case ConfigureRequest:
1692 		case Expose:
1693 		case MapRequest:
1694 			handler[ev.type](&ev);
1695 			break;
1696 		case MotionNotify:
1697 			if ((ev.xmotion.time - lasttime) <= (1000 / 60))
1698 				continue;
1699 			lasttime = ev.xmotion.time;
1700 
1701 			nw = MAX(ev.xmotion.x - ocx - 2 * c->bw + 1, 1);
1702 			nh = MAX(ev.xmotion.y - ocy - 2 * c->bw + 1, 1);
1703 #if PATCH_RESIZECORNERS /* {{{ */
1704 			nx = horizcorner ? ev.xmotion.x : c->x;
1705 			ny = vertcorner ? ev.xmotion.y : c->y;
1706 			nw = MAX(horizcorner ? (ocx2 - nx) : (ev.xmotion.x - ocx - 2 * c->bw + 1), 1);
1707 			nh = MAX(vertcorner ? (ocy2 - ny) : (ev.xmotion.y - ocy - 2 * c->bw + 1), 1);
1708 #endif /* }}} */
1709 
1710 			if (c->mon->wx + nw >= selmon->wx && c->mon->wx + nw <= selmon->wx + selmon->ww
1711 			&& c->mon->wy + nh >= selmon->wy && c->mon->wy + nh <= selmon->wy + selmon->wh)
1712 			{
1713 				if (!c->isfloating && selmon->lt[selmon->sellt]->arrange
1714 				&& (abs(nw - c->w) > snap || abs(nh - c->h) > snap))
1715 					togglefloating(NULL);
1716 			}
1717 			if (!selmon->lt[selmon->sellt]->arrange || c->isfloating)
1718 #if PATCH_RESIZECORNERS /* {{{ */
1719 				resize(c, nx, ny, nw, nh, 1);
1720 #else /* }}} */
1721 				resize(c, c->x, c->y, nw, nh, 1);
1722 #endif
1723 			break;
1724 		}
1725 	} while (ev.type != ButtonRelease);
1726 #if PATCH_RESIZECORNERS /* {{{ */
1727 	XWarpPointer(dpy, None, c->win, 0, 0, 0, 0,
1728 		      horizcorner ? (-c->bw) : (c->w + c->bw - 1),
1729 		      vertcorner ? (-c->bw) : (c->h + c->bw - 1));
1730 #else /* }}} */
1731 	XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->bw - 1, c->h + c->bw - 1);
1732 #endif
1733 	XUngrabPointer(dpy, CurrentTime);
1734 	while (XCheckMaskEvent(dpy, EnterWindowMask, &ev));
1735 	if ((m = recttomon(c->x, c->y, c->w, c->h)) != selmon) {
1736 		sendmon(c, m);
1737 		selmon = m;
1738 		focus(NULL);
1739 	}
1740 }
1741 
1742 void
1743 restack(Monitor *m)
1744 {
1745 	Client *c;
1746 	XEvent ev;
1747 	XWindowChanges wc;
1748 
1749 	drawbar(m);
1750 	if (!m->sel)
1751 		return;
1752 	if (m->sel->isfloating || !m->lt[m->sellt]->arrange)
1753 		XRaiseWindow(dpy, m->sel->win);
1754 	if (m->lt[m->sellt]->arrange) {
1755 		wc.stack_mode = Below;
1756 		wc.sibling = m->barwin;
1757 		for (c = m->stack; c; c = c->snext)
1758 			if (!c->isfloating && ISVISIBLE(c)) {
1759 				XConfigureWindow(dpy, c->win, CWSibling|CWStackMode, &wc);
1760 				wc.sibling = c->win;
1761 			}
1762 	}
1763 	XSync(dpy, False);
1764 	while (XCheckMaskEvent(dpy, EnterWindowMask, &ev));
1765 #if PATCH_WARP /* {{{ */
1766 	if (m == selmon && (m->tagset[m->seltags] & m->sel->tags) && selmon->lt[selmon->sellt] != &layouts[2])
1767 		warp(m->sel);
1768 #endif /* }}} */
1769 }
1770 
1771 void
1772 run(void)
1773 {
1774 	XEvent ev;
1775 	/* main event loop */
1776 	XSync(dpy, False);
1777 	while (running && !XNextEvent(dpy, &ev))
1778 		if (handler[ev.type])
1779 			handler[ev.type](&ev); /* call handler */
1780 }
1781 
1782 void
1783 scan(void)
1784 {
1785 	unsigned int i, num;
1786 	Window d1, d2, *wins = NULL;
1787 	XWindowAttributes wa;
1788 
1789 	if (XQueryTree(dpy, root, &d1, &d2, &wins, &num)) {
1790 		for (i = 0; i < num; i++) {
1791 			if (!XGetWindowAttributes(dpy, wins[i], &wa)
1792 			|| wa.override_redirect || XGetTransientForHint(dpy, wins[i], &d1))
1793 				continue;
1794 			if (wa.map_state == IsViewable || getstate(wins[i]) == IconicState)
1795 				manage(wins[i], &wa);
1796 		}
1797 		for (i = 0; i < num; i++) { /* now the transients */
1798 			if (!XGetWindowAttributes(dpy, wins[i], &wa))
1799 				continue;
1800 			if (XGetTransientForHint(dpy, wins[i], &d1)
1801 			&& (wa.map_state == IsViewable || getstate(wins[i]) == IconicState))
1802 				manage(wins[i], &wa);
1803 		}
1804 		if (wins)
1805 			XFree(wins);
1806 	}
1807 }
1808 
1809 void
1810 sendmon(Client *c, Monitor *m)
1811 {
1812 	if (c->mon == m)
1813 		return;
1814 	unfocus(c, 1);
1815 	detach(c);
1816 	detachstack(c);
1817 	c->mon = m;
1818 	c->tags = m->tagset[m->seltags]; /* assign tags of target monitor */
1819 	attach(c);
1820 	attachstack(c);
1821 	focus(NULL);
1822 	arrange(NULL);
1823 }
1824 
1825 void
1826 setclientstate(Client *c, long state)
1827 {
1828 	long data[] = { state, None };
1829 
1830 	XChangeProperty(dpy, c->win, wmatom[WMState], wmatom[WMState], 32,
1831 		PropModeReplace, (unsigned char *)data, 2);
1832 }
1833 
1834 #if PATCH_SYSTRAY /* {{{ */
1835 int
1836 sendevent(Window w, Atom proto, int mask, long d0, long d1, long d2, long d3, long d4)
1837 {
1838 	int n;
1839 	Atom *protocols;
1840 	int exists = 0;
1841 	XEvent ev;
1842 	Atom mt;
1843 
1844 	if (proto == wmatom[WMTakeFocus] || proto == wmatom[WMDelete]) {
1845 		mt = wmatom[WMProtocols];
1846 		if (XGetWMProtocols(dpy, w, &protocols, &n)) {
1847 			while (!exists && n--)
1848 				exists = protocols[n] == proto;
1849 			XFree(protocols);
1850 		}
1851 	} else {
1852 		exists = True;
1853 		mt = proto;
1854 	}
1855 	if (exists) {
1856 		ev.type = ClientMessage;
1857 		ev.xclient.window = w;
1858 		ev.xclient.message_type = mt;
1859 		ev.xclient.format = 32;
1860 		ev.xclient.data.l[0] = d0;
1861 		ev.xclient.data.l[1] = d1;
1862 		ev.xclient.data.l[2] = d2;
1863 		ev.xclient.data.l[3] = d3;
1864 		ev.xclient.data.l[4] = d4;
1865 		XSendEvent(dpy, w, False, mask, &ev);
1866 	}
1867 
1868 	return exists;
1869 }
1870 #else /* }}} */
1871 int
1872 sendevent(Client *c, Atom proto)
1873 {
1874 	int n;
1875 	Atom *protocols;
1876 	int exists = 0;
1877 	XEvent ev;
1878 
1879 	if (XGetWMProtocols(dpy, c->win, &protocols, &n)) {
1880 		while (!exists && n--)
1881 			exists = protocols[n] == proto;
1882 		XFree(protocols);
1883 	}
1884 	if (exists) {
1885 		ev.type = ClientMessage;
1886 		ev.xclient.window = c->win;
1887 		ev.xclient.message_type = wmatom[WMProtocols];
1888 		ev.xclient.format = 32;
1889 		ev.xclient.data.l[0] = proto;
1890 		ev.xclient.data.l[1] = CurrentTime;
1891 		XSendEvent(dpy, c->win, False, NoEventMask, &ev);
1892 	}
1893 	return exists;
1894 }
1895 #endif
1896 
1897 void
1898 setfocus(Client *c)
1899 {
1900 	if (!c->neverfocus) {
1901 		XSetInputFocus(dpy, c->win, RevertToPointerRoot, CurrentTime);
1902 		XChangeProperty(dpy, root, netatom[NetActiveWindow],
1903 			XA_WINDOW, 32, PropModeReplace,
1904 			(unsigned char *) &(c->win), 1);
1905 	}
1906 #if PATCH_SYSTRAY /* {{{ */
1907 	sendevent(c->win, wmatom[WMTakeFocus], NoEventMask, wmatom[WMTakeFocus], CurrentTime, 0, 0, 0);
1908 #else /* }}} */
1909 	sendevent(c, wmatom[WMTakeFocus]);
1910 #endif
1911 }
1912 
1913 void
1914 setfullscreen(Client *c, int fullscreen)
1915 {
1916 	if (fullscreen && !c->isfullscreen) {
1917 		XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32,
1918 			PropModeReplace, (unsigned char*)&netatom[NetWMFullscreen], 1);
1919 		c->isfullscreen = 1;
1920 		c->oldstate = c->isfloating;
1921 		c->oldbw = c->bw;
1922 		c->bw = 0;
1923 		c->isfloating = 1;
1924 		resizeclient(c, c->mon->mx, c->mon->my, c->mon->mw, c->mon->mh);
1925 		XRaiseWindow(dpy, c->win);
1926 	} else if (!fullscreen && c->isfullscreen){
1927 		XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32,
1928 			PropModeReplace, (unsigned char*)0, 0);
1929 		c->isfullscreen = 0;
1930 		c->isfloating = c->oldstate;
1931 		c->bw = c->oldbw;
1932 		c->x = c->oldx;
1933 		c->y = c->oldy;
1934 		c->w = c->oldw;
1935 		c->h = c->oldh;
1936 		resizeclient(c, c->x, c->y, c->w, c->h);
1937 		arrange(c->mon);
1938 	}
1939 }
1940 
1941 void
1942 setlayout(const Arg *arg)
1943 {
1944 #if PATCH_PERTAG /* {{{ */
1945 	if (!arg || !arg->v || arg->v != selmon->lt[selmon->sellt])
1946 		selmon->sellt = selmon->pertag->sellts[selmon->pertag->curtag] ^= 1;
1947 	if (arg && arg->v)
1948 		selmon->lt[selmon->sellt] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt] = (Layout *)arg->v;
1949 #else
1950 	if (!arg || !arg->v || arg->v != selmon->lt[selmon->sellt])
1951 		selmon->sellt ^= 1;
1952 	if (arg && arg->v)
1953 		selmon->lt[selmon->sellt] = (Layout *)arg->v;
1954 #endif /* }}} */
1955 	strncpy(selmon->ltsymbol, selmon->lt[selmon->sellt]->symbol, sizeof selmon->ltsymbol);
1956 	if (selmon->sel)
1957 		arrange(selmon);
1958 	else
1959 		drawbar(selmon);
1960 }
1961 
1962 /* arg > 1.0 will set mfact absolutely */
1963 void
1964 setmfact(const Arg *arg)
1965 {
1966 	float f;
1967 
1968 	if (!arg || !selmon->lt[selmon->sellt]->arrange)
1969 		return;
1970 	f = arg->f < 1.0 ? arg->f + selmon->mfact : arg->f - 1.0;
1971 	if (f < 0.05 || f > 0.95)
1972 		return;
1973 #if PATCH_PERTAG /* {{{ */
1974 	selmon->mfact = selmon->pertag->mfacts[selmon->pertag->curtag] = f;
1975 #else
1976 	selmon->mfact = f;
1977 #endif /* }}} */
1978 	arrange(selmon);
1979 }
1980 
1981 void
1982 setup(void)
1983 {
1984 	int i;
1985 	XSetWindowAttributes wa;
1986 	Atom utf8string;
1987 
1988 	/* clean up any zombies immediately */
1989 	sigchld(0);
1990 
1991 	/* init screen */
1992 	screen = DefaultScreen(dpy);
1993 	sw = DisplayWidth(dpy, screen);
1994 	sh = DisplayHeight(dpy, screen);
1995 	root = RootWindow(dpy, screen);
1996 	drw = drw_create(dpy, screen, root, sw, sh);
1997 	if (!drw_fontset_create(drw, fonts, LENGTH(fonts)))
1998 		die("no fonts could be loaded.");
1999 	lrpad = drw->fonts->h;
2000 	bh = drw->fonts->h + 2;
2001 	updategeom();
2002 	/* init atoms */
2003 	utf8string = XInternAtom(dpy, "UTF8_STRING", False);
2004 	wmatom[WMProtocols] = XInternAtom(dpy, "WM_PROTOCOLS", False);
2005 	wmatom[WMDelete] = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
2006 	wmatom[WMState] = XInternAtom(dpy, "WM_STATE", False);
2007 	wmatom[WMTakeFocus] = XInternAtom(dpy, "WM_TAKE_FOCUS", False);
2008 	netatom[NetActiveWindow] = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", False);
2009 	netatom[NetSupported] = XInternAtom(dpy, "_NET_SUPPORTED", False);
2010 	netatom[NetWMName] = XInternAtom(dpy, "_NET_WM_NAME", False);
2011 	netatom[NetWMState] = XInternAtom(dpy, "_NET_WM_STATE", False);
2012 	netatom[NetWMCheck] = XInternAtom(dpy, "_NET_SUPPORTING_WM_CHECK", False);
2013 	netatom[NetWMFullscreen] = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False);
2014 	netatom[NetWMWindowType] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False);
2015 	netatom[NetWMWindowTypeDialog] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DIALOG", False);
2016 	netatom[NetClientList] = XInternAtom(dpy, "_NET_CLIENT_LIST", False);
2017 #if PATCH_DECORATION_HINTS /* {{{ */
2018 	motifatom = XInternAtom(dpy, "_MOTIF_WM_HINTS", False);
2019 	cursor[CurNormal] = drw_cur_create(drw, XC_left_ptr);
2020 	cursor[CurResize] = drw_cur_create(drw, XC_sizing);
2021 #endif /* }}} */
2022 #if PATCH_SYSTRAY /* {{{ */
2023 	netatom[NetSystemTray] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_S0", False);
2024 	netatom[NetSystemTrayOP] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_OPCODE", False);
2025 	netatom[NetSystemTrayOrientation] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_ORIENTATION", False);
2026 	netatom[NetSystemTrayOrientationHorz] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_ORIENTATION_HORZ", False);
2027 	xatom[Manager] = XInternAtom(dpy, "MANAGER", False);
2028 	xatom[Xembed] = XInternAtom(dpy, "_XEMBED", False);
2029 	xatom[XembedInfo] = XInternAtom(dpy, "_XEMBED_INFO", False);
2030 #endif /* }}} */
2031 	/* init cursors */
2032 	cursor[CurNormal] = drw_cur_create(drw, XC_left_ptr);
2033 	cursor[CurResize] = drw_cur_create(drw, XC_sizing);
2034 	cursor[CurMove] = drw_cur_create(drw, XC_fleur);
2035 	/* init appearance */
2036 	scheme = ecalloc(LENGTH(colors), sizeof(Clr *));
2037 	for (i = 0; i < LENGTH(colors); i++)
2038 		scheme[i] = drw_scm_create(drw, colors[i], 3);
2039 #if PATCH_SYSTRAY /* {{{ */
2040 	/* init system tray */
2041 	updatesystray();
2042 #endif /* }}} */
2043 	/* init bars */
2044 	updatebars();
2045 	updatestatus();
2046 	/* supporting window for NetWMCheck */
2047 	wmcheckwin = XCreateSimpleWindow(dpy, root, 0, 0, 1, 1, 0, 0, 0);
2048 	XChangeProperty(dpy, wmcheckwin, netatom[NetWMCheck], XA_WINDOW, 32,
2049 		PropModeReplace, (unsigned char *) &wmcheckwin, 1);
2050 	XChangeProperty(dpy, wmcheckwin, netatom[NetWMName], utf8string, 8,
2051 		PropModeReplace, (unsigned char *) "dwm", 3);
2052 	XChangeProperty(dpy, root, netatom[NetWMCheck], XA_WINDOW, 32,
2053 		PropModeReplace, (unsigned char *) &wmcheckwin, 1);
2054 	/* EWMH support per view */
2055 	XChangeProperty(dpy, root, netatom[NetSupported], XA_ATOM, 32,
2056 		PropModeReplace, (unsigned char *) netatom, NetLast);
2057 	XDeleteProperty(dpy, root, netatom[NetClientList]);
2058 	/* select events */
2059 	wa.cursor = cursor[CurNormal]->cursor;
2060 	wa.event_mask = SubstructureRedirectMask|SubstructureNotifyMask
2061 		|ButtonPressMask|PointerMotionMask|EnterWindowMask
2062 		|LeaveWindowMask|StructureNotifyMask|PropertyChangeMask;
2063 	XChangeWindowAttributes(dpy, root, CWEventMask|CWCursor, &wa);
2064 	XSelectInput(dpy, root, wa.event_mask);
2065 	grabkeys();
2066 	focus(NULL);
2067 }
2068 
2069 
2070 void
2071 seturgent(Client *c, int urg)
2072 {
2073 	XWMHints *wmh;
2074 
2075 	c->isurgent = urg;
2076 	if (!(wmh = XGetWMHints(dpy, c->win)))
2077 		return;
2078 	wmh->flags = urg ? (wmh->flags | XUrgencyHint) : (wmh->flags & ~XUrgencyHint);
2079 	XSetWMHints(dpy, c->win, wmh);
2080 	XFree(wmh);
2081 }
2082 
2083 void
2084 showhide(Client *c)
2085 {
2086 	if (!c)
2087 		return;
2088 	if (ISVISIBLE(c)) {
2089 		/* show clients top down */
2090 		XMoveWindow(dpy, c->win, c->x, c->y);
2091 		if ((!c->mon->lt[c->mon->sellt]->arrange || c->isfloating) && !c->isfullscreen)
2092 			resize(c, c->x, c->y, c->w, c->h, 0);
2093 		showhide(c->snext);
2094 	} else {
2095 		/* hide clients bottom up */
2096 		showhide(c->snext);
2097 		XMoveWindow(dpy, c->win, WIDTH(c) * -2, c->y);
2098 	}
2099 }
2100 
2101 void
2102 sigchld(int unused)
2103 {
2104 	if (signal(SIGCHLD, sigchld) == SIG_ERR)
2105 		die("can't install SIGCHLD handler:");
2106 	while (0 < waitpid(-1, NULL, WNOHANG));
2107 }
2108 
2109 void
2110 spawn(const Arg *arg)
2111 {
2112 	if (arg->v == dmenucmd)
2113 		dmenumon[0] = '0' + selmon->num;
2114 	if (fork() == 0) {
2115 		if (dpy)
2116 			close(ConnectionNumber(dpy));
2117 		setsid();
2118 		execvp(((char **)arg->v)[0], (char **)arg->v);
2119 		fprintf(stderr, "dwm: execvp %s", ((char **)arg->v)[0]);
2120 		perror(" failed");
2121 		exit(EXIT_SUCCESS);
2122 	}
2123 }
2124 
2125 void
2126 tag(const Arg *arg)
2127 {
2128 	if (selmon->sel && arg->ui & TAGMASK) {
2129 		selmon->sel->tags = arg->ui & TAGMASK;
2130 		focus(NULL);
2131 		arrange(selmon);
2132 	}
2133 }
2134 
2135 void
2136 tagmon(const Arg *arg)
2137 {
2138 	if (!selmon->sel || !mons->next)
2139 		return;
2140 	sendmon(selmon->sel, dirtomon(arg->i));
2141 }
2142 
2143 void
2144 tile(Monitor *m)
2145 {
2146 	unsigned int i, n, h, mw, my, ty;
2147 	Client *c;
2148 
2149 	for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++);
2150 	if (n == 0)
2151 		return;
2152 
2153 	if (n > m->nmaster)
2154 		mw = m->nmaster ? m->ww * m->mfact : 0;
2155 	else
2156 		mw = m->ww;
2157 	for (i = my = ty = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++)
2158 		if (i < m->nmaster) {
2159 			h = (m->wh - my) / (MIN(n, m->nmaster) - i);
2160 			resize(c, m->wx, m->wy + my, mw - (2*c->bw), h - (2*c->bw), 0);
2161 			if (my + HEIGHT(c) < m->wh)
2162 				my += HEIGHT(c);
2163 		} else {
2164 			h = (m->wh - ty) / (n - i);
2165 			resize(c, m->wx + mw, m->wy + ty, m->ww - mw - (2*c->bw), h - (2*c->bw), 0);
2166 			if (ty + HEIGHT(c) < m->wh)
2167 				ty += HEIGHT(c);
2168 		}
2169 }
2170 
2171 void
2172 togglebar(const Arg *arg)
2173 {
2174 #if PATCH_PERTAG /* {{{ */
2175 	selmon->showbar = selmon->pertag->showbars[selmon->pertag->curtag] = !selmon->showbar;
2176 #else
2177 	selmon->showbar = !selmon->showbar;
2178 #endif /* }}} */
2179 	updatebarpos(selmon);
2180 #if PATCH_SYSTRAY /* {{{ */
2181 	resizebarwin(selmon);
2182 	XWindowChanges wc;
2183 	if (!selmon->showbar)
2184 		wc.y = -bh;
2185 	else if (selmon->showbar) {
2186 		wc.y = 0;
2187 		if (!selmon->topbar)
2188 			wc.y = selmon->mh - bh;
2189 	}
2190 	XConfigureWindow(dpy, systray->win, CWY, &wc);
2191 #else /* }}} */
2192 	XMoveResizeWindow(dpy, selmon->barwin, selmon->wx, selmon->by, selmon->ww, bh);
2193 #endif
2194 	arrange(selmon);
2195 }
2196 
2197 void
2198 togglefloating(const Arg *arg)
2199 {
2200 	if (!selmon->sel)
2201 		return;
2202 	if (selmon->sel->isfullscreen) /* no support for fullscreen windows */
2203 		return;
2204 	selmon->sel->isfloating = !selmon->sel->isfloating || selmon->sel->isfixed;
2205 	if (selmon->sel->isfloating)
2206 		resize(selmon->sel, selmon->sel->x, selmon->sel->y,
2207 			selmon->sel->w, selmon->sel->h, 0);
2208 	arrange(selmon);
2209 }
2210 
2211 void
2212 toggletag(const Arg *arg)
2213 {
2214 	unsigned int newtags;
2215 
2216 	if (!selmon->sel)
2217 		return;
2218 	newtags = selmon->sel->tags ^ (arg->ui & TAGMASK);
2219 	if (newtags) {
2220 		selmon->sel->tags = newtags;
2221 		focus(NULL);
2222 		arrange(selmon);
2223 	}
2224 }
2225 
2226 void
2227 toggleview(const Arg *arg)
2228 {
2229 	unsigned int newtagset = selmon->tagset[selmon->seltags] ^ (arg->ui & TAGMASK);
2230 
2231 	if (newtagset) {
2232 		selmon->tagset[selmon->seltags] = newtagset;
2233 #if PATCH_PERTAG /* {{{ */
2234 		int i;
2235 
2236 		if (newtagset == ~0) {
2237 			selmon->pertag->prevtag = selmon->pertag->curtag;
2238 			selmon->pertag->curtag = 0;
2239 		}
2240 
2241 		/* test if the user did not select the same tag */
2242 		if (!(newtagset & 1 << (selmon->pertag->curtag - 1))) {
2243 			selmon->pertag->prevtag = selmon->pertag->curtag;
2244 			for (i = 0; !(newtagset & 1 << i); i++) ;
2245 			selmon->pertag->curtag = i + 1;
2246 		}
2247 
2248 		/* apply settings for this view */
2249 		selmon->nmaster = selmon->pertag->nmasters[selmon->pertag->curtag];
2250 		selmon->mfact = selmon->pertag->mfacts[selmon->pertag->curtag];
2251 		selmon->sellt = selmon->pertag->sellts[selmon->pertag->curtag];
2252 		selmon->lt[selmon->sellt] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt];
2253 		selmon->lt[selmon->sellt^1] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt^1];
2254 
2255 		if (selmon->showbar != selmon->pertag->showbars[selmon->pertag->curtag])
2256 			togglebar(NULL);
2257 #endif /* }}} */
2258 		focus(NULL);
2259 		arrange(selmon);
2260 	}
2261 }
2262 
2263 void
2264 unfocus(Client *c, int setfocus)
2265 {
2266 	if (!c)
2267 		return;
2268 	grabbuttons(c, 0);
2269 	XSetWindowBorder(dpy, c->win, scheme[SchemeNorm][ColBorder].pixel);
2270 	if (setfocus) {
2271 		XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime);
2272 		XDeleteProperty(dpy, root, netatom[NetActiveWindow]);
2273 	}
2274 }
2275 
2276 void
2277 unmanage(Client *c, int destroyed)
2278 {
2279 	Monitor *m = c->mon;
2280 	XWindowChanges wc;
2281 
2282 	detach(c);
2283 	detachstack(c);
2284 	if (!destroyed) {
2285 		wc.border_width = c->oldbw;
2286 		XGrabServer(dpy); /* avoid race conditions */
2287 		XSetErrorHandler(xerrordummy);
2288 		XConfigureWindow(dpy, c->win, CWBorderWidth, &wc); /* restore border */
2289 		XUngrabButton(dpy, AnyButton, AnyModifier, c->win);
2290 		setclientstate(c, WithdrawnState);
2291 		XSync(dpy, False);
2292 		XSetErrorHandler(xerror);
2293 		XUngrabServer(dpy);
2294 	}
2295 	free(c);
2296 	focus(NULL);
2297 	updateclientlist();
2298 	arrange(m);
2299 }
2300 
2301 void
2302 unmapnotify(XEvent *e)
2303 {
2304 	Client *c;
2305 	XUnmapEvent *ev = &e->xunmap;
2306 
2307 	if ((c = wintoclient(ev->window))) {
2308 		if (ev->send_event)
2309 			setclientstate(c, WithdrawnState);
2310 		else
2311 			unmanage(c, 0);
2312 	}
2313 #if PATCH_SYSTRAY /* {{{ */
2314 	else if ((c = wintosystrayicon(ev->window))) {
2315 		/* KLUDGE! sometimes icons occasionally unmap their windows, but do
2316 		 * _not_ destroy them. We map those windows back */
2317 		XMapRaised(dpy, c->win);
2318 		updatesystray();
2319 	}
2320 #endif /* }}} */
2321 }
2322 
2323 void
2324 updatebars(void)
2325 {
2326 	Monitor *m;
2327 	XSetWindowAttributes wa = {
2328 		.override_redirect = True,
2329 		.background_pixmap = ParentRelative,
2330 		.event_mask = ButtonPressMask|ExposureMask
2331 	};
2332 	XClassHint ch = {"dwm", "dwm"};
2333 	for (m = mons; m; m = m->next) {
2334 		if (m->barwin)
2335 			continue;
2336 #if PATCH_SYSTRAY /* {{{ */
2337 		unsigned int w;
2338 		w = m->ww;
2339 		if (m == systraytomon(m))
2340 			w -= getsystraywidth();
2341 		m->barwin = XCreateWindow(dpy, root, m->wx, m->by, w, bh, 0, DefaultDepth(dpy, screen),
2342 				CopyFromParent, DefaultVisual(dpy, screen),
2343 				CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa);
2344 		if (m == systraytomon(m))
2345 			XMapRaised(dpy, systray->win);
2346 #else /* }}} */
2347 		m->barwin = XCreateWindow(dpy, root, m->wx, m->by, m->ww, bh, 0, DefaultDepth(dpy, screen),
2348 				CopyFromParent, DefaultVisual(dpy, screen),
2349 				CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa);
2350 #endif
2351 		XDefineCursor(dpy, m->barwin, cursor[CurNormal]->cursor);
2352 		XMapRaised(dpy, m->barwin);
2353 		XSetClassHint(dpy, m->barwin, &ch);
2354 	}
2355 }
2356 
2357 void
2358 updatebarpos(Monitor *m)
2359 {
2360 	m->wy = m->my;
2361 	m->wh = m->mh;
2362 	if (m->showbar) {
2363 		m->wh -= bh;
2364 		m->by = m->topbar ? m->wy : m->wy + m->wh;
2365 		m->wy = m->topbar ? m->wy + bh : m->wy;
2366 	} else
2367 		m->by = -bh;
2368 }
2369 
2370 void
2371 updateclientlist()
2372 {
2373 	Client *c;
2374 	Monitor *m;
2375 
2376 	XDeleteProperty(dpy, root, netatom[NetClientList]);
2377 	for (m = mons; m; m = m->next)
2378 		for (c = m->clients; c; c = c->next)
2379 			XChangeProperty(dpy, root, netatom[NetClientList],
2380 				XA_WINDOW, 32, PropModeAppend,
2381 				(unsigned char *) &(c->win), 1);
2382 }
2383 
2384 int
2385 updategeom(void)
2386 {
2387 	int dirty = 0;
2388 
2389 #ifdef XINERAMA
2390 	if (XineramaIsActive(dpy)) {
2391 		int i, j, n, nn;
2392 		Client *c;
2393 		Monitor *m;
2394 		XineramaScreenInfo *info = XineramaQueryScreens(dpy, &nn);
2395 		XineramaScreenInfo *unique = NULL;
2396 
2397 		for (n = 0, m = mons; m; m = m->next, n++);
2398 		/* only consider unique geometries as separate screens */
2399 		unique = ecalloc(nn, sizeof(XineramaScreenInfo));
2400 		for (i = 0, j = 0; i < nn; i++)
2401 			if (isuniquegeom(unique, j, &info[i]))
2402 				memcpy(&unique[j++], &info[i], sizeof(XineramaScreenInfo));
2403 		XFree(info);
2404 		nn = j;
2405 		if (n <= nn) { /* new monitors available */
2406 			for (i = 0; i < (nn - n); i++) {
2407 				for (m = mons; m && m->next; m = m->next);
2408 				if (m)
2409 					m->next = createmon();
2410 				else
2411 					mons = createmon();
2412 			}
2413 			for (i = 0, m = mons; i < nn && m; m = m->next, i++)
2414 				if (i >= n
2415 				|| unique[i].x_org != m->mx || unique[i].y_org != m->my
2416 				|| unique[i].width != m->mw || unique[i].height != m->mh)
2417 				{
2418 					dirty = 1;
2419 					m->num = i;
2420 					m->mx = m->wx = unique[i].x_org;
2421 					m->my = m->wy = unique[i].y_org;
2422 					m->mw = m->ww = unique[i].width;
2423 					m->mh = m->wh = unique[i].height;
2424 					updatebarpos(m);
2425 				}
2426 		} else { /* less monitors available nn < n */
2427 			for (i = nn; i < n; i++) {
2428 				for (m = mons; m && m->next; m = m->next);
2429 				while ((c = m->clients)) {
2430 					dirty = 1;
2431 					m->clients = c->next;
2432 					detachstack(c);
2433 					c->mon = mons;
2434 					attach(c);
2435 					attachstack(c);
2436 				}
2437 				if (m == selmon)
2438 					selmon = mons;
2439 				cleanupmon(m);
2440 			}
2441 		}
2442 		free(unique);
2443 	} else
2444 #endif /* XINERAMA */
2445 	{ /* default monitor setup */
2446 		if (!mons)
2447 			mons = createmon();
2448 		if (mons->mw != sw || mons->mh != sh) {
2449 			dirty = 1;
2450 			mons->mw = mons->ww = sw;
2451 			mons->mh = mons->wh = sh;
2452 			updatebarpos(mons);
2453 		}
2454 	}
2455 	if (dirty) {
2456 		selmon = mons;
2457 		selmon = wintomon(root);
2458 	}
2459 	return dirty;
2460 }
2461 
2462 void
2463 updatenumlockmask(void)
2464 {
2465 	unsigned int i, j;
2466 	XModifierKeymap *modmap;
2467 
2468 	numlockmask = 0;
2469 	modmap = XGetModifierMapping(dpy);
2470 	for (i = 0; i < 8; i++)
2471 		for (j = 0; j < modmap->max_keypermod; j++)
2472 			if (modmap->modifiermap[i * modmap->max_keypermod + j]
2473 				== XKeysymToKeycode(dpy, XK_Num_Lock))
2474 				numlockmask = (1 << i);
2475 	XFreeModifiermap(modmap);
2476 }
2477 
2478 void
2479 updatesizehints(Client *c)
2480 {
2481 	long msize;
2482 	XSizeHints size;
2483 
2484 	if (!XGetWMNormalHints(dpy, c->win, &size, &msize))
2485 		/* size is uninitialized, ensure that size.flags aren't used */
2486 		size.flags = PSize;
2487 	if (size.flags & PBaseSize) {
2488 		c->basew = size.base_width;
2489 		c->baseh = size.base_height;
2490 	} else if (size.flags & PMinSize) {
2491 		c->basew = size.min_width;
2492 		c->baseh = size.min_height;
2493 	} else
2494 		c->basew = c->baseh = 0;
2495 	if (size.flags & PResizeInc) {
2496 		c->incw = size.width_inc;
2497 		c->inch = size.height_inc;
2498 	} else
2499 		c->incw = c->inch = 0;
2500 	if (size.flags & PMaxSize) {
2501 		c->maxw = size.max_width;
2502 		c->maxh = size.max_height;
2503 	} else
2504 		c->maxw = c->maxh = 0;
2505 	if (size.flags & PMinSize) {
2506 		c->minw = size.min_width;
2507 		c->minh = size.min_height;
2508 	} else if (size.flags & PBaseSize) {
2509 		c->minw = size.base_width;
2510 		c->minh = size.base_height;
2511 	} else
2512 		c->minw = c->minh = 0;
2513 	if (size.flags & PAspect) {
2514 		c->mina = (float)size.min_aspect.y / size.min_aspect.x;
2515 		c->maxa = (float)size.max_aspect.x / size.max_aspect.y;
2516 	} else
2517 		c->maxa = c->mina = 0.0;
2518 	c->isfixed = (c->maxw && c->maxh && c->maxw == c->minw && c->maxh == c->minh);
2519 }
2520 
2521 void
2522 updatestatus(void)
2523 {
2524 	if (!gettextprop(root, XA_WM_NAME, stext, sizeof(stext)))
2525 		strcpy(stext, "dwm-"VERSION);
2526 	drawbar(selmon);
2527 #if PATCH_SYSTRAY /* {{{ */
2528 	updatesystray();
2529 #endif /* }}} */
2530 }
2531 
2532 void
2533 updatetitle(Client *c)
2534 {
2535 	if (!gettextprop(c->win, netatom[NetWMName], c->name, sizeof c->name))
2536 		gettextprop(c->win, XA_WM_NAME, c->name, sizeof c->name);
2537 	if (c->name[0] == '\0') /* hack to mark broken clients */
2538 		strcpy(c->name, broken);
2539 }
2540 
2541 void
2542 updatewindowtype(Client *c)
2543 {
2544 	Atom state = getatomprop(c, netatom[NetWMState]);
2545 	Atom wtype = getatomprop(c, netatom[NetWMWindowType]);
2546 
2547 	if (state == netatom[NetWMFullscreen])
2548 		setfullscreen(c, 1);
2549 	if (wtype == netatom[NetWMWindowTypeDialog])
2550 		c->isfloating = 1;
2551 }
2552 
2553 void
2554 updatewmhints(Client *c)
2555 {
2556 	XWMHints *wmh;
2557 
2558 	if ((wmh = XGetWMHints(dpy, c->win))) {
2559 		if (c == selmon->sel && wmh->flags & XUrgencyHint) {
2560 			wmh->flags &= ~XUrgencyHint;
2561 			XSetWMHints(dpy, c->win, wmh);
2562 		} else
2563 			c->isurgent = (wmh->flags & XUrgencyHint) ? 1 : 0;
2564 		if (wmh->flags & InputHint)
2565 			c->neverfocus = !wmh->input;
2566 		else
2567 			c->neverfocus = 0;
2568 		XFree(wmh);
2569 	}
2570 }
2571 
2572 void
2573 view(const Arg *arg)
2574 {
2575 #if PATCH_PERTAG /* {{{ */
2576 	int i;
2577 	unsigned int tmptag;
2578 
2579 	if ((arg->ui & TAGMASK) == selmon->tagset[selmon->seltags])
2580 		return;
2581 	selmon->seltags ^= 1; /* toggle sel tagset */
2582 	if (arg->ui & TAGMASK) {
2583 		selmon->tagset[selmon->seltags] = arg->ui & TAGMASK;
2584 		selmon->pertag->prevtag = selmon->pertag->curtag;
2585 
2586 		if (arg->ui == ~0)
2587 			selmon->pertag->curtag = 0;
2588 		else {
2589 			for (i = 0; !(arg->ui & 1 << i); i++) ;
2590 			selmon->pertag->curtag = i + 1;
2591 		}
2592 	} else {
2593 		tmptag = selmon->pertag->prevtag;
2594 		selmon->pertag->prevtag = selmon->pertag->curtag;
2595 		selmon->pertag->curtag = tmptag;
2596 	}
2597 
2598 	selmon->nmaster = selmon->pertag->nmasters[selmon->pertag->curtag];
2599 	selmon->mfact = selmon->pertag->mfacts[selmon->pertag->curtag];
2600 	selmon->sellt = selmon->pertag->sellts[selmon->pertag->curtag];
2601 	selmon->lt[selmon->sellt] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt];
2602 	selmon->lt[selmon->sellt^1] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt^1];
2603 
2604 	if (selmon->showbar != selmon->pertag->showbars[selmon->pertag->curtag])
2605 		togglebar(NULL);
2606 #else
2607 	if ((arg->ui & TAGMASK) == selmon->tagset[selmon->seltags])
2608 		return;
2609 	selmon->seltags ^= 1; /* toggle sel tagset */
2610 	if (arg->ui & TAGMASK)
2611 		selmon->tagset[selmon->seltags] = arg->ui & TAGMASK;
2612 #endif /* }}} */
2613 	focus(NULL);
2614 	arrange(selmon);
2615 }
2616 
2617 Client *
2618 wintoclient(Window w)
2619 {
2620 	Client *c;
2621 	Monitor *m;
2622 
2623 	for (m = mons; m; m = m->next)
2624 		for (c = m->clients; c; c = c->next)
2625 			if (c->win == w)
2626 				return c;
2627 	return NULL;
2628 }
2629 
2630 Monitor *
2631 wintomon(Window w)
2632 {
2633 	int x, y;
2634 	Client *c;
2635 	Monitor *m;
2636 
2637 	if (w == root && getrootptr(&x, &y))
2638 		return recttomon(x, y, 1, 1);
2639 	for (m = mons; m; m = m->next)
2640 		if (w == m->barwin)
2641 			return m;
2642 	if ((c = wintoclient(w)))
2643 		return c->mon;
2644 	return selmon;
2645 }
2646 
2647 /* There's no way to check accesses to destroyed windows, thus those cases are
2648  * ignored (especially on UnmapNotify's). Other types of errors call Xlibs
2649  * default error handler, which may call exit. */
2650 int
2651 xerror(Display *dpy, XErrorEvent *ee)
2652 {
2653 	if (ee->error_code == BadWindow
2654 	|| (ee->request_code == X_SetInputFocus && ee->error_code == BadMatch)
2655 	|| (ee->request_code == X_PolyText8 && ee->error_code == BadDrawable)
2656 	|| (ee->request_code == X_PolyFillRectangle && ee->error_code == BadDrawable)
2657 	|| (ee->request_code == X_PolySegment && ee->error_code == BadDrawable)
2658 	|| (ee->request_code == X_ConfigureWindow && ee->error_code == BadMatch)
2659 	|| (ee->request_code == X_GrabButton && ee->error_code == BadAccess)
2660 	|| (ee->request_code == X_GrabKey && ee->error_code == BadAccess)
2661 	|| (ee->request_code == X_CopyArea && ee->error_code == BadDrawable))
2662 		return 0;
2663 	fprintf(stderr, "dwm: fatal error: request code=%d, error code=%d\n",
2664 		ee->request_code, ee->error_code);
2665 	return xerrorxlib(dpy, ee); /* may call exit */
2666 }
2667 
2668 int
2669 xerrordummy(Display *dpy, XErrorEvent *ee)
2670 {
2671 	return 0;
2672 }
2673 
2674 /* Startup Error handler to check if another window manager
2675  * is already running. */
2676 int
2677 xerrorstart(Display *dpy, XErrorEvent *ee)
2678 {
2679 	die("dwm: another window manager is already running");
2680 	return -1;
2681 }
2682 
2683 void
2684 zoom(const Arg *arg)
2685 {
2686 	Client *c = selmon->sel;
2687 
2688 	if (!selmon->lt[selmon->sellt]->arrange
2689 	|| (selmon->sel && selmon->sel->isfloating))
2690 		return;
2691 	if (c == nexttiled(selmon->clients))
2692 		if (!c || !(c = nexttiled(c->next)))
2693 			return;
2694 	pop(c);
2695 }
2696 
2697 int
2698 main(int argc, char *argv[])
2699 {
2700 	if (argc == 2 && !strcmp("-v", argv[1]))
2701 		die("dwm-"VERSION);
2702 	else if (argc != 1)
2703 		die("usage: dwm [-v]");
2704 	if (!setlocale(LC_CTYPE, "") || !XSupportsLocale())
2705 		fputs("warning: no locale support\n", stderr);
2706 	if (!(dpy = XOpenDisplay(NULL)))
2707 		die("dwm: cannot open display");
2708 	checkotherwm();
2709 	setup();
2710 #ifdef __OpenBSD__
2711 	if (pledge("stdio rpath proc exec", NULL) == -1)
2712 		die("pledge");
2713 #endif /* __OpenBSD__ */
2714 	scan();
2715 	run();
2716 	cleanup();
2717 	XCloseDisplay(dpy);
2718 	return EXIT_SUCCESS;
2719 }
2720 
2721 #if PATCH_DECORATION_HINTS /* {{{ */
2722 void
2723 updatemotifhints(Client *c)
2724 {
2725 	Atom real;
2726 	int format;
2727 	unsigned char *p = NULL;
2728 	unsigned long n, extra;
2729 	unsigned long *motif;
2730 	int width, height;
2731 
2732 	if (XGetWindowProperty(dpy, c->win, motifatom, 0L, 5L, False, motifatom,
2733 	                       &real, &format, &n, &extra, &p) == Success && p != NULL) {
2734 		motif = (unsigned long*)p;
2735 		if (motif[MWM_HINTS_FLAGS_FIELD] & MWM_HINTS_DECORATIONS) {
2736 			width = WIDTH(c);
2737 			height = HEIGHT(c);
2738 
2739 			if (motif[MWM_HINTS_DECORATIONS_FIELD] & MWM_DECOR_ALL ||
2740 			    motif[MWM_HINTS_DECORATIONS_FIELD] & MWM_DECOR_BORDER ||
2741 			    motif[MWM_HINTS_DECORATIONS_FIELD] & MWM_DECOR_TITLE)
2742 				c->bw = c->oldbw = borderpx;
2743 			else
2744 				c->bw = c->oldbw = 0;
2745 
2746 			resize(c, c->x, c->y, width - (2*c->bw), height - (2*c->bw), 0);
2747 		}
2748 		XFree(p);
2749 	}
2750 }
2751 #endif /* }}} */
2752 #if PATCH_WARP /* {{{ */
2753 void
2754 warp(const Client *c)
2755 {
2756 	int x, y;
2757 
2758 	if (!c) {
2759 		XWarpPointer(dpy, None, root, 0, 0, 0, 0, selmon->wx + selmon->ww/2, selmon->wy + selmon->wh/2);
2760 		return;
2761 	}
2762 
2763 	if (!getrootptr(&x, &y) ||
2764 	    (x > c->x - c->bw &&
2765 	     y > c->y - c->bw &&
2766 	     x < c->x + c->w + c->bw*2 &&
2767 	     y < c->y + c->h + c->bw*2) ||
2768 	    (y > c->mon->by && y < c->mon->by + bh) ||
2769 	    (c->mon->topbar && !y))
2770 		return;
2771 
2772 	XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w / 2, c->h / 2);
2773 }
2774 #endif /* }}} */
2775 #if PATCH_AWESOMEBAR /* {{{ */
2776 void
2777 togglewin(const Arg *arg)
2778 {
2779 	Client *c = (Client*)arg->v;
2780 
2781 	if (c == selmon->sel) {
2782 		focus(NULL);
2783 		arrange(c->mon);
2784 	} else {
2785 		focus(c);
2786 		restack(selmon);
2787 	}
2788 }
2789 
2790 void
2791 killwin(const Arg *arg)
2792 {
2793 	selmon->sel = (Client*)arg->v;
2794 	killclient(arg);
2795 }
2796 
2797 void
2798 zoomwin(const Arg *arg)
2799 {
2800 	selmon->sel = (Client*)arg->v;
2801 	zoom(arg);
2802 }
2803 #endif /* }}} */
2804 #if PATCH_SYSTRAY /* {{{ */
2805 unsigned int
2806 getsystraywidth()
2807 {
2808 	unsigned int w = 0;
2809 	Client *i;
2810 	for(i = systray->icons; i; w += i->w + systrayspacing, i = i->next) ;
2811 	return w ? w + systrayspacing : 1;
2812 }
2813 
2814 void
2815 removesystrayicon(Client *i)
2816 {
2817 	Client **ii;
2818 
2819 	if (!i) return;
2820 	for (ii = &systray->icons; *ii && *ii != i; ii = &(*ii)->next);
2821 	if (ii)
2822 		*ii = i->next;
2823 	free(i);
2824 }
2825 
2826 void
2827 resizebarwin(Monitor *m)
2828 {
2829 	unsigned int w = m->ww;
2830 	if (m == systraytomon(m))
2831 		w -= getsystraywidth();
2832 	XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, w, bh);
2833 }
2834 
2835 void
2836 resizerequest(XEvent *e)
2837 {
2838 	XResizeRequestEvent *ev = &e->xresizerequest;
2839 	Client *i;
2840 
2841 	if ((i = wintosystrayicon(ev->window))) {
2842 		updatesystrayicongeom(i, ev->width, ev->height);
2843 		resizebarwin(selmon);
2844 		updatesystray();
2845 	}
2846 }
2847 
2848 void
2849 updatesystrayicongeom(Client *i, int w, int h)
2850 {
2851 	if (i) {
2852 		i->h = bh;
2853 		if (w == h)
2854 			i->w = bh;
2855 		else if (h == bh)
2856 			i->w = w;
2857 		else
2858 			i->w = (int) ((float)bh * ((float)w / (float)h));
2859 		applysizehints(i, &(i->x), &(i->y), &(i->w), &(i->h), False);
2860 		/* force icons into the systray dimensions if they don't want to */
2861 		if (i->h > bh) {
2862 			if (i->w == i->h)
2863 				i->w = bh;
2864 			else
2865 				i->w = (int) ((float)bh * ((float)i->w / (float)i->h));
2866 			i->h = bh;
2867 		}
2868 	}
2869 }
2870 
2871 void
2872 updatesystrayiconstate(Client *i, XPropertyEvent *ev)
2873 {
2874 	long flags;
2875 	int code = 0;
2876 
2877 	if (!i || ev->atom != xatom[XembedInfo] ||
2878 			!(flags = getatomprop(i, xatom[XembedInfo])))
2879 		return;
2880 
2881 	if (flags & XEMBED_MAPPED && !i->tags) {
2882 		i->tags = 1;
2883 		code = XEMBED_WINDOW_ACTIVATE;
2884 		XMapRaised(dpy, i->win);
2885 		setclientstate(i, NormalState);
2886 	}
2887 	else if (!(flags & XEMBED_MAPPED) && i->tags) {
2888 		i->tags = 0;
2889 		code = XEMBED_WINDOW_DEACTIVATE;
2890 		XUnmapWindow(dpy, i->win);
2891 		setclientstate(i, WithdrawnState);
2892 	}
2893 	else
2894 		return;
2895 	sendevent(i->win, xatom[Xembed], StructureNotifyMask, CurrentTime, code, 0,
2896 			systray->win, XEMBED_EMBEDDED_VERSION);
2897 }
2898 
2899 void
2900 updatesystray(void)
2901 {
2902 	XSetWindowAttributes wa;
2903 	XWindowChanges wc;
2904 	Client *i;
2905 	Monitor *m = systraytomon(NULL);
2906 	unsigned int x = m->mx + m->mw;
2907 	unsigned int w = 1;
2908 
2909 	if (!systray) {
2910 		/* init systray */
2911 		if (!(systray = (Systray *)calloc(1, sizeof(Systray))))
2912 			die("fatal: could not malloc() %u bytes\n", sizeof(Systray));
2913 		systray->win = XCreateSimpleWindow(dpy, root, x, m->by, w, bh, 0, 0, scheme[SchemeSel][ColBg].pixel);
2914 		wa.event_mask        = ButtonPressMask | ExposureMask;
2915 		wa.override_redirect = True;
2916 		wa.background_pixel  = scheme[SchemeNorm][ColBg].pixel;
2917 		XSelectInput(dpy, systray->win, SubstructureNotifyMask);
2918 		XChangeProperty(dpy, systray->win, netatom[NetSystemTrayOrientation], XA_CARDINAL, 32,
2919 				PropModeReplace, (unsigned char *)&netatom[NetSystemTrayOrientationHorz], 1);
2920 		XChangeWindowAttributes(dpy, systray->win, CWEventMask|CWOverrideRedirect|CWBackPixel, &wa);
2921 		XMapRaised(dpy, systray->win);
2922 		XSetSelectionOwner(dpy, netatom[NetSystemTray], systray->win, CurrentTime);
2923 		if (XGetSelectionOwner(dpy, netatom[NetSystemTray]) == systray->win) {
2924 			sendevent(root, xatom[Manager], StructureNotifyMask, CurrentTime, netatom[NetSystemTray], systray->win, 0, 0);
2925 			XSync(dpy, False);
2926 		}
2927 		else {
2928 			fprintf(stderr, "dwm: unable to obtain system tray.\n");
2929 			free(systray);
2930 			systray = NULL;
2931 			return;
2932 		}
2933 	}
2934 	for (w = 0, i = systray->icons; i; i = i->next) {
2935 		/* make sure the background color stays the same */
2936 		wa.background_pixel  = scheme[SchemeNorm][ColBg].pixel;
2937 		XChangeWindowAttributes(dpy, i->win, CWBackPixel, &wa);
2938 		XMapRaised(dpy, i->win);
2939 		w += systrayspacing;
2940 		i->x = w;
2941 		XMoveResizeWindow(dpy, i->win, i->x, 0, i->w, i->h);
2942 		w += i->w;
2943 		if (i->mon != m)
2944 			i->mon = m;
2945 	}
2946 	w = w ? w + systrayspacing : 1;
2947 	x -= w;
2948 	XMoveResizeWindow(dpy, systray->win, x, m->by, w, bh);
2949 	wc.x = x; wc.y = m->by; wc.width = w; wc.height = bh;
2950 	wc.stack_mode = Above; wc.sibling = m->barwin;
2951 	wc.stack_mode = TopIf;
2952 	XConfigureWindow(dpy, systray->win, CWX|CWY|CWWidth|CWHeight|CWSibling|CWStackMode, &wc);
2953 	XMapWindow(dpy, systray->win);
2954 	XMapSubwindows(dpy, systray->win);
2955 	/* redraw background */
2956 	XSetForeground(dpy, drw->gc, scheme[SchemeNorm][ColBg].pixel);
2957 	XFillRectangle(dpy, systray->win, drw->gc, 0, 0, w, bh);
2958 	XSync(dpy, False);
2959 }
2960 
2961 Client *
2962 wintosystrayicon(Window w)
2963 {
2964 	Client *i = NULL;
2965 
2966 	if (!w) return i;
2967 	for (i = systray->icons; i && i->win != w; i = i->next) ;
2968 	return i;
2969 }
2970 
2971 Monitor *
2972 systraytomon(Monitor *m)
2973 {
2974 	if (!m) return selmon;
2975 	return m == selmon ? m : NULL;
2976 }
2977 #endif /* }}} */