all repos — dwm @ a6df995b5d4efabc243d6f564db356fc406c6601

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