all repos — dwm @ a785a0d71213c2ab628778727c4354ad5bb517fb

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