all repos — dwm @ dba22848c7d077c1a988a901c9390dc3c8cc9d64

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