all repos — dwm @ 985e3305249987739867f33879ddbb8bc3028e00

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