all repos — dwm @ 00ca643bd7ccba6efd001cc679525a53a3c75586

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