all repos — dwm @ 0235a84ef286db5c97239615ae525ce973641941

fork of suckless dynamic window manager

dwm.c (view raw)

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