all repos — dwm @ e0d6451086eba0642cff83c2a8e05ff84be60488

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