all repos — dwm @ 49197fe4bf023478108c76c1bed74a7d1ef138de

fork of suckless dynamic window manager

dwm.c (view raw)

   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 * Calls to fetch an X event from the event queue are blocking.  Due reading
  10 * status text from standard input, a select()-driven main loop has been
  11 * implemented which selects for reads on the X connection and STDIN_FILENO to
  12 * handle all data smoothly. The event handlers of dwm are organized in an
  13 * array which is accessed whenever a new event has been fetched. This allows
  14 * event dispatching in O(1) time.
  15 *
  16 * Each child of the root window is called a client, except windows which have
  17 * set the override_redirect flag.  Clients are organized in a global
  18 * doubly-linked client list, the focus history is remembered through a global
  19 * stack list. Each client contains an array of Bools of the same size as the
  20 * global tags array to indicate the tags of a client.  For each client dwm
  21 * creates a small title window, which is resized whenever the (_NET_)WM_NAME
  22 * properties are updated or the client is moved/resized.
  23 *
  24 * Keys and tagging rules are organized as arrays and defined in the config.h
  25 * file. These arrays are kept static in event.o and tag.o respectively,
  26 * because no other part of dwm needs access to them.  The current layout is
  27 * represented by the lt pointer.
  28 *
  29 * To understand everything else, start reading main().
  30 */
  31#include <errno.h>
  32#include <locale.h>
  33#include <regex.h>
  34#include <stdarg.h>
  35#include <stdio.h>
  36#include <stdlib.h>
  37#include <string.h>
  38#include <unistd.h>
  39#include <sys/select.h>
  40#include <sys/wait.h>
  41#include <X11/cursorfont.h>
  42#include <X11/keysym.h>
  43#include <X11/Xatom.h>
  44#include <X11/Xproto.h>
  45#include <X11/Xutil.h>
  46
  47/* macros */
  48#define BUTTONMASK		(ButtonPressMask | ButtonReleaseMask)
  49#define CLEANMASK(mask)		(mask & ~(numlockmask | LockMask))
  50#define MOUSEMASK		(BUTTONMASK | PointerMotionMask)
  51
  52/* enums */
  53enum { BarTop, BarBot, BarOff };			/* bar position */
  54enum { CurNormal, CurResize, CurMove, CurLast };	/* cursor */
  55enum { ColBorder, ColFG, ColBG, ColLast };		/* color */
  56enum { NetSupported, NetWMName, NetLast };		/* EWMH atoms */
  57enum { WMProtocols, WMDelete, WMName, WMState, WMLast };/* default atoms */
  58
  59/* typedefs */
  60typedef struct Client Client;
  61struct Client {
  62	char name[256];
  63	int x, y, w, h;
  64	int rx, ry, rw, rh; /* revert geometry */
  65	int basew, baseh, incw, inch, maxw, maxh, minw, minh;
  66	int minax, maxax, minay, maxay;
  67	long flags; 
  68	unsigned int border, oldborder;
  69	Bool isbanned, isfixed, ismax, isfloating;
  70	Bool *tags;
  71	Client *next;
  72	Client *prev;
  73	Client *snext;
  74	Window win;
  75};
  76
  77typedef struct {
  78	int x, y, w, h;
  79	unsigned long norm[ColLast];
  80	unsigned long sel[ColLast];
  81	Drawable drawable;
  82	GC gc;
  83	struct {
  84		int ascent;
  85		int descent;
  86		int height;
  87		XFontSet set;
  88		XFontStruct *xfont;
  89	} font;
  90} DC; /* draw context */
  91
  92typedef struct {
  93	unsigned long mod;
  94	KeySym keysym;
  95	void (*func)(const char *arg);
  96	const char *arg;
  97} Key;
  98
  99typedef struct {
 100	const char *symbol;
 101	void (*arrange)(void);
 102} Layout;
 103
 104typedef struct {
 105	const char *prop;
 106	const char *tags;
 107	Bool isfloating;
 108} Rule;
 109
 110typedef struct {
 111	regex_t *propregex;
 112	regex_t *tagregex;
 113} Regs;
 114
 115/* functions */
 116
 117static void applyrules(Client *c);
 118static void arrange(void);
 119static void attach(Client *c);
 120static void attachstack(Client *c);
 121static void ban(Client *c);
 122static void buttonpress(XEvent *e);
 123static void cleanup(void);
 124static void compileregs(void);
 125static void configure(Client *c);
 126static void configurenotify(XEvent *e);
 127static void configurerequest(XEvent *e);
 128static void destroynotify(XEvent *e);
 129static void detach(Client *c);
 130static void detachstack(Client *c);
 131static void drawbar(void);
 132static void drawsquare(Bool filled, Bool empty, unsigned long col[ColLast]);
 133static void drawtext(const char *text, unsigned long col[ColLast]);
 134static void *emallocz(unsigned int size);
 135static void enternotify(XEvent *e);
 136static void eprint(const char *errstr, ...);
 137static void expose(XEvent *e);
 138static void floating(void); /* default floating layout */
 139static void focus(Client *c);
 140static void focusnext(const char *arg);
 141static void focusprev(const char *arg);
 142static Client *getclient(Window w);
 143static long getstate(Window w);
 144static Bool gettextprop(Window w, Atom atom, char *text, unsigned int size);
 145static void grabbuttons(Client *c, Bool focused);
 146static unsigned int idxoftag(const char *tag);
 147static void initbar(void);
 148static unsigned long initcolor(const char *colstr);
 149static void initfont(const char *fontstr);
 150static void initlayouts(void);
 151static void initstyle(void);
 152static Bool isarrange(void (*func)());
 153static Bool isfloating(void);
 154static Bool isoccupied(unsigned int t);
 155static Bool isprotodel(Client *c);
 156static Bool isvisible(Client *c);
 157static void keypress(XEvent *e);
 158static void killclient(const char *arg);
 159static void leavenotify(XEvent *e);
 160static void manage(Window w, XWindowAttributes *wa);
 161static void mappingnotify(XEvent *e);
 162static void maprequest(XEvent *e);
 163static void movemouse(Client *c);
 164static Client *nexttiled(Client *c);
 165static void propertynotify(XEvent *e);
 166static void quit(const char *arg);
 167static void resize(Client *c, int x, int y, int w, int h, Bool sizehints);
 168static void resizemouse(Client *c);
 169static void restack(void);
 170static void scan(void);
 171static void setclientstate(Client *c, long state);
 172static void setlayout(const char *arg);
 173static void setmwfact(const char *arg);
 174static void setup(void);
 175static void spawn(const char *arg);
 176static void tag(const char *arg);
 177static unsigned int textnw(const char *text, unsigned int len);
 178static unsigned int textw(const char *text);
 179static void tile(void);
 180static void togglebar(const char *arg);
 181static void togglefloating(const char *arg);
 182static void togglemax(const char *arg);
 183static void toggletag(const char *arg);
 184static void toggleview(const char *arg);
 185static void unban(Client *c);
 186static void unmanage(Client *c);
 187static void unmapnotify(XEvent *e);
 188static void updatebarpos(void);
 189static void updatesizehints(Client *c);
 190static void updatetitle(Client *c);
 191static void view(const char *arg);
 192static int xerror(Display *dpy, XErrorEvent *ee);
 193static int xerrordummy(Display *dsply, XErrorEvent *ee);
 194static int xerrorstart(Display *dsply, XErrorEvent *ee);
 195static void zoom(const char *arg);
 196
 197/* variables */
 198static char stext[256];
 199static double mwfact;
 200static int screen, sx, sy, sw, sh, wax, way, waw, wah;
 201static int (*xerrorxlib)(Display *, XErrorEvent *);
 202static unsigned int bh, bpos, ntags;
 203static unsigned int blw = 0;
 204static unsigned int ltidx = 0; /* default */
 205static unsigned int nlayouts = 0;
 206static unsigned int nrules = 0;
 207static unsigned int numlockmask = 0;
 208static void (*handler[LASTEvent]) (XEvent *) = {
 209	[ButtonPress] = buttonpress,
 210	[ConfigureRequest] = configurerequest,
 211	[ConfigureNotify] = configurenotify,
 212	[DestroyNotify] = destroynotify,
 213	[EnterNotify] = enternotify,
 214	[LeaveNotify] = leavenotify,
 215	[Expose] = expose,
 216	[KeyPress] = keypress,
 217	[MappingNotify] = mappingnotify,
 218	[MapRequest] = maprequest,
 219	[PropertyNotify] = propertynotify,
 220	[UnmapNotify] = unmapnotify
 221};
 222static Atom wmatom[WMLast], netatom[NetLast];
 223static Bool otherwm, readin;
 224static Bool running = True;
 225static Bool *seltags;
 226static Bool selscreen = True;
 227static Client *clients = NULL;
 228static Client *sel = NULL;
 229static Client *stack = NULL;
 230static Cursor cursor[CurLast];
 231static Display *dpy;
 232static DC dc = {0};
 233static Window barwin, root;
 234static Regs *regs = NULL;
 235
 236/* configuration, allows nested code to access above variables */
 237#include "config.h"
 238
 239/* implementation */
 240static void
 241applyrules(Client *c) {
 242	static char buf[512];
 243	unsigned int i, j;
 244	regmatch_t tmp;
 245	Bool matched = False;
 246	XClassHint ch = { 0 };
 247
 248	/* rule matching */
 249	XGetClassHint(dpy, c->win, &ch);
 250	snprintf(buf, sizeof buf, "%s:%s:%s",
 251			ch.res_class ? ch.res_class : "",
 252			ch.res_name ? ch.res_name : "", c->name);
 253	for(i = 0; i < nrules; i++)
 254		if(regs[i].propregex && !regexec(regs[i].propregex, buf, 1, &tmp, 0)) {
 255			c->isfloating = rules[i].isfloating;
 256			for(j = 0; regs[i].tagregex && j < ntags; j++) {
 257				if(!regexec(regs[i].tagregex, tags[j], 1, &tmp, 0)) {
 258					matched = True;
 259					c->tags[j] = True;
 260				}
 261			}
 262		}
 263	if(ch.res_class)
 264		XFree(ch.res_class);
 265	if(ch.res_name)
 266		XFree(ch.res_name);
 267	if(!matched)
 268		for(i = 0; i < ntags; i++)
 269			c->tags[i] = seltags[i];
 270}
 271
 272static void
 273arrange(void) {
 274	Client *c;
 275
 276	for(c = clients; c; c = c->next)
 277		if(isvisible(c))
 278			unban(c);
 279		else
 280			ban(c);
 281	layouts[ltidx].arrange();
 282	focus(NULL);
 283	restack();
 284}
 285
 286static void
 287attach(Client *c) {
 288	if(clients)
 289		clients->prev = c;
 290	c->next = clients;
 291	clients = c;
 292}
 293
 294static void
 295attachstack(Client *c) {
 296	c->snext = stack;
 297	stack = c;
 298}
 299
 300static void
 301ban(Client *c) {
 302	if(c->isbanned)
 303		return;
 304	XMoveWindow(dpy, c->win, c->x + 2 * sw, c->y);
 305	c->isbanned = True;
 306}
 307
 308static void
 309buttonpress(XEvent *e) {
 310	unsigned int i, x;
 311	Client *c;
 312	XButtonPressedEvent *ev = &e->xbutton;
 313
 314	if(barwin == ev->window) {
 315		x = 0;
 316		for(i = 0; i < ntags; i++) {
 317			x += textw(tags[i]);
 318			if(ev->x < x) {
 319				if(ev->button == Button1) {
 320					if(ev->state & MODKEY)
 321						tag(tags[i]);
 322					else
 323						view(tags[i]);
 324				}
 325				else if(ev->button == Button3) {
 326					if(ev->state & MODKEY)
 327						toggletag(tags[i]);
 328					else
 329						toggleview(tags[i]);
 330				}
 331				return;
 332			}
 333		}
 334		if((ev->x < x + blw) && ev->button == Button1)
 335			setlayout(NULL);
 336	}
 337	else if((c = getclient(ev->window))) {
 338		focus(c);
 339		if(CLEANMASK(ev->state) != MODKEY)
 340			return;
 341		if(ev->button == Button1 && (isfloating() || c->isfloating)) {
 342			restack();
 343			movemouse(c);
 344		}
 345		else if(ev->button == Button2)
 346			zoom(NULL);
 347		else if(ev->button == Button3
 348		&& (isfloating() || c->isfloating) && !c->isfixed)
 349		{
 350			restack();
 351			resizemouse(c);
 352		}
 353	}
 354}
 355
 356static void
 357cleanup(void) {
 358	close(STDIN_FILENO);
 359	while(stack) {
 360		unban(stack);
 361		unmanage(stack);
 362	}
 363	if(dc.font.set)
 364		XFreeFontSet(dpy, dc.font.set);
 365	else
 366		XFreeFont(dpy, dc.font.xfont);
 367	XUngrabKey(dpy, AnyKey, AnyModifier, root);
 368	XFreePixmap(dpy, dc.drawable);
 369	XFreeGC(dpy, dc.gc);
 370	XDestroyWindow(dpy, barwin);
 371	XFreeCursor(dpy, cursor[CurNormal]);
 372	XFreeCursor(dpy, cursor[CurResize]);
 373	XFreeCursor(dpy, cursor[CurMove]);
 374	XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime);
 375	XSync(dpy, False);
 376	free(seltags);
 377}
 378
 379static void
 380compileregs(void) {
 381	unsigned int i;
 382	regex_t *reg;
 383
 384	if(regs)
 385		return;
 386	nrules = sizeof rules / sizeof rules[0];
 387	regs = emallocz(nrules * sizeof(Regs));
 388	for(i = 0; i < nrules; i++) {
 389		if(rules[i].prop) {
 390			reg = emallocz(sizeof(regex_t));
 391			if(regcomp(reg, rules[i].prop, REG_EXTENDED))
 392				free(reg);
 393			else
 394				regs[i].propregex = reg;
 395		}
 396		if(rules[i].tags) {
 397			reg = emallocz(sizeof(regex_t));
 398			if(regcomp(reg, rules[i].tags, REG_EXTENDED))
 399				free(reg);
 400			else
 401				regs[i].tagregex = reg;
 402		}
 403	}
 404}
 405
 406static void
 407configure(Client *c) {
 408	XConfigureEvent ce;
 409
 410	ce.type = ConfigureNotify;
 411	ce.display = dpy;
 412	ce.event = c->win;
 413	ce.window = c->win;
 414	ce.x = c->x;
 415	ce.y = c->y;
 416	ce.width = c->w;
 417	ce.height = c->h;
 418	ce.border_width = c->border;
 419	ce.above = None;
 420	ce.override_redirect = False;
 421	XSendEvent(dpy, c->win, False, StructureNotifyMask, (XEvent *)&ce);
 422}
 423
 424static void
 425configurenotify(XEvent *e) {
 426	XConfigureEvent *ev = &e->xconfigure;
 427
 428	if (ev->window == root && (ev->width != sw || ev->height != sh)) {
 429		sw = ev->width;
 430		sh = ev->height;
 431		XFreePixmap(dpy, dc.drawable);
 432		dc.drawable = XCreatePixmap(dpy, root, sw, bh, DefaultDepth(dpy, screen));
 433		XResizeWindow(dpy, barwin, sw, bh);
 434		updatebarpos();
 435		arrange();
 436	}
 437}
 438
 439static void
 440configurerequest(XEvent *e) {
 441	Client *c;
 442	XConfigureRequestEvent *ev = &e->xconfigurerequest;
 443	XWindowChanges wc;
 444
 445	if((c = getclient(ev->window))) {
 446		c->ismax = False;
 447		if(ev->value_mask & CWBorderWidth)
 448			c->border = ev->border_width;
 449		if(c->isfixed || c->isfloating || isfloating()) {
 450			if(ev->value_mask & CWX)
 451				c->x = ev->x;
 452			if(ev->value_mask & CWY)
 453				c->y = ev->y;
 454			if(ev->value_mask & CWWidth)
 455				c->w = ev->width;
 456			if(ev->value_mask & CWHeight)
 457				c->h = ev->height;
 458			if((c->x + c->w) > sw && c->isfloating)
 459				c->x = sw / 2 - c->w / 2; /* center in x direction */
 460			if((c->y + c->h) > sh && c->isfloating)
 461				c->y = sh / 2 - c->h / 2; /* center in y direction */
 462			if((ev->value_mask & (CWX | CWY))
 463			&& !(ev->value_mask & (CWWidth | CWHeight)))
 464				configure(c);
 465			if(isvisible(c))
 466				XMoveResizeWindow(dpy, c->win, c->x, c->y, c->w, c->h);
 467		}
 468		else
 469			configure(c);
 470	}
 471	else {
 472		wc.x = ev->x;
 473		wc.y = ev->y;
 474		wc.width = ev->width;
 475		wc.height = ev->height;
 476		wc.border_width = ev->border_width;
 477		wc.sibling = ev->above;
 478		wc.stack_mode = ev->detail;
 479		XConfigureWindow(dpy, ev->window, ev->value_mask, &wc);
 480	}
 481	XSync(dpy, False);
 482}
 483
 484static void
 485destroynotify(XEvent *e) {
 486	Client *c;
 487	XDestroyWindowEvent *ev = &e->xdestroywindow;
 488
 489	if((c = getclient(ev->window)))
 490		unmanage(c);
 491}
 492
 493static void
 494detach(Client *c) {
 495	if(c->prev)
 496		c->prev->next = c->next;
 497	if(c->next)
 498		c->next->prev = c->prev;
 499	if(c == clients)
 500		clients = c->next;
 501	c->next = c->prev = NULL;
 502}
 503
 504static void
 505detachstack(Client *c) {
 506	Client **tc;
 507
 508	for(tc=&stack; *tc && *tc != c; tc=&(*tc)->snext);
 509	*tc = c->snext;
 510}
 511
 512static void
 513drawbar(void) {
 514	int i, x;
 515
 516	dc.x = dc.y = 0;
 517	for(i = 0; i < ntags; i++) {
 518		dc.w = textw(tags[i]);
 519		if(seltags[i]) {
 520			drawtext(tags[i], dc.sel);
 521			drawsquare(sel && sel->tags[i], isoccupied(i), dc.sel);
 522		}
 523		else {
 524			drawtext(tags[i], dc.norm);
 525			drawsquare(sel && sel->tags[i], isoccupied(i), dc.norm);
 526		}
 527		dc.x += dc.w;
 528	}
 529	dc.w = blw;
 530	drawtext(layouts[ltidx].symbol, dc.norm);
 531	x = dc.x + dc.w;
 532	dc.w = textw(stext);
 533	dc.x = sw - dc.w;
 534	if(dc.x < x) {
 535		dc.x = x;
 536		dc.w = sw - x;
 537	}
 538	drawtext(stext, dc.norm);
 539	if((dc.w = dc.x - x) > bh) {
 540		dc.x = x;
 541		if(sel) {
 542			drawtext(sel->name, dc.sel);
 543			drawsquare(sel->ismax, sel->isfloating, dc.sel);
 544		}
 545		else
 546			drawtext(NULL, dc.norm);
 547	}
 548	XCopyArea(dpy, dc.drawable, barwin, dc.gc, 0, 0, sw, bh, 0, 0);
 549	XSync(dpy, False);
 550}
 551
 552static void
 553drawsquare(Bool filled, Bool empty, unsigned long col[ColLast]) {
 554	int x;
 555	XGCValues gcv;
 556	XRectangle r = { dc.x, dc.y, dc.w, dc.h };
 557
 558	gcv.foreground = col[ColFG];
 559	XChangeGC(dpy, dc.gc, GCForeground, &gcv);
 560	x = (dc.font.ascent + dc.font.descent + 2) / 4;
 561	r.x = dc.x + 1;
 562	r.y = dc.y + 1;
 563	if(filled) {
 564		r.width = r.height = x + 1;
 565		XFillRectangles(dpy, dc.drawable, dc.gc, &r, 1);
 566	}
 567	else if(empty) {
 568		r.width = r.height = x;
 569		XDrawRectangles(dpy, dc.drawable, dc.gc, &r, 1);
 570	}
 571}
 572
 573static void
 574drawtext(const char *text, unsigned long col[ColLast]) {
 575	int x, y, w, h;
 576	static char buf[256];
 577	unsigned int len, olen;
 578	XRectangle r = { dc.x, dc.y, dc.w, dc.h };
 579
 580	XSetForeground(dpy, dc.gc, col[ColBG]);
 581	XFillRectangles(dpy, dc.drawable, dc.gc, &r, 1);
 582	if(!text)
 583		return;
 584	w = 0;
 585	olen = len = strlen(text);
 586	if(len >= sizeof buf)
 587		len = sizeof buf - 1;
 588	memcpy(buf, text, len);
 589	buf[len] = 0;
 590	h = dc.font.ascent + dc.font.descent;
 591	y = dc.y + (dc.h / 2) - (h / 2) + dc.font.ascent;
 592	x = dc.x + (h / 2);
 593	/* shorten text if necessary */
 594	while(len && (w = textnw(buf, len)) > dc.w - h)
 595		buf[--len] = 0;
 596	if(len < olen) {
 597		if(len > 1)
 598			buf[len - 1] = '.';
 599		if(len > 2)
 600			buf[len - 2] = '.';
 601		if(len > 3)
 602			buf[len - 3] = '.';
 603	}
 604	if(w > dc.w)
 605		return; /* too long */
 606	XSetForeground(dpy, dc.gc, col[ColFG]);
 607	if(dc.font.set)
 608		XmbDrawString(dpy, dc.drawable, dc.font.set, dc.gc, x, y, buf, len);
 609	else
 610		XDrawString(dpy, dc.drawable, dc.gc, x, y, buf, len);
 611}
 612
 613static void *
 614emallocz(unsigned int size) {
 615	void *res = calloc(1, size);
 616
 617	if(!res)
 618		eprint("fatal: could not malloc() %u bytes\n", size);
 619	return res;
 620}
 621
 622static void
 623enternotify(XEvent *e) {
 624	Client *c;
 625	XCrossingEvent *ev = &e->xcrossing;
 626
 627	if(ev->mode != NotifyNormal || ev->detail == NotifyInferior)
 628		return;
 629	if((c = getclient(ev->window)))
 630		focus(c);
 631	else if(ev->window == root) {
 632		selscreen = True;
 633		focus(NULL);
 634	}
 635}
 636
 637static void
 638eprint(const char *errstr, ...) {
 639	va_list ap;
 640
 641	va_start(ap, errstr);
 642	vfprintf(stderr, errstr, ap);
 643	va_end(ap);
 644	exit(EXIT_FAILURE);
 645}
 646
 647static void
 648expose(XEvent *e) {
 649	XExposeEvent *ev = &e->xexpose;
 650
 651	if(ev->count == 0) {
 652		if(barwin == ev->window)
 653			drawbar();
 654	}
 655}
 656
 657static void
 658floating(void) { /* default floating layout */
 659	Client *c;
 660
 661	for(c = clients; c; c = c->next)
 662		if(isvisible(c))
 663			resize(c, c->x, c->y, c->w, c->h, True);
 664}
 665
 666static void
 667focus(Client *c) {
 668	if((!c && selscreen) || (c && !isvisible(c)))
 669		for(c = stack; c && !isvisible(c); c = c->snext);
 670	if(sel && sel != c) {
 671		grabbuttons(sel, False);
 672		XSetWindowBorder(dpy, sel->win, dc.norm[ColBorder]);
 673	}
 674	if(c) {
 675		detachstack(c);
 676		attachstack(c);
 677		grabbuttons(c, True);
 678	}
 679	sel = c;
 680	drawbar();
 681	if(!selscreen)
 682		return;
 683	if(c) {
 684		XSetWindowBorder(dpy, c->win, dc.sel[ColBorder]);
 685		XSetInputFocus(dpy, c->win, RevertToPointerRoot, CurrentTime);
 686	}
 687	else
 688		XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime);
 689}
 690
 691static void
 692focusnext(const char *arg) {
 693	Client *c;
 694
 695	if(!sel)
 696		return;
 697	for(c = sel->next; c && !isvisible(c); c = c->next);
 698	if(!c)
 699		for(c = clients; c && !isvisible(c); c = c->next);
 700	if(c) {
 701		focus(c);
 702		restack();
 703	}
 704}
 705
 706static void
 707focusprev(const char *arg) {
 708	Client *c;
 709
 710	if(!sel)
 711		return;
 712	for(c = sel->prev; c && !isvisible(c); c = c->prev);
 713	if(!c) {
 714		for(c = clients; c && c->next; c = c->next);
 715		for(; c && !isvisible(c); c = c->prev);
 716	}
 717	if(c) {
 718		focus(c);
 719		restack();
 720	}
 721}
 722
 723static Client *
 724getclient(Window w) {
 725	Client *c;
 726
 727	for(c = clients; c && c->win != w; c = c->next);
 728	return c;
 729}
 730
 731static long
 732getstate(Window w) {
 733	int format, status;
 734	long result = -1;
 735	unsigned char *p = NULL;
 736	unsigned long n, extra;
 737	Atom real;
 738
 739	status = XGetWindowProperty(dpy, w, wmatom[WMState], 0L, 2L, False, wmatom[WMState],
 740			&real, &format, &n, &extra, (unsigned char **)&p);
 741	if(status != Success)
 742		return -1;
 743	if(n != 0)
 744		result = *p;
 745	XFree(p);
 746	return result;
 747}
 748
 749static Bool
 750gettextprop(Window w, Atom atom, char *text, unsigned int size) {
 751	char **list = NULL;
 752	int n;
 753	XTextProperty name;
 754
 755	if(!text || size == 0)
 756		return False;
 757	text[0] = '\0';
 758	XGetTextProperty(dpy, w, &name, atom);
 759	if(!name.nitems)
 760		return False;
 761	if(name.encoding == XA_STRING)
 762		strncpy(text, (char *)name.value, size - 1);
 763	else {
 764		if(XmbTextPropertyToTextList(dpy, &name, &list, &n) >= Success
 765		&& n > 0 && *list)
 766		{
 767			strncpy(text, *list, size - 1);
 768			XFreeStringList(list);
 769		}
 770	}
 771	text[size - 1] = '\0';
 772	XFree(name.value);
 773	return True;
 774}
 775
 776static void
 777grabbuttons(Client *c, Bool focused) {
 778	XUngrabButton(dpy, AnyButton, AnyModifier, c->win);
 779
 780	if(focused) {
 781		XGrabButton(dpy, Button1, MODKEY, c->win, False, BUTTONMASK,
 782				GrabModeAsync, GrabModeSync, None, None);
 783		XGrabButton(dpy, Button1, MODKEY | LockMask, c->win, False, BUTTONMASK,
 784				GrabModeAsync, GrabModeSync, None, None);
 785		XGrabButton(dpy, Button1, MODKEY | numlockmask, c->win, False, BUTTONMASK,
 786				GrabModeAsync, GrabModeSync, None, None);
 787		XGrabButton(dpy, Button1, MODKEY | numlockmask | LockMask, c->win, False, BUTTONMASK,
 788				GrabModeAsync, GrabModeSync, None, None);
 789
 790		XGrabButton(dpy, Button2, MODKEY, c->win, False, BUTTONMASK,
 791				GrabModeAsync, GrabModeSync, None, None);
 792		XGrabButton(dpy, Button2, MODKEY | LockMask, c->win, False, BUTTONMASK,
 793				GrabModeAsync, GrabModeSync, None, None);
 794		XGrabButton(dpy, Button2, MODKEY | numlockmask, c->win, False, BUTTONMASK,
 795				GrabModeAsync, GrabModeSync, None, None);
 796		XGrabButton(dpy, Button2, MODKEY | numlockmask | LockMask, c->win, False, BUTTONMASK,
 797				GrabModeAsync, GrabModeSync, None, None);
 798
 799		XGrabButton(dpy, Button3, MODKEY, c->win, False, BUTTONMASK,
 800				GrabModeAsync, GrabModeSync, None, None);
 801		XGrabButton(dpy, Button3, MODKEY | LockMask, c->win, False, BUTTONMASK,
 802				GrabModeAsync, GrabModeSync, None, None);
 803		XGrabButton(dpy, Button3, MODKEY | numlockmask, c->win, False, BUTTONMASK,
 804				GrabModeAsync, GrabModeSync, None, None);
 805		XGrabButton(dpy, Button3, MODKEY | numlockmask | LockMask, c->win, False, BUTTONMASK,
 806				GrabModeAsync, GrabModeSync, None, None);
 807	}
 808	else
 809		XGrabButton(dpy, AnyButton, AnyModifier, c->win, False, BUTTONMASK,
 810				GrabModeAsync, GrabModeSync, None, None);
 811}
 812
 813static unsigned int
 814idxoftag(const char *tag) {
 815	unsigned int i;
 816
 817	for(i = 0; i < ntags; i++)
 818		if(tags[i] == tag)
 819			return i;
 820	return 0;
 821}
 822
 823static void
 824initbar(void) {
 825	XSetWindowAttributes wa;
 826
 827	wa.override_redirect = 1;
 828	wa.background_pixmap = ParentRelative;
 829	wa.event_mask = ButtonPressMask | ExposureMask;
 830	barwin = XCreateWindow(dpy, root, sx, sy, sw, bh, 0,
 831			DefaultDepth(dpy, screen), CopyFromParent, DefaultVisual(dpy, screen),
 832			CWOverrideRedirect | CWBackPixmap | CWEventMask, &wa);
 833	XDefineCursor(dpy, barwin, cursor[CurNormal]);
 834	updatebarpos();
 835	XMapRaised(dpy, barwin);
 836	strcpy(stext, "dwm-"VERSION);
 837	dc.drawable = XCreatePixmap(dpy, root, sw, bh, DefaultDepth(dpy, screen));
 838	dc.gc = XCreateGC(dpy, root, 0, 0);
 839	XSetLineAttributes(dpy, dc.gc, 1, LineSolid, CapButt, JoinMiter);
 840	if(!dc.font.set)
 841		XSetFont(dpy, dc.gc, dc.font.xfont->fid);
 842}
 843
 844static unsigned long
 845initcolor(const char *colstr) {
 846	Colormap cmap = DefaultColormap(dpy, screen);
 847	XColor color;
 848
 849	if(!XAllocNamedColor(dpy, cmap, colstr, &color, &color))
 850		eprint("error, cannot allocate color '%s'\n", colstr);
 851	return color.pixel;
 852}
 853
 854static void
 855initfont(const char *fontstr) {
 856	char *def, **missing;
 857	int i, n;
 858
 859	missing = NULL;
 860	if(dc.font.set)
 861		XFreeFontSet(dpy, dc.font.set);
 862	dc.font.set = XCreateFontSet(dpy, fontstr, &missing, &n, &def);
 863	if(missing) {
 864		while(n--)
 865			fprintf(stderr, "dwm: missing fontset: %s\n", missing[n]);
 866		XFreeStringList(missing);
 867	}
 868	if(dc.font.set) {
 869		XFontSetExtents *font_extents;
 870		XFontStruct **xfonts;
 871		char **font_names;
 872		dc.font.ascent = dc.font.descent = 0;
 873		font_extents = XExtentsOfFontSet(dc.font.set);
 874		n = XFontsOfFontSet(dc.font.set, &xfonts, &font_names);
 875		for(i = 0, dc.font.ascent = 0, dc.font.descent = 0; i < n; i++) {
 876			if(dc.font.ascent < (*xfonts)->ascent)
 877				dc.font.ascent = (*xfonts)->ascent;
 878			if(dc.font.descent < (*xfonts)->descent)
 879				dc.font.descent = (*xfonts)->descent;
 880			xfonts++;
 881		}
 882	}
 883	else {
 884		if(dc.font.xfont)
 885			XFreeFont(dpy, dc.font.xfont);
 886		dc.font.xfont = NULL;
 887		if(!(dc.font.xfont = XLoadQueryFont(dpy, fontstr))
 888		|| !(dc.font.xfont = XLoadQueryFont(dpy, "fixed")))
 889			eprint("error, cannot load font: '%s'\n", fontstr);
 890		dc.font.ascent = dc.font.xfont->ascent;
 891		dc.font.descent = dc.font.xfont->descent;
 892	}
 893	dc.font.height = dc.font.ascent + dc.font.descent;
 894}
 895
 896static void
 897initlayouts(void) {
 898	unsigned int i, w;
 899
 900	nlayouts = sizeof layouts / sizeof layouts[0];
 901	for(blw = i = 0; i < nlayouts; i++) {
 902		w = textw(layouts[i].symbol);
 903		if(w > blw)
 904			blw = w;
 905	}
 906}
 907
 908static void
 909initstyle(void) {
 910	dc.norm[ColBorder] = initcolor(NORMBORDERCOLOR);
 911	dc.norm[ColBG] = initcolor(NORMBGCOLOR);
 912	dc.norm[ColFG] = initcolor(NORMFGCOLOR);
 913	dc.sel[ColBorder] = initcolor(SELBORDERCOLOR);
 914	dc.sel[ColBG] = initcolor(SELBGCOLOR);
 915	dc.sel[ColFG] = initcolor(SELFGCOLOR);
 916	initfont(FONT);
 917	dc.h = bh = dc.font.height + 2;
 918}
 919
 920static Bool
 921isarrange(void (*func)())
 922{
 923	return func == layouts[ltidx].arrange;
 924}
 925
 926static Bool
 927isfloating(void) {
 928	return layouts[ltidx].arrange == floating;
 929}
 930
 931static Bool
 932isoccupied(unsigned int t) {
 933	Client *c;
 934
 935	for(c = clients; c; c = c->next)
 936		if(c->tags[t])
 937			return True;
 938	return False;
 939}
 940
 941static Bool
 942isprotodel(Client *c) {
 943	int i, n;
 944	Atom *protocols;
 945	Bool ret = False;
 946
 947	if(XGetWMProtocols(dpy, c->win, &protocols, &n)) {
 948		for(i = 0; !ret && i < n; i++)
 949			if(protocols[i] == wmatom[WMDelete])
 950				ret = True;
 951		XFree(protocols);
 952	}
 953	return ret;
 954}
 955
 956static Bool
 957isvisible(Client *c) {
 958	unsigned int i;
 959
 960	for(i = 0; i < ntags; i++)
 961		if(c->tags[i] && seltags[i])
 962			return True;
 963	return False;
 964}
 965
 966static void
 967keypress(XEvent *e) {
 968	KEYS
 969	unsigned int len = sizeof keys / sizeof keys[0];
 970	unsigned int i;
 971	KeyCode code;
 972	KeySym keysym;
 973	XKeyEvent *ev;
 974
 975	if(!e) { /* grabkeys */
 976		XUngrabKey(dpy, AnyKey, AnyModifier, root);
 977		for(i = 0; i < len; i++) {
 978			code = XKeysymToKeycode(dpy, keys[i].keysym);
 979			XGrabKey(dpy, code, keys[i].mod, root, True,
 980					GrabModeAsync, GrabModeAsync);
 981			XGrabKey(dpy, code, keys[i].mod | LockMask, root, True,
 982					GrabModeAsync, GrabModeAsync);
 983			XGrabKey(dpy, code, keys[i].mod | numlockmask, root, True,
 984					GrabModeAsync, GrabModeAsync);
 985			XGrabKey(dpy, code, keys[i].mod | numlockmask | LockMask, root, True,
 986					GrabModeAsync, GrabModeAsync);
 987		}
 988		return;
 989	}
 990	ev = &e->xkey;
 991	keysym = XKeycodeToKeysym(dpy, (KeyCode)ev->keycode, 0);
 992	for(i = 0; i < len; i++)
 993		if(keysym == keys[i].keysym
 994		&& CLEANMASK(keys[i].mod) == CLEANMASK(ev->state))
 995		{
 996			if(keys[i].func)
 997				keys[i].func(keys[i].arg);
 998		}
 999}
1000
1001static void
1002killclient(const char *arg) {
1003	XEvent ev;
1004
1005	if(!sel)
1006		return;
1007	if(isprotodel(sel)) {
1008		ev.type = ClientMessage;
1009		ev.xclient.window = sel->win;
1010		ev.xclient.message_type = wmatom[WMProtocols];
1011		ev.xclient.format = 32;
1012		ev.xclient.data.l[0] = wmatom[WMDelete];
1013		ev.xclient.data.l[1] = CurrentTime;
1014		XSendEvent(dpy, sel->win, False, NoEventMask, &ev);
1015	}
1016	else
1017		XKillClient(dpy, sel->win);
1018}
1019
1020static void
1021leavenotify(XEvent *e) {
1022	XCrossingEvent *ev = &e->xcrossing;
1023
1024	if((ev->window == root) && !ev->same_screen) {
1025		selscreen = False;
1026		focus(NULL);
1027	}
1028}
1029
1030static void
1031manage(Window w, XWindowAttributes *wa) {
1032	unsigned int i;
1033	Client *c, *t = NULL;
1034	Window trans;
1035	Status rettrans;
1036	XWindowChanges wc;
1037
1038	c = emallocz(sizeof(Client));
1039	c->tags = emallocz(ntags * sizeof(Bool));
1040	c->win = w;
1041	c->x = wa->x;
1042	c->y = wa->y;
1043	c->w = wa->width;
1044	c->h = wa->height;
1045	c->oldborder = wa->border_width;
1046	if(c->w == sw && c->h == sh) {
1047		c->x = sx;
1048		c->y = sy;
1049		c->border = wa->border_width;
1050	}
1051	else {
1052		if(c->x + c->w + 2 * c->border > wax + waw)
1053			c->x = wax + waw - c->w - 2 * c->border;
1054		if(c->y + c->h + 2 * c->border > way + wah)
1055			c->y = way + wah - c->h - 2 * c->border;
1056		if(c->x < wax)
1057			c->x = wax;
1058		if(c->y < way)
1059			c->y = way;
1060		c->border = BORDERPX;
1061	}
1062	wc.border_width = c->border;
1063	XConfigureWindow(dpy, w, CWBorderWidth, &wc);
1064	XSetWindowBorder(dpy, w, dc.norm[ColBorder]);
1065	configure(c); /* propagates border_width, if size doesn't change */
1066	updatesizehints(c);
1067	XSelectInput(dpy, w,
1068		StructureNotifyMask | PropertyChangeMask | EnterWindowMask);
1069	grabbuttons(c, False);
1070	updatetitle(c);
1071	if((rettrans = XGetTransientForHint(dpy, w, &trans) == Success))
1072		for(t = clients; t && t->win != trans; t = t->next);
1073	if(t)
1074		for(i = 0; i < ntags; i++)
1075			c->tags[i] = t->tags[i];
1076	applyrules(c);
1077	if(!c->isfloating)
1078		c->isfloating = (rettrans == Success) || c->isfixed;
1079	attach(c);
1080	attachstack(c);
1081	XMoveResizeWindow(dpy, c->win, c->x, c->y, c->w, c->h); /* some windows require this */
1082	ban(c);
1083	XMapWindow(dpy, c->win);
1084	setclientstate(c, NormalState);
1085	arrange();
1086}
1087
1088static void
1089mappingnotify(XEvent *e) {
1090	XMappingEvent *ev = &e->xmapping;
1091
1092	XRefreshKeyboardMapping(ev);
1093	if(ev->request == MappingKeyboard)
1094		keypress(NULL);
1095}
1096
1097static void
1098maprequest(XEvent *e) {
1099	static XWindowAttributes wa;
1100	XMapRequestEvent *ev = &e->xmaprequest;
1101
1102	if(!XGetWindowAttributes(dpy, ev->window, &wa))
1103		return;
1104	if(wa.override_redirect)
1105		return;
1106	if(!getclient(ev->window))
1107		manage(ev->window, &wa);
1108}
1109
1110static void
1111movemouse(Client *c) {
1112	int x1, y1, ocx, ocy, di, nx, ny;
1113	unsigned int dui;
1114	Window dummy;
1115	XEvent ev;
1116
1117	ocx = nx = c->x;
1118	ocy = ny = c->y;
1119	if(XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync,
1120			None, cursor[CurMove], CurrentTime) != GrabSuccess)
1121		return;
1122	c->ismax = False;
1123	XQueryPointer(dpy, root, &dummy, &dummy, &x1, &y1, &di, &di, &dui);
1124	for(;;) {
1125		XMaskEvent(dpy, MOUSEMASK | ExposureMask | SubstructureRedirectMask, &ev);
1126		switch (ev.type) {
1127		case ButtonRelease:
1128			XUngrabPointer(dpy, CurrentTime);
1129			return;
1130		case ConfigureRequest:
1131		case Expose:
1132		case MapRequest:
1133			handler[ev.type](&ev);
1134			break;
1135		case MotionNotify:
1136			XSync(dpy, False);
1137			nx = ocx + (ev.xmotion.x - x1);
1138			ny = ocy + (ev.xmotion.y - y1);
1139			if(abs(wax + nx) < SNAP)
1140				nx = wax;
1141			else if(abs((wax + waw) - (nx + c->w + 2 * c->border)) < SNAP)
1142				nx = wax + waw - c->w - 2 * c->border;
1143			if(abs(way - ny) < SNAP)
1144				ny = way;
1145			else if(abs((way + wah) - (ny + c->h + 2 * c->border)) < SNAP)
1146				ny = way + wah - c->h - 2 * c->border;
1147			resize(c, nx, ny, c->w, c->h, False);
1148			break;
1149		}
1150	}
1151}
1152
1153static Client *
1154nexttiled(Client *c) {
1155	for(; c && (c->isfloating || !isvisible(c)); c = c->next);
1156	return c;
1157}
1158
1159static void
1160propertynotify(XEvent *e) {
1161	Client *c;
1162	Window trans;
1163	XPropertyEvent *ev = &e->xproperty;
1164
1165	if(ev->state == PropertyDelete)
1166		return; /* ignore */
1167	if((c = getclient(ev->window))) {
1168		switch (ev->atom) {
1169			default: break;
1170			case XA_WM_TRANSIENT_FOR:
1171				XGetTransientForHint(dpy, c->win, &trans);
1172				if(!c->isfloating && (c->isfloating = (getclient(trans) != NULL)))
1173					arrange();
1174				break;
1175			case XA_WM_NORMAL_HINTS:
1176				updatesizehints(c);
1177				break;
1178		}
1179		if(ev->atom == XA_WM_NAME || ev->atom == netatom[NetWMName]) {
1180			updatetitle(c);
1181			if(c == sel)
1182				drawbar();
1183		}
1184	}
1185}
1186
1187static void
1188quit(const char *arg) {
1189	readin = running = False;
1190}
1191
1192static void
1193resize(Client *c, int x, int y, int w, int h, Bool sizehints) {
1194	double dx, dy, max, min, ratio;
1195	XWindowChanges wc; 
1196
1197	if(sizehints) {
1198		if(c->minay > 0 && c->maxay > 0 && (h - c->baseh) > 0 && (w - c->basew) > 0) {
1199			dx = (double)(w - c->basew);
1200			dy = (double)(h - c->baseh);
1201			min = (double)(c->minax) / (double)(c->minay);
1202			max = (double)(c->maxax) / (double)(c->maxay);
1203			ratio = dx / dy;
1204			if(max > 0 && min > 0 && ratio > 0) {
1205				if(ratio < min) {
1206					dy = (dx * min + dy) / (min * min + 1);
1207					dx = dy * min;
1208					w = (int)dx + c->basew;
1209					h = (int)dy + c->baseh;
1210				}
1211				else if(ratio > max) {
1212					dy = (dx * min + dy) / (max * max + 1);
1213					dx = dy * min;
1214					w = (int)dx + c->basew;
1215					h = (int)dy + c->baseh;
1216				}
1217			}
1218		}
1219		if(c->minw && w < c->minw)
1220			w = c->minw;
1221		if(c->minh && h < c->minh)
1222			h = c->minh;
1223		if(c->maxw && w > c->maxw)
1224			w = c->maxw;
1225		if(c->maxh && h > c->maxh)
1226			h = c->maxh;
1227		if(c->incw)
1228			w -= (w - c->basew) % c->incw;
1229		if(c->inch)
1230			h -= (h - c->baseh) % c->inch;
1231	}
1232	if(w <= 0 || h <= 0)
1233		return;
1234	/* offscreen appearance fixes */
1235	if(x > sw)
1236		x = sw - w - 2 * c->border;
1237	if(y > sh)
1238		y = sh - h - 2 * c->border;
1239	if(x + w + 2 * c->border < sx)
1240		x = sx;
1241	if(y + h + 2 * c->border < sy)
1242		y = sy;
1243	if(c->x != x || c->y != y || c->w != w || c->h != h) {
1244		c->x = wc.x = x;
1245		c->y = wc.y = y;
1246		c->w = wc.width = w;
1247		c->h = wc.height = h;
1248		wc.border_width = c->border;
1249		XConfigureWindow(dpy, c->win, CWX | CWY | CWWidth | CWHeight | CWBorderWidth, &wc);
1250		configure(c);
1251		XSync(dpy, False);
1252	}
1253}
1254
1255static void
1256resizemouse(Client *c) {
1257	int ocx, ocy;
1258	int nw, nh;
1259	XEvent ev;
1260
1261	ocx = c->x;
1262	ocy = c->y;
1263	if(XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync,
1264			None, cursor[CurResize], CurrentTime) != GrabSuccess)
1265		return;
1266	c->ismax = False;
1267	XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->border - 1, c->h + c->border - 1);
1268	for(;;) {
1269		XMaskEvent(dpy, MOUSEMASK | ExposureMask | SubstructureRedirectMask , &ev);
1270		switch(ev.type) {
1271		case ButtonRelease:
1272			XWarpPointer(dpy, None, c->win, 0, 0, 0, 0,
1273					c->w + c->border - 1, c->h + c->border - 1);
1274			XUngrabPointer(dpy, CurrentTime);
1275			while(XCheckMaskEvent(dpy, EnterWindowMask, &ev));
1276			return;
1277		case ConfigureRequest:
1278		case Expose:
1279		case MapRequest:
1280			handler[ev.type](&ev);
1281			break;
1282		case MotionNotify:
1283			XSync(dpy, False);
1284			if((nw = ev.xmotion.x - ocx - 2 * c->border + 1) <= 0)
1285				nw = 1;
1286			if((nh = ev.xmotion.y - ocy - 2 * c->border + 1) <= 0)
1287				nh = 1;
1288			resize(c, c->x, c->y, nw, nh, True);
1289			break;
1290		}
1291	}
1292}
1293
1294static void
1295restack(void) {
1296	Client *c;
1297	XEvent ev;
1298	XWindowChanges wc;
1299
1300	drawbar();
1301	if(!sel)
1302		return;
1303	if(sel->isfloating || isfloating())
1304		XRaiseWindow(dpy, sel->win);
1305	if(!isfloating()) {
1306		wc.stack_mode = Below;
1307		wc.sibling = barwin;
1308		if(!sel->isfloating) {
1309			XConfigureWindow(dpy, sel->win, CWSibling | CWStackMode, &wc);
1310			wc.sibling = sel->win;
1311		}
1312		for(c = nexttiled(clients); c; c = nexttiled(c->next)) {
1313			if(c == sel)
1314				continue;
1315			XConfigureWindow(dpy, c->win, CWSibling | CWStackMode, &wc);
1316			wc.sibling = c->win;
1317		}
1318	}
1319	XSync(dpy, False);
1320	while(XCheckMaskEvent(dpy, EnterWindowMask, &ev));
1321}
1322
1323static void
1324scan(void) {
1325	unsigned int i, num;
1326	Window *wins, d1, d2;
1327	XWindowAttributes wa;
1328
1329	wins = NULL;
1330	if(XQueryTree(dpy, root, &d1, &d2, &wins, &num)) {
1331		for(i = 0; i < num; i++) {
1332			if(!XGetWindowAttributes(dpy, wins[i], &wa)
1333			|| wa.override_redirect || XGetTransientForHint(dpy, wins[i], &d1))
1334				continue;
1335			if(wa.map_state == IsViewable || getstate(wins[i]) == IconicState)
1336				manage(wins[i], &wa);
1337		}
1338		for(i = 0; i < num; i++) { /* now the transients */
1339			if(!XGetWindowAttributes(dpy, wins[i], &wa))
1340				continue;
1341			if(XGetTransientForHint(dpy, wins[i], &d1)
1342			&& (wa.map_state == IsViewable || getstate(wins[i]) == IconicState))
1343				manage(wins[i], &wa);
1344		}
1345	}
1346	if(wins)
1347		XFree(wins);
1348}
1349
1350static void
1351setclientstate(Client *c, long state) {
1352	long data[] = {state, None};
1353
1354	XChangeProperty(dpy, c->win, wmatom[WMState], wmatom[WMState], 32,
1355			PropModeReplace, (unsigned char *)data, 2);
1356}
1357
1358static void
1359setlayout(const char *arg) {
1360	unsigned int i;
1361
1362	if(!arg) {
1363		if(++ltidx == nlayouts)
1364			ltidx = 0;;
1365	}
1366	else {
1367		for(i = 0; i < nlayouts; i++)
1368			if(!strcmp(arg, layouts[i].symbol))
1369				break;
1370		if(i == nlayouts)
1371			return;
1372		ltidx = i;
1373	}
1374	if(sel)
1375		arrange();
1376	else
1377		drawbar();
1378}
1379
1380static void
1381setmwfact(const char *arg) {
1382	double delta;
1383
1384	if(!isarrange(tile))
1385		return;
1386	/* arg handling, manipulate mwfact */
1387	if(arg == NULL)
1388		mwfact = MWFACT;
1389	else if(1 == sscanf(arg, "%lf", &delta)) {
1390		if(arg[0] != '+' && arg[0] != '-')
1391			mwfact = delta;
1392		else
1393			mwfact += delta;
1394		if(mwfact < 0.1)
1395			mwfact = 0.1;
1396		else if(mwfact > 0.9)
1397			mwfact = 0.9;
1398	}
1399	arrange();
1400}
1401
1402static void
1403setup(void) {
1404	int i, j;
1405	unsigned int mask;
1406	Window w;
1407	XModifierKeymap *modmap;
1408	XSetWindowAttributes wa;
1409
1410	/* init atoms */
1411	wmatom[WMProtocols] = XInternAtom(dpy, "WM_PROTOCOLS", False);
1412	wmatom[WMDelete] = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
1413	wmatom[WMName] = XInternAtom(dpy, "WM_NAME", False);
1414	wmatom[WMState] = XInternAtom(dpy, "WM_STATE", False);
1415	netatom[NetSupported] = XInternAtom(dpy, "_NET_SUPPORTED", False);
1416	netatom[NetWMName] = XInternAtom(dpy, "_NET_WM_NAME", False);
1417	XChangeProperty(dpy, root, netatom[NetSupported], XA_ATOM, 32,
1418			PropModeReplace, (unsigned char *) netatom, NetLast);
1419	/* init cursors */
1420	cursor[CurNormal] = XCreateFontCursor(dpy, XC_left_ptr);
1421	cursor[CurResize] = XCreateFontCursor(dpy, XC_sizing);
1422	cursor[CurMove] = XCreateFontCursor(dpy, XC_fleur);
1423	/* init modifier map */
1424	modmap = XGetModifierMapping(dpy);
1425	for (i = 0; i < 8; i++)
1426		for (j = 0; j < modmap->max_keypermod; j++) {
1427			if(modmap->modifiermap[i * modmap->max_keypermod + j]
1428					== XKeysymToKeycode(dpy, XK_Num_Lock))
1429				numlockmask = (1 << i);
1430		}
1431	XFreeModifiermap(modmap);
1432	/* select for events */
1433	wa.event_mask = SubstructureRedirectMask | SubstructureNotifyMask
1434		| EnterWindowMask | LeaveWindowMask | StructureNotifyMask;
1435	wa.cursor = cursor[CurNormal];
1436	XChangeWindowAttributes(dpy, root, CWEventMask | CWCursor, &wa);
1437	XSelectInput(dpy, root, wa.event_mask);
1438	keypress(NULL); /* grabkeys */
1439	compileregs();
1440	for(ntags = 0; tags[ntags]; ntags++);
1441	seltags = emallocz(sizeof(Bool) * ntags);
1442	seltags[0] = True;
1443	/* geometry */
1444	sx = sy = 0;
1445	sw = DisplayWidth(dpy, screen);
1446	sh = DisplayHeight(dpy, screen);
1447	initstyle();
1448	initlayouts();
1449	initbar();
1450	/* multihead support */
1451	selscreen = XQueryPointer(dpy, root, &w, &w, &i, &i, &i, &i, &mask);
1452}
1453
1454static void
1455spawn(const char *arg) {
1456	static char *shell = NULL;
1457
1458	if(!shell && !(shell = getenv("SHELL")))
1459		shell = "/bin/sh";
1460	if(!arg)
1461		return;
1462	/* The double-fork construct avoids zombie processes and keeps the code
1463	 * clean from stupid signal handlers. */
1464	if(fork() == 0) {
1465		if(fork() == 0) {
1466			if(dpy)
1467				close(ConnectionNumber(dpy));
1468			setsid();
1469			execl(shell, shell, "-c", arg, (char *)NULL);
1470			fprintf(stderr, "dwm: execl '%s -c %s'", shell, arg);
1471			perror(" failed");
1472		}
1473		exit(0);
1474	}
1475	wait(0);
1476}
1477
1478static void
1479tag(const char *arg) {
1480	unsigned int i;
1481
1482	if(!sel)
1483		return;
1484	for(i = 0; i < ntags; i++)
1485		sel->tags[i] = arg == NULL;
1486	i = idxoftag(arg);
1487	if(i >= 0 && i < ntags)
1488		sel->tags[i] = True;
1489	arrange();
1490}
1491
1492static unsigned int
1493textnw(const char *text, unsigned int len) {
1494	XRectangle r;
1495
1496	if(dc.font.set) {
1497		XmbTextExtents(dc.font.set, text, len, NULL, &r);
1498		return r.width;
1499	}
1500	return XTextWidth(dc.font.xfont, text, len);
1501}
1502
1503static unsigned int
1504textw(const char *text) {
1505	return textnw(text, strlen(text)) + dc.font.height;
1506}
1507
1508static void
1509tile(void) {
1510	unsigned int i, n, nx, ny, nw, nh, mw, th;
1511	Client *c;
1512
1513	for(n = 0, c = nexttiled(clients); c; c = nexttiled(c->next))
1514		n++;
1515
1516	/* window geoms */
1517	mw = (n == 1) ? waw : mwfact * waw;
1518	th = (n > 1) ? wah / (n - 1) : 0;
1519	if(n > 1 && th < bh)
1520		th = wah;
1521
1522	nx = wax;
1523	ny = way;
1524	for(i = 0, c = nexttiled(clients); c; c = nexttiled(c->next), i++) {
1525		c->ismax = False;
1526		if(i == 0) { /* master */
1527			nw = mw - 2 * c->border;
1528			nh = wah - 2 * c->border;
1529		}
1530		else {  /* tile window */
1531			if(i == 1) {
1532				ny = way;
1533				nx += mw;
1534			}
1535			nw = waw - mw - 2 * c->border;
1536			if(i + 1 == n) /* remainder */
1537				nh = (way + wah) - ny - 2 * c->border;
1538			else
1539				nh = th - 2 * c->border;
1540		}
1541		resize(c, nx, ny, nw, nh, RESIZEHINTS);
1542		if(n > 1 && th != wah)
1543			ny += nh + 2 * c->border;
1544	}
1545}
1546
1547static void
1548togglebar(const char *arg) {
1549	if(bpos == BarOff)
1550		bpos = (BARPOS == BarOff) ? BarTop : BARPOS;
1551	else
1552		bpos = BarOff;
1553	updatebarpos();
1554	arrange();
1555}
1556
1557static void
1558togglefloating(const char *arg) {
1559	if(!sel)
1560		return;
1561	sel->isfloating = !sel->isfloating;
1562	if(sel->isfloating)
1563		resize(sel, sel->x, sel->y, sel->w, sel->h, True);
1564	arrange();
1565}
1566
1567static void
1568togglemax(const char *arg) {
1569	XEvent ev;
1570
1571	if(!sel || (!isfloating() && !sel->isfloating) || sel->isfixed)
1572		return;
1573	if((sel->ismax = !sel->ismax)) {
1574		sel->rx = sel->x;
1575		sel->ry = sel->y;
1576		sel->rw = sel->w;
1577		sel->rh = sel->h;
1578		resize(sel, wax, way, waw - 2 * sel->border, wah - 2 * sel->border, True);
1579	}
1580	else
1581		resize(sel, sel->rx, sel->ry, sel->rw, sel->rh, True);
1582	drawbar();
1583	while(XCheckMaskEvent(dpy, EnterWindowMask, &ev));
1584}
1585
1586static void
1587toggletag(const char *arg) {
1588	unsigned int i, j;
1589
1590	if(!sel)
1591		return;
1592	i = idxoftag(arg);
1593	sel->tags[i] = !sel->tags[i];
1594	for(j = 0; j < ntags && !sel->tags[j]; j++);
1595	if(j == ntags)
1596		sel->tags[i] = True;
1597	arrange();
1598}
1599
1600static void
1601toggleview(const char *arg) {
1602	unsigned int i, j;
1603
1604	i = idxoftag(arg);
1605	seltags[i] = !seltags[i];
1606	for(j = 0; j < ntags && !seltags[j]; j++);
1607	if(j == ntags)
1608		seltags[i] = True; /* cannot toggle last view */
1609	arrange();
1610}
1611
1612static void
1613unban(Client *c) {
1614	if(!c->isbanned)
1615		return;
1616	XMoveWindow(dpy, c->win, c->x, c->y);
1617	c->isbanned = False;
1618}
1619
1620static void
1621unmanage(Client *c) {
1622	XWindowChanges wc;
1623
1624	wc.border_width = c->oldborder;
1625	/* The server grab construct avoids race conditions. */
1626	XGrabServer(dpy);
1627	XSetErrorHandler(xerrordummy);
1628	XConfigureWindow(dpy, c->win, CWBorderWidth, &wc); /* restore border */
1629	detach(c);
1630	detachstack(c);
1631	if(sel == c)
1632		focus(NULL);
1633	XUngrabButton(dpy, AnyButton, AnyModifier, c->win);
1634	setclientstate(c, WithdrawnState);
1635	free(c->tags);
1636	free(c);
1637	XSync(dpy, False);
1638	XSetErrorHandler(xerror);
1639	XUngrabServer(dpy);
1640	arrange();
1641}
1642
1643static void
1644unmapnotify(XEvent *e) {
1645	Client *c;
1646	XUnmapEvent *ev = &e->xunmap;
1647
1648	if((c = getclient(ev->window)))
1649		unmanage(c);
1650}
1651
1652static void
1653updatebarpos(void) {
1654	XEvent ev;
1655
1656	wax = sx;
1657	way = sy;
1658	wah = sh;
1659	waw = sw;
1660	switch(bpos) {
1661	default:
1662		wah -= bh;
1663		way += bh;
1664		XMoveWindow(dpy, barwin, sx, sy);
1665		break;
1666	case BarBot:
1667		wah -= bh;
1668		XMoveWindow(dpy, barwin, sx, sy + wah);
1669		break;
1670	case BarOff:
1671		XMoveWindow(dpy, barwin, sx, sy - bh);
1672		break;
1673	}
1674	XSync(dpy, False);
1675	while(XCheckMaskEvent(dpy, EnterWindowMask, &ev));
1676}
1677
1678static void
1679updatesizehints(Client *c) {
1680	long msize;
1681	XSizeHints size;
1682
1683	if(!XGetWMNormalHints(dpy, c->win, &size, &msize) || !size.flags)
1684		size.flags = PSize;
1685	c->flags = size.flags;
1686	if(c->flags & PBaseSize) {
1687		c->basew = size.base_width;
1688		c->baseh = size.base_height;
1689	}
1690	else if(c->flags & PMinSize) {
1691		c->basew = size.min_width;
1692		c->baseh = size.min_height;
1693	}
1694	else
1695		c->basew = c->baseh = 0;
1696	if(c->flags & PResizeInc) {
1697		c->incw = size.width_inc;
1698		c->inch = size.height_inc;
1699	}
1700	else
1701		c->incw = c->inch = 0;
1702	if(c->flags & PMaxSize) {
1703		c->maxw = size.max_width;
1704		c->maxh = size.max_height;
1705	}
1706	else
1707		c->maxw = c->maxh = 0;
1708	if(c->flags & PMinSize) {
1709		c->minw = size.min_width;
1710		c->minh = size.min_height;
1711	}
1712	else if(c->flags & PBaseSize) {
1713		c->minw = size.base_width;
1714		c->minh = size.base_height;
1715	}
1716	else
1717		c->minw = c->minh = 0;
1718	if(c->flags & PAspect) {
1719		c->minax = size.min_aspect.x;
1720		c->maxax = size.max_aspect.x;
1721		c->minay = size.min_aspect.y;
1722		c->maxay = size.max_aspect.y;
1723	}
1724	else
1725		c->minax = c->maxax = c->minay = c->maxay = 0;
1726	c->isfixed = (c->maxw && c->minw && c->maxh && c->minh
1727			&& c->maxw == c->minw && c->maxh == c->minh);
1728}
1729
1730static void
1731updatetitle(Client *c) {
1732	if(!gettextprop(c->win, netatom[NetWMName], c->name, sizeof c->name))
1733		gettextprop(c->win, wmatom[WMName], c->name, sizeof c->name);
1734}
1735
1736/* There's no way to check accesses to destroyed windows, thus those cases are
1737 * ignored (especially on UnmapNotify's).  Other types of errors call Xlibs
1738 * default error handler, which may call exit.  */
1739static int
1740xerror(Display *dpy, XErrorEvent *ee) {
1741	if(ee->error_code == BadWindow
1742	|| (ee->request_code == X_SetInputFocus && ee->error_code == BadMatch)
1743	|| (ee->request_code == X_PolyText8 && ee->error_code == BadDrawable)
1744	|| (ee->request_code == X_PolyFillRectangle && ee->error_code == BadDrawable)
1745	|| (ee->request_code == X_PolySegment && ee->error_code == BadDrawable)
1746	|| (ee->request_code == X_ConfigureWindow && ee->error_code == BadMatch)
1747	|| (ee->request_code == X_GrabKey && ee->error_code == BadAccess)
1748	|| (ee->request_code == X_CopyArea && ee->error_code == BadDrawable))
1749		return 0;
1750	fprintf(stderr, "dwm: fatal error: request code=%d, error code=%d\n",
1751		ee->request_code, ee->error_code);
1752	return xerrorxlib(dpy, ee); /* may call exit */
1753}
1754
1755static int
1756xerrordummy(Display *dsply, XErrorEvent *ee) {
1757	return 0;
1758}
1759
1760/* Startup Error handler to check if another window manager
1761 * is already running. */
1762static int
1763xerrorstart(Display *dsply, XErrorEvent *ee) {
1764	otherwm = True;
1765	return -1;
1766}
1767
1768static void
1769view(const char *arg) {
1770	unsigned int i;
1771
1772	for(i = 0; i < ntags; i++)
1773		seltags[i] = arg == NULL;
1774	i = idxoftag(arg);
1775	if(i >= 0 && i < ntags)
1776		seltags[i] = True;
1777	arrange();
1778}
1779
1780static void
1781zoom(const char *arg) {
1782	Client *c;
1783
1784	if(!sel || !isarrange(tile) || sel->isfloating)
1785		return;
1786	if((c = sel) == nexttiled(clients))
1787		if(!(c = nexttiled(c->next)))
1788			return;
1789	detach(c);
1790	attach(c);
1791	focus(c);
1792	arrange();
1793}
1794
1795int
1796main(int argc, char *argv[]) {
1797	char *p;
1798	int r, xfd;
1799	fd_set rd;
1800	XEvent ev;
1801
1802	if(argc == 2 && !strcmp("-v", argv[1]))
1803		eprint("dwm-"VERSION", © 2006-2007 A. R. Garbe, S. van Dijk, J. Salmi, P. Hruby, S. Nagy\n");
1804	else if(argc != 1)
1805		eprint("usage: dwm [-v]\n");
1806
1807	/* macros from config.h can be used at function level only */
1808	mwfact = MWFACT;
1809	bpos = BARPOS;
1810
1811	setlocale(LC_CTYPE, "");
1812	if(!(dpy = XOpenDisplay(0)))
1813		eprint("dwm: cannot open display\n");
1814	xfd = ConnectionNumber(dpy);
1815	screen = DefaultScreen(dpy);
1816	root = RootWindow(dpy, screen);
1817	otherwm = False;
1818	XSetErrorHandler(xerrorstart);
1819	/* this causes an error if some other window manager is running */
1820	XSelectInput(dpy, root, SubstructureRedirectMask);
1821	XSync(dpy, False);
1822	if(otherwm)
1823		eprint("dwm: another window manager is already running\n");
1824
1825	XSync(dpy, False);
1826	XSetErrorHandler(NULL);
1827	xerrorxlib = XSetErrorHandler(xerror);
1828	XSync(dpy, False);
1829	setup();
1830	drawbar();
1831	scan();
1832
1833	/* main event loop, also reads status text from stdin */
1834	XSync(dpy, False);
1835	readin = True;
1836	while(running) {
1837		FD_ZERO(&rd);
1838		if(readin)
1839			FD_SET(STDIN_FILENO, &rd);
1840		FD_SET(xfd, &rd);
1841		if(select(xfd + 1, &rd, NULL, NULL, NULL) == -1) {
1842			if(errno == EINTR)
1843				continue;
1844			eprint("select failed\n");
1845		}
1846		if(FD_ISSET(STDIN_FILENO, &rd)) {
1847			switch(r = read(STDIN_FILENO, stext, sizeof stext - 1)) {
1848			case -1:
1849				strncpy(stext, strerror(errno), sizeof stext - 1);
1850				stext[sizeof stext - 1] = '\0';
1851				readin = False;
1852				break;
1853			case 0:
1854				strncpy(stext, "EOF", 4);
1855				readin = False;
1856				break;
1857			default:
1858				for(stext[r] = '\0', p = stext + strlen(stext) - 1; p >= stext && *p == '\n'; *p-- = '\0');
1859				for(; p >= stext && *p != '\n'; --p);
1860				if(p > stext)
1861					strncpy(stext, p + 1, sizeof stext);
1862			}
1863			drawbar();
1864		}
1865		while(XPending(dpy)) {
1866			XNextEvent(dpy, &ev);
1867			if(handler[ev.type])
1868				(handler[ev.type])(&ev); /* call handler */
1869		}
1870	}
1871	cleanup();
1872	XCloseDisplay(dpy);
1873	return 0;
1874}