dwm

Simple dynamic window manager for X
git clone https://git.sinitax.com/suckless/dwm
Log | Files | Refs | README | LICENSE | sfeed.txt

dwm.c (53291B)


      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 * The event handlers of dwm are organized in an array which is accessed
     10 * whenever a new event has been fetched. This allows event dispatching
     11 * in O(1) time.
     12 *
     13 * Each child of the root window is called a client, except windows which have
     14 * set the override_redirect flag. Clients are organized in a linked client
     15 * list on each monitor, the focus history is remembered through a stack list
     16 * on each monitor. Each client contains a bit array to indicate the tags of a
     17 * client.
     18 *
     19 * Keys and tagging rules are organized as arrays and defined in config.h.
     20 *
     21 * To understand everything else, start reading main().
     22 */
     23#include <errno.h>
     24#include <locale.h>
     25#include <signal.h>
     26#include <stdarg.h>
     27#include <stdio.h>
     28#include <stdlib.h>
     29#include <string.h>
     30#include <unistd.h>
     31#include <sys/types.h>
     32#include <sys/wait.h>
     33#include <X11/cursorfont.h>
     34#include <X11/keysym.h>
     35#include <X11/Xatom.h>
     36#include <X11/Xlib.h>
     37#include <X11/Xproto.h>
     38#include <X11/Xutil.h>
     39#ifdef XINERAMA
     40#include <X11/extensions/Xinerama.h>
     41#endif /* XINERAMA */
     42#include <X11/Xft/Xft.h>
     43
     44#include "drw.h"
     45#include "util.h"
     46
     47/* macros */
     48#define BUTTONMASK              (ButtonPressMask|ButtonReleaseMask)
     49#define CLEANMASK(mask)         (mask & ~(numlockmask|LockMask) & (ShiftMask|ControlMask|Mod1Mask|Mod2Mask|Mod3Mask|Mod4Mask|Mod5Mask))
     50#define INTERSECT(x,y,w,h,m)    (MAX(0, MIN((x)+(w),(m)->wx+(m)->ww) - MAX((x),(m)->wx)) \
     51                               * MAX(0, MIN((y)+(h),(m)->wy+(m)->wh) - MAX((y),(m)->wy)))
     52#define ISVISIBLE(C)            ((C->tags & C->mon->tagset[C->mon->seltags]))
     53#define LENGTH(X)               (sizeof X / sizeof X[0])
     54#define MOUSEMASK               (BUTTONMASK|PointerMotionMask)
     55#define WIDTH(X)                ((X)->w + 2 * (X)->bw)
     56#define HEIGHT(X)               ((X)->h + 2 * (X)->bw)
     57#define TAGMASK                 ((1 << LENGTH(tags)) - 1)
     58#define TEXTW(X)                (drw_fontset_getwidth(drw, (X)) + lrpad)
     59
     60/* enums */
     61enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */
     62enum { SchemeNorm, SchemeSel }; /* color schemes */
     63enum { NetSupported, NetWMName, NetWMState, NetWMCheck,
     64       NetWMFullscreen, NetActiveWindow, NetWMWindowType,
     65       NetWMWindowTypeDialog, NetClientList, NetLast }; /* EWMH atoms */
     66enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */
     67enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle,
     68       ClkClientWin, ClkRootWin, ClkLast }; /* clicks */
     69
     70typedef union {
     71	int i;
     72	unsigned int ui;
     73	float f;
     74	const void *v;
     75} Arg;
     76
     77typedef struct {
     78	unsigned int click;
     79	unsigned int mask;
     80	unsigned int button;
     81	void (*func)(const Arg *arg);
     82	const Arg arg;
     83} Button;
     84
     85typedef struct Monitor Monitor;
     86typedef struct Client Client;
     87struct Client {
     88	char name[256];
     89	float mina, maxa;
     90	int x, y, w, h;
     91	int oldx, oldy, oldw, oldh;
     92	int basew, baseh, incw, inch, maxw, maxh, minw, minh, hintsvalid;
     93	int bw, oldbw;
     94	unsigned int tags;
     95	int isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen;
     96	Client *next;
     97	Client *snext;
     98	Monitor *mon;
     99	Window win;
    100};
    101
    102typedef struct {
    103	unsigned int mod;
    104	KeySym keysym;
    105	void (*func)(const Arg *);
    106	const Arg arg;
    107} Key;
    108
    109typedef struct {
    110	const char *symbol;
    111	void (*arrange)(Monitor *);
    112} Layout;
    113
    114struct Monitor {
    115	char ltsymbol[16];
    116	float mfact;
    117	int nmaster;
    118	int num;
    119	int by;               /* bar geometry */
    120	int mx, my, mw, mh;   /* screen size */
    121	int wx, wy, ww, wh;   /* window area  */
    122	unsigned int seltags;
    123	unsigned int sellt;
    124	unsigned int tagset[2];
    125	int showbar;
    126	int topbar;
    127	Client *clients;
    128	Client *sel;
    129	Client *stack;
    130	Monitor *next;
    131	Window barwin;
    132	const Layout *lt[2];
    133};
    134
    135typedef struct {
    136	const char *class;
    137	const char *instance;
    138	const char *title;
    139	unsigned int tags;
    140	int isfloating;
    141	int monitor;
    142} Rule;
    143
    144/* function declarations */
    145static void applyrules(Client *c);
    146static int applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact);
    147static void arrange(Monitor *m);
    148static void arrangemon(Monitor *m);
    149static void attach(Client *c);
    150static void attachstack(Client *c);
    151static void buttonpress(XEvent *e);
    152static void checkotherwm(void);
    153static void cleanup(void);
    154static void cleanupmon(Monitor *mon);
    155static void clientmessage(XEvent *e);
    156static void configure(Client *c);
    157static void configurenotify(XEvent *e);
    158static void configurerequest(XEvent *e);
    159static Monitor *createmon(void);
    160static void destroynotify(XEvent *e);
    161static void detach(Client *c);
    162static void detachstack(Client *c);
    163static Monitor *dirtomon(int dir);
    164static void drawbar(Monitor *m);
    165static void drawbars(void);
    166static void enternotify(XEvent *e);
    167static void expose(XEvent *e);
    168static void focus(Client *c);
    169static void focusin(XEvent *e);
    170static void focusmon(const Arg *arg);
    171static void focusstack(const Arg *arg);
    172static Atom getatomprop(Client *c, Atom prop);
    173static int getrootptr(int *x, int *y);
    174static long getstate(Window w);
    175static int gettextprop(Window w, Atom atom, char *text, unsigned int size);
    176static void grabbuttons(Client *c, int focused);
    177static void grabkeys(void);
    178static void incnmaster(const Arg *arg);
    179static void keypress(XEvent *e);
    180static void killclient(const Arg *arg);
    181static void manage(Window w, XWindowAttributes *wa);
    182static void mappingnotify(XEvent *e);
    183static void maprequest(XEvent *e);
    184static void monocle(Monitor *m);
    185static void motionnotify(XEvent *e);
    186static void movemouse(const Arg *arg);
    187static Client *nexttiled(Client *c);
    188static void pop(Client *c);
    189static void propertynotify(XEvent *e);
    190static void quit(const Arg *arg);
    191static Monitor *recttomon(int x, int y, int w, int h);
    192static void resize(Client *c, int x, int y, int w, int h, int interact);
    193static void resizeclient(Client *c, int x, int y, int w, int h);
    194static void resizemouse(const Arg *arg);
    195static void restack(Monitor *m);
    196static void run(void);
    197static void scan(void);
    198static int sendevent(Client *c, Atom proto);
    199static void sendmon(Client *c, Monitor *m);
    200static void setclientstate(Client *c, long state);
    201static void setfocus(Client *c);
    202static void setfullscreen(Client *c, int fullscreen);
    203static void setlayout(const Arg *arg);
    204static void setmfact(const Arg *arg);
    205static void setup(void);
    206static void seturgent(Client *c, int urg);
    207static void showhide(Client *c);
    208static void spawn(const Arg *arg);
    209static void tag(const Arg *arg);
    210static void tagmon(const Arg *arg);
    211static void tile(Monitor *m);
    212static void togglebar(const Arg *arg);
    213static void togglefloating(const Arg *arg);
    214static void toggletag(const Arg *arg);
    215static void toggleview(const Arg *arg);
    216static void unfocus(Client *c, int setfocus);
    217static void unmanage(Client *c, int destroyed);
    218static void unmapnotify(XEvent *e);
    219static void updatebarpos(Monitor *m);
    220static void updatebars(void);
    221static void updateclientlist(void);
    222static int updategeom(void);
    223static void updatenumlockmask(void);
    224static void updatesizehints(Client *c);
    225static void updatestatus(void);
    226static void updatetitle(Client *c);
    227static void updatewindowtype(Client *c);
    228static void updatewmhints(Client *c);
    229static void view(const Arg *arg);
    230static Client *wintoclient(Window w);
    231static Monitor *wintomon(Window w);
    232static int xerror(Display *dpy, XErrorEvent *ee);
    233static int xerrordummy(Display *dpy, XErrorEvent *ee);
    234static int xerrorstart(Display *dpy, XErrorEvent *ee);
    235static void zoom(const Arg *arg);
    236
    237/* variables */
    238static const char broken[] = "broken";
    239static char stext[256];
    240static int screen;
    241static int sw, sh;           /* X display screen geometry width, height */
    242static int bh;               /* bar height */
    243static int lrpad;            /* sum of left and right padding for text */
    244static int (*xerrorxlib)(Display *, XErrorEvent *);
    245static unsigned int numlockmask = 0;
    246static void (*handler[LASTEvent]) (XEvent *) = {
    247	[ButtonPress] = buttonpress,
    248	[ClientMessage] = clientmessage,
    249	[ConfigureRequest] = configurerequest,
    250	[ConfigureNotify] = configurenotify,
    251	[DestroyNotify] = destroynotify,
    252	[EnterNotify] = enternotify,
    253	[Expose] = expose,
    254	[FocusIn] = focusin,
    255	[KeyPress] = keypress,
    256	[MappingNotify] = mappingnotify,
    257	[MapRequest] = maprequest,
    258	[MotionNotify] = motionnotify,
    259	[PropertyNotify] = propertynotify,
    260	[UnmapNotify] = unmapnotify
    261};
    262static Atom wmatom[WMLast], netatom[NetLast];
    263static int running = 1;
    264static Cur *cursor[CurLast];
    265static Clr **scheme;
    266static Display *dpy;
    267static Drw *drw;
    268static Monitor *mons, *selmon;
    269static Window root, wmcheckwin;
    270
    271/* configuration, allows nested code to access above variables */
    272#include "config.h"
    273
    274/* compile-time check if all tags fit into an unsigned int bit array. */
    275struct NumTags { char limitexceeded[LENGTH(tags) > 31 ? -1 : 1]; };
    276
    277/* function implementations */
    278void
    279applyrules(Client *c)
    280{
    281	const char *class, *instance;
    282	unsigned int i;
    283	const Rule *r;
    284	Monitor *m;
    285	XClassHint ch = { NULL, NULL };
    286
    287	/* rule matching */
    288	c->isfloating = 0;
    289	c->tags = 0;
    290	XGetClassHint(dpy, c->win, &ch);
    291	class    = ch.res_class ? ch.res_class : broken;
    292	instance = ch.res_name  ? ch.res_name  : broken;
    293
    294	for (i = 0; i < LENGTH(rules); i++) {
    295		r = &rules[i];
    296		if ((!r->title || strstr(c->name, r->title))
    297		&& (!r->class || strstr(class, r->class))
    298		&& (!r->instance || strstr(instance, r->instance)))
    299		{
    300			c->isfloating = r->isfloating;
    301			c->tags |= r->tags;
    302			for (m = mons; m && m->num != r->monitor; m = m->next);
    303			if (m)
    304				c->mon = m;
    305		}
    306	}
    307	if (ch.res_class)
    308		XFree(ch.res_class);
    309	if (ch.res_name)
    310		XFree(ch.res_name);
    311	c->tags = c->tags & TAGMASK ? c->tags & TAGMASK : c->mon->tagset[c->mon->seltags];
    312}
    313
    314int
    315applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact)
    316{
    317	int baseismin;
    318	Monitor *m = c->mon;
    319
    320	/* set minimum possible */
    321	*w = MAX(1, *w);
    322	*h = MAX(1, *h);
    323	if (interact) {
    324		if (*x > sw)
    325			*x = sw - WIDTH(c);
    326		if (*y > sh)
    327			*y = sh - HEIGHT(c);
    328		if (*x + *w + 2 * c->bw < 0)
    329			*x = 0;
    330		if (*y + *h + 2 * c->bw < 0)
    331			*y = 0;
    332	} else {
    333		if (*x >= m->wx + m->ww)
    334			*x = m->wx + m->ww - WIDTH(c);
    335		if (*y >= m->wy + m->wh)
    336			*y = m->wy + m->wh - HEIGHT(c);
    337		if (*x + *w + 2 * c->bw <= m->wx)
    338			*x = m->wx;
    339		if (*y + *h + 2 * c->bw <= m->wy)
    340			*y = m->wy;
    341	}
    342	if (*h < bh)
    343		*h = bh;
    344	if (*w < bh)
    345		*w = bh;
    346	if (resizehints || c->isfloating || !c->mon->lt[c->mon->sellt]->arrange) {
    347		if (!c->hintsvalid)
    348			updatesizehints(c);
    349		/* see last two sentences in ICCCM 4.1.2.3 */
    350		baseismin = c->basew == c->minw && c->baseh == c->minh;
    351		if (!baseismin) { /* temporarily remove base dimensions */
    352			*w -= c->basew;
    353			*h -= c->baseh;
    354		}
    355		/* adjust for aspect limits */
    356		if (c->mina > 0 && c->maxa > 0) {
    357			if (c->maxa < (float)*w / *h)
    358				*w = *h * c->maxa + 0.5;
    359			else if (c->mina < (float)*h / *w)
    360				*h = *w * c->mina + 0.5;
    361		}
    362		if (baseismin) { /* increment calculation requires this */
    363			*w -= c->basew;
    364			*h -= c->baseh;
    365		}
    366		/* adjust for increment value */
    367		if (c->incw)
    368			*w -= *w % c->incw;
    369		if (c->inch)
    370			*h -= *h % c->inch;
    371		/* restore base dimensions */
    372		*w = MAX(*w + c->basew, c->minw);
    373		*h = MAX(*h + c->baseh, c->minh);
    374		if (c->maxw)
    375			*w = MIN(*w, c->maxw);
    376		if (c->maxh)
    377			*h = MIN(*h, c->maxh);
    378	}
    379	return *x != c->x || *y != c->y || *w != c->w || *h != c->h;
    380}
    381
    382void
    383arrange(Monitor *m)
    384{
    385	if (m)
    386		showhide(m->stack);
    387	else for (m = mons; m; m = m->next)
    388		showhide(m->stack);
    389	if (m) {
    390		arrangemon(m);
    391		restack(m);
    392	} else for (m = mons; m; m = m->next)
    393		arrangemon(m);
    394}
    395
    396void
    397arrangemon(Monitor *m)
    398{
    399	strncpy(m->ltsymbol, m->lt[m->sellt]->symbol, sizeof m->ltsymbol);
    400	if (m->lt[m->sellt]->arrange)
    401		m->lt[m->sellt]->arrange(m);
    402}
    403
    404void
    405attach(Client *c)
    406{
    407	c->next = c->mon->clients;
    408	c->mon->clients = c;
    409}
    410
    411void
    412attachstack(Client *c)
    413{
    414	c->snext = c->mon->stack;
    415	c->mon->stack = c;
    416}
    417
    418void
    419buttonpress(XEvent *e)
    420{
    421	unsigned int i, x, click;
    422	Arg arg = {0};
    423	Client *c;
    424	Monitor *m;
    425	XButtonPressedEvent *ev = &e->xbutton;
    426
    427	click = ClkRootWin;
    428	/* focus monitor if necessary */
    429	if ((m = wintomon(ev->window)) && m != selmon) {
    430		unfocus(selmon->sel, 1);
    431		selmon = m;
    432		focus(NULL);
    433	}
    434	if (ev->window == selmon->barwin) {
    435		i = x = 0;
    436		do
    437			x += TEXTW(tags[i]);
    438		while (ev->x >= x && ++i < LENGTH(tags));
    439		if (i < LENGTH(tags)) {
    440			click = ClkTagBar;
    441			arg.ui = 1 << i;
    442		} else if (ev->x < x + TEXTW(selmon->ltsymbol))
    443			click = ClkLtSymbol;
    444		else if (ev->x > selmon->ww - (int)TEXTW(stext))
    445			click = ClkStatusText;
    446		else
    447			click = ClkWinTitle;
    448	} else if ((c = wintoclient(ev->window))) {
    449		focus(c);
    450		restack(selmon);
    451		XAllowEvents(dpy, ReplayPointer, CurrentTime);
    452		click = ClkClientWin;
    453	}
    454	for (i = 0; i < LENGTH(buttons); i++)
    455		if (click == buttons[i].click && buttons[i].func && buttons[i].button == ev->button
    456		&& CLEANMASK(buttons[i].mask) == CLEANMASK(ev->state))
    457			buttons[i].func(click == ClkTagBar && buttons[i].arg.i == 0 ? &arg : &buttons[i].arg);
    458}
    459
    460void
    461checkotherwm(void)
    462{
    463	xerrorxlib = XSetErrorHandler(xerrorstart);
    464	/* this causes an error if some other window manager is running */
    465	XSelectInput(dpy, DefaultRootWindow(dpy), SubstructureRedirectMask);
    466	XSync(dpy, False);
    467	XSetErrorHandler(xerror);
    468	XSync(dpy, False);
    469}
    470
    471void
    472cleanup(void)
    473{
    474	Arg a = {.ui = ~0};
    475	Layout foo = { "", NULL };
    476	Monitor *m;
    477	size_t i;
    478
    479	view(&a);
    480	selmon->lt[selmon->sellt] = &foo;
    481	for (m = mons; m; m = m->next)
    482		while (m->stack)
    483			unmanage(m->stack, 0);
    484	XUngrabKey(dpy, AnyKey, AnyModifier, root);
    485	while (mons)
    486		cleanupmon(mons);
    487	for (i = 0; i < CurLast; i++)
    488		drw_cur_free(drw, cursor[i]);
    489	for (i = 0; i < LENGTH(colors); i++)
    490		free(scheme[i]);
    491	free(scheme);
    492	XDestroyWindow(dpy, wmcheckwin);
    493	drw_free(drw);
    494	XSync(dpy, False);
    495	XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime);
    496	XDeleteProperty(dpy, root, netatom[NetActiveWindow]);
    497}
    498
    499void
    500cleanupmon(Monitor *mon)
    501{
    502	Monitor *m;
    503
    504	if (mon == mons)
    505		mons = mons->next;
    506	else {
    507		for (m = mons; m && m->next != mon; m = m->next);
    508		m->next = mon->next;
    509	}
    510	XUnmapWindow(dpy, mon->barwin);
    511	XDestroyWindow(dpy, mon->barwin);
    512	free(mon);
    513}
    514
    515void
    516clientmessage(XEvent *e)
    517{
    518	XClientMessageEvent *cme = &e->xclient;
    519	Client *c = wintoclient(cme->window);
    520
    521	if (!c)
    522		return;
    523	if (cme->message_type == netatom[NetWMState]) {
    524		if (cme->data.l[1] == netatom[NetWMFullscreen]
    525		|| cme->data.l[2] == netatom[NetWMFullscreen])
    526			setfullscreen(c, (cme->data.l[0] == 1 /* _NET_WM_STATE_ADD    */
    527				|| (cme->data.l[0] == 2 /* _NET_WM_STATE_TOGGLE */ && !c->isfullscreen)));
    528	} else if (cme->message_type == netatom[NetActiveWindow]) {
    529		if (c != selmon->sel && !c->isurgent)
    530			seturgent(c, 1);
    531	}
    532}
    533
    534void
    535configure(Client *c)
    536{
    537	XConfigureEvent ce;
    538
    539	ce.type = ConfigureNotify;
    540	ce.display = dpy;
    541	ce.event = c->win;
    542	ce.window = c->win;
    543	ce.x = c->x;
    544	ce.y = c->y;
    545	ce.width = c->w;
    546	ce.height = c->h;
    547	ce.border_width = c->bw;
    548	ce.above = None;
    549	ce.override_redirect = False;
    550	XSendEvent(dpy, c->win, False, StructureNotifyMask, (XEvent *)&ce);
    551}
    552
    553void
    554configurenotify(XEvent *e)
    555{
    556	Monitor *m;
    557	Client *c;
    558	XConfigureEvent *ev = &e->xconfigure;
    559	int dirty;
    560
    561	/* TODO: updategeom handling sucks, needs to be simplified */
    562	if (ev->window == root) {
    563		dirty = (sw != ev->width || sh != ev->height);
    564		sw = ev->width;
    565		sh = ev->height;
    566		if (updategeom() || dirty) {
    567			drw_resize(drw, sw, bh);
    568			updatebars();
    569			for (m = mons; m; m = m->next) {
    570				for (c = m->clients; c; c = c->next)
    571					if (c->isfullscreen)
    572						resizeclient(c, m->mx, m->my, m->mw, m->mh);
    573				XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, m->ww, bh);
    574			}
    575			focus(NULL);
    576			arrange(NULL);
    577		}
    578	}
    579}
    580
    581void
    582configurerequest(XEvent *e)
    583{
    584	Client *c;
    585	Monitor *m;
    586	XConfigureRequestEvent *ev = &e->xconfigurerequest;
    587	XWindowChanges wc;
    588
    589	if ((c = wintoclient(ev->window))) {
    590		if (ev->value_mask & CWBorderWidth)
    591			c->bw = ev->border_width;
    592		else if (c->isfloating || !selmon->lt[selmon->sellt]->arrange) {
    593			m = c->mon;
    594			if (ev->value_mask & CWX) {
    595				c->oldx = c->x;
    596				c->x = m->mx + ev->x;
    597			}
    598			if (ev->value_mask & CWY) {
    599				c->oldy = c->y;
    600				c->y = m->my + ev->y;
    601			}
    602			if (ev->value_mask & CWWidth) {
    603				c->oldw = c->w;
    604				c->w = ev->width;
    605			}
    606			if (ev->value_mask & CWHeight) {
    607				c->oldh = c->h;
    608				c->h = ev->height;
    609			}
    610			if ((c->x + c->w) > m->mx + m->mw && c->isfloating)
    611				c->x = m->mx + (m->mw / 2 - WIDTH(c) / 2); /* center in x direction */
    612			if ((c->y + c->h) > m->my + m->mh && c->isfloating)
    613				c->y = m->my + (m->mh / 2 - HEIGHT(c) / 2); /* center in y direction */
    614			if ((ev->value_mask & (CWX|CWY)) && !(ev->value_mask & (CWWidth|CWHeight)))
    615				configure(c);
    616			if (ISVISIBLE(c))
    617				XMoveResizeWindow(dpy, c->win, c->x, c->y, c->w, c->h);
    618		} else
    619			configure(c);
    620	} else {
    621		wc.x = ev->x;
    622		wc.y = ev->y;
    623		wc.width = ev->width;
    624		wc.height = ev->height;
    625		wc.border_width = ev->border_width;
    626		wc.sibling = ev->above;
    627		wc.stack_mode = ev->detail;
    628		XConfigureWindow(dpy, ev->window, ev->value_mask, &wc);
    629	}
    630	XSync(dpy, False);
    631}
    632
    633Monitor *
    634createmon(void)
    635{
    636	Monitor *m;
    637
    638	m = ecalloc(1, sizeof(Monitor));
    639	m->tagset[0] = m->tagset[1] = 1;
    640	m->mfact = mfact;
    641	m->nmaster = nmaster;
    642	m->showbar = showbar;
    643	m->topbar = topbar;
    644	m->lt[0] = &layouts[0];
    645	m->lt[1] = &layouts[1 % LENGTH(layouts)];
    646	strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol);
    647	return m;
    648}
    649
    650void
    651destroynotify(XEvent *e)
    652{
    653	Client *c;
    654	XDestroyWindowEvent *ev = &e->xdestroywindow;
    655
    656	if ((c = wintoclient(ev->window)))
    657		unmanage(c, 1);
    658}
    659
    660void
    661detach(Client *c)
    662{
    663	Client **tc;
    664
    665	for (tc = &c->mon->clients; *tc && *tc != c; tc = &(*tc)->next);
    666	*tc = c->next;
    667}
    668
    669void
    670detachstack(Client *c)
    671{
    672	Client **tc, *t;
    673
    674	for (tc = &c->mon->stack; *tc && *tc != c; tc = &(*tc)->snext);
    675	*tc = c->snext;
    676
    677	if (c == c->mon->sel) {
    678		for (t = c->mon->stack; t && !ISVISIBLE(t); t = t->snext);
    679		c->mon->sel = t;
    680	}
    681}
    682
    683Monitor *
    684dirtomon(int dir)
    685{
    686	Monitor *m = NULL;
    687
    688	if (dir > 0) {
    689		if (!(m = selmon->next))
    690			m = mons;
    691	} else if (selmon == mons)
    692		for (m = mons; m->next; m = m->next);
    693	else
    694		for (m = mons; m->next != selmon; m = m->next);
    695	return m;
    696}
    697
    698void
    699drawbar(Monitor *m)
    700{
    701	int x, w, tw = 0;
    702	int boxs = drw->fonts->h / 9;
    703	int boxw = drw->fonts->h / 6 + 2;
    704	unsigned int i, occ = 0, urg = 0;
    705	Client *c;
    706
    707	if (!m->showbar)
    708		return;
    709
    710	/* draw status first so it can be overdrawn by tags later */
    711	if (m == selmon) { /* status is only drawn on selected monitor */
    712		drw_setscheme(drw, scheme[SchemeNorm]);
    713		tw = TEXTW(stext) - lrpad + 2; /* 2px right padding */
    714		drw_text(drw, m->ww - tw, 0, tw, bh, 0, stext, 0);
    715	}
    716
    717	for (c = m->clients; c; c = c->next) {
    718		occ |= c->tags;
    719		if (c->isurgent)
    720			urg |= c->tags;
    721	}
    722	x = 0;
    723	for (i = 0; i < LENGTH(tags); i++) {
    724		w = TEXTW(tags[i]);
    725		drw_setscheme(drw, scheme[m->tagset[m->seltags] & 1 << i ? SchemeSel : SchemeNorm]);
    726		drw_text(drw, x, 0, w, bh, lrpad / 2, tags[i], urg & 1 << i);
    727		if (occ & 1 << i)
    728			drw_rect(drw, x + boxs, boxs, boxw, boxw,
    729				m == selmon && selmon->sel && selmon->sel->tags & 1 << i,
    730				urg & 1 << i);
    731		x += w;
    732	}
    733	w = TEXTW(m->ltsymbol);
    734	drw_setscheme(drw, scheme[SchemeNorm]);
    735	x = drw_text(drw, x, 0, w, bh, lrpad / 2, m->ltsymbol, 0);
    736
    737	if ((w = m->ww - tw - x) > bh) {
    738		if (m->sel) {
    739			drw_setscheme(drw, scheme[m == selmon ? SchemeSel : SchemeNorm]);
    740			drw_text(drw, x, 0, w, bh, lrpad / 2, m->sel->name, 0);
    741			if (m->sel->isfloating)
    742				drw_rect(drw, x + boxs, boxs, boxw, boxw, m->sel->isfixed, 0);
    743		} else {
    744			drw_setscheme(drw, scheme[SchemeNorm]);
    745			drw_rect(drw, x, 0, w, bh, 1, 1);
    746		}
    747	}
    748	drw_map(drw, m->barwin, 0, 0, m->ww, bh);
    749}
    750
    751void
    752drawbars(void)
    753{
    754	Monitor *m;
    755
    756	for (m = mons; m; m = m->next)
    757		drawbar(m);
    758}
    759
    760void
    761enternotify(XEvent *e)
    762{
    763	Client *c;
    764	Monitor *m;
    765	XCrossingEvent *ev = &e->xcrossing;
    766
    767	if ((ev->mode != NotifyNormal || ev->detail == NotifyInferior) && ev->window != root)
    768		return;
    769	c = wintoclient(ev->window);
    770	m = c ? c->mon : wintomon(ev->window);
    771	if (m != selmon) {
    772		unfocus(selmon->sel, 1);
    773		selmon = m;
    774	} else if (!c || c == selmon->sel)
    775		return;
    776	focus(c);
    777}
    778
    779void
    780expose(XEvent *e)
    781{
    782	Monitor *m;
    783	XExposeEvent *ev = &e->xexpose;
    784
    785	if (ev->count == 0 && (m = wintomon(ev->window)))
    786		drawbar(m);
    787}
    788
    789void
    790focus(Client *c)
    791{
    792	if (!c || !ISVISIBLE(c))
    793		for (c = selmon->stack; c && !ISVISIBLE(c); c = c->snext);
    794	if (selmon->sel && selmon->sel != c)
    795		unfocus(selmon->sel, 0);
    796	if (c) {
    797		if (c->mon != selmon)
    798			selmon = c->mon;
    799		if (c->isurgent)
    800			seturgent(c, 0);
    801		detachstack(c);
    802		attachstack(c);
    803		grabbuttons(c, 1);
    804		XSetWindowBorder(dpy, c->win, scheme[SchemeSel][ColBorder].pixel);
    805		setfocus(c);
    806	} else {
    807		XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime);
    808		XDeleteProperty(dpy, root, netatom[NetActiveWindow]);
    809	}
    810	selmon->sel = c;
    811	drawbars();
    812}
    813
    814/* there are some broken focus acquiring clients needing extra handling */
    815void
    816focusin(XEvent *e)
    817{
    818	XFocusChangeEvent *ev = &e->xfocus;
    819
    820	if (selmon->sel && ev->window != selmon->sel->win)
    821		setfocus(selmon->sel);
    822}
    823
    824void
    825focusmon(const Arg *arg)
    826{
    827	Monitor *m;
    828
    829	if (!mons->next)
    830		return;
    831	if ((m = dirtomon(arg->i)) == selmon)
    832		return;
    833	unfocus(selmon->sel, 0);
    834	selmon = m;
    835	focus(NULL);
    836}
    837
    838void
    839focusstack(const Arg *arg)
    840{
    841	Client *c = NULL, *i;
    842
    843	if (!selmon->sel || (selmon->sel->isfullscreen && lockfullscreen))
    844		return;
    845	if (arg->i > 0) {
    846		for (c = selmon->sel->next; c && !ISVISIBLE(c); c = c->next);
    847		if (!c)
    848			for (c = selmon->clients; c && !ISVISIBLE(c); c = c->next);
    849	} else {
    850		for (i = selmon->clients; i != selmon->sel; i = i->next)
    851			if (ISVISIBLE(i))
    852				c = i;
    853		if (!c)
    854			for (; i; i = i->next)
    855				if (ISVISIBLE(i))
    856					c = i;
    857	}
    858	if (c) {
    859		focus(c);
    860		restack(selmon);
    861	}
    862}
    863
    864Atom
    865getatomprop(Client *c, Atom prop)
    866{
    867	int di;
    868	unsigned long dl;
    869	unsigned char *p = NULL;
    870	Atom da, atom = None;
    871
    872	if (XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, XA_ATOM,
    873		&da, &di, &dl, &dl, &p) == Success && p) {
    874		atom = *(Atom *)p;
    875		XFree(p);
    876	}
    877	return atom;
    878}
    879
    880int
    881getrootptr(int *x, int *y)
    882{
    883	int di;
    884	unsigned int dui;
    885	Window dummy;
    886
    887	return XQueryPointer(dpy, root, &dummy, &dummy, x, y, &di, &di, &dui);
    888}
    889
    890long
    891getstate(Window w)
    892{
    893	int format;
    894	long result = -1;
    895	unsigned char *p = NULL;
    896	unsigned long n, extra;
    897	Atom real;
    898
    899	if (XGetWindowProperty(dpy, w, wmatom[WMState], 0L, 2L, False, wmatom[WMState],
    900		&real, &format, &n, &extra, (unsigned char **)&p) != Success)
    901		return -1;
    902	if (n != 0)
    903		result = *p;
    904	XFree(p);
    905	return result;
    906}
    907
    908int
    909gettextprop(Window w, Atom atom, char *text, unsigned int size)
    910{
    911	char **list = NULL;
    912	int n;
    913	XTextProperty name;
    914
    915	if (!text || size == 0)
    916		return 0;
    917	text[0] = '\0';
    918	if (!XGetTextProperty(dpy, w, &name, atom) || !name.nitems)
    919		return 0;
    920	if (name.encoding == XA_STRING) {
    921		strncpy(text, (char *)name.value, size - 1);
    922	} else if (XmbTextPropertyToTextList(dpy, &name, &list, &n) >= Success && n > 0 && *list) {
    923		strncpy(text, *list, size - 1);
    924		XFreeStringList(list);
    925	}
    926	text[size - 1] = '\0';
    927	XFree(name.value);
    928	return 1;
    929}
    930
    931void
    932grabbuttons(Client *c, int focused)
    933{
    934	updatenumlockmask();
    935	{
    936		unsigned int i, j;
    937		unsigned int modifiers[] = { 0, LockMask, numlockmask, numlockmask|LockMask };
    938		XUngrabButton(dpy, AnyButton, AnyModifier, c->win);
    939		if (!focused)
    940			XGrabButton(dpy, AnyButton, AnyModifier, c->win, False,
    941				BUTTONMASK, GrabModeSync, GrabModeSync, None, None);
    942		for (i = 0; i < LENGTH(buttons); i++)
    943			if (buttons[i].click == ClkClientWin)
    944				for (j = 0; j < LENGTH(modifiers); j++)
    945					XGrabButton(dpy, buttons[i].button,
    946						buttons[i].mask | modifiers[j],
    947						c->win, False, BUTTONMASK,
    948						GrabModeAsync, GrabModeSync, None, None);
    949	}
    950}
    951
    952void
    953grabkeys(void)
    954{
    955	updatenumlockmask();
    956	{
    957		unsigned int i, j, k;
    958		unsigned int modifiers[] = { 0, LockMask, numlockmask, numlockmask|LockMask };
    959		int start, end, skip;
    960		KeySym *syms;
    961
    962		XUngrabKey(dpy, AnyKey, AnyModifier, root);
    963		XDisplayKeycodes(dpy, &start, &end);
    964		syms = XGetKeyboardMapping(dpy, start, end - start + 1, &skip);
    965		if (!syms)
    966			return;
    967		for (k = start; k <= end; k++)
    968			for (i = 0; i < LENGTH(keys); i++)
    969				/* skip modifier codes, we do that ourselves */
    970				if (keys[i].keysym == syms[(k - start) * skip])
    971					for (j = 0; j < LENGTH(modifiers); j++)
    972						XGrabKey(dpy, k,
    973							 keys[i].mod | modifiers[j],
    974							 root, True,
    975							 GrabModeAsync, GrabModeAsync);
    976		XFree(syms);
    977	}
    978}
    979
    980void
    981incnmaster(const Arg *arg)
    982{
    983	selmon->nmaster = MAX(selmon->nmaster + arg->i, 0);
    984	arrange(selmon);
    985}
    986
    987#ifdef XINERAMA
    988static int
    989isuniquegeom(XineramaScreenInfo *unique, size_t n, XineramaScreenInfo *info)
    990{
    991	while (n--)
    992		if (unique[n].x_org == info->x_org && unique[n].y_org == info->y_org
    993		&& unique[n].width == info->width && unique[n].height == info->height)
    994			return 0;
    995	return 1;
    996}
    997#endif /* XINERAMA */
    998
    999void
   1000keypress(XEvent *e)
   1001{
   1002	unsigned int i;
   1003	KeySym keysym;
   1004	XKeyEvent *ev;
   1005
   1006	ev = &e->xkey;
   1007	keysym = XKeycodeToKeysym(dpy, (KeyCode)ev->keycode, 0);
   1008	for (i = 0; i < LENGTH(keys); i++)
   1009		if (keysym == keys[i].keysym
   1010		&& CLEANMASK(keys[i].mod) == CLEANMASK(ev->state)
   1011		&& keys[i].func)
   1012			keys[i].func(&(keys[i].arg));
   1013}
   1014
   1015void
   1016killclient(const Arg *arg)
   1017{
   1018	if (!selmon->sel)
   1019		return;
   1020	if (!sendevent(selmon->sel, wmatom[WMDelete])) {
   1021		XGrabServer(dpy);
   1022		XSetErrorHandler(xerrordummy);
   1023		XSetCloseDownMode(dpy, DestroyAll);
   1024		XKillClient(dpy, selmon->sel->win);
   1025		XSync(dpy, False);
   1026		XSetErrorHandler(xerror);
   1027		XUngrabServer(dpy);
   1028	}
   1029}
   1030
   1031void
   1032manage(Window w, XWindowAttributes *wa)
   1033{
   1034	Client *c, *t = NULL;
   1035	Window trans = None;
   1036	XWindowChanges wc;
   1037
   1038	c = ecalloc(1, sizeof(Client));
   1039	c->win = w;
   1040	/* geometry */
   1041	c->x = c->oldx = wa->x;
   1042	c->y = c->oldy = wa->y;
   1043	c->w = c->oldw = wa->width;
   1044	c->h = c->oldh = wa->height;
   1045	c->oldbw = wa->border_width;
   1046
   1047	updatetitle(c);
   1048	if (XGetTransientForHint(dpy, w, &trans) && (t = wintoclient(trans))) {
   1049		c->mon = t->mon;
   1050		c->tags = t->tags;
   1051	} else {
   1052		c->mon = selmon;
   1053		applyrules(c);
   1054	}
   1055
   1056	if (c->x + WIDTH(c) > c->mon->wx + c->mon->ww)
   1057		c->x = c->mon->wx + c->mon->ww - WIDTH(c);
   1058	if (c->y + HEIGHT(c) > c->mon->wy + c->mon->wh)
   1059		c->y = c->mon->wy + c->mon->wh - HEIGHT(c);
   1060	c->x = MAX(c->x, c->mon->wx);
   1061	c->y = MAX(c->y, c->mon->wy);
   1062	c->bw = borderpx;
   1063
   1064	wc.border_width = c->bw;
   1065	XConfigureWindow(dpy, w, CWBorderWidth, &wc);
   1066	XSetWindowBorder(dpy, w, scheme[SchemeNorm][ColBorder].pixel);
   1067	configure(c); /* propagates border_width, if size doesn't change */
   1068	updatewindowtype(c);
   1069	updatesizehints(c);
   1070	updatewmhints(c);
   1071	XSelectInput(dpy, w, EnterWindowMask|FocusChangeMask|PropertyChangeMask|StructureNotifyMask);
   1072	grabbuttons(c, 0);
   1073	if (!c->isfloating)
   1074		c->isfloating = c->oldstate = trans != None || c->isfixed;
   1075	if (c->isfloating)
   1076		XRaiseWindow(dpy, c->win);
   1077	attach(c);
   1078	attachstack(c);
   1079	XChangeProperty(dpy, root, netatom[NetClientList], XA_WINDOW, 32, PropModeAppend,
   1080		(unsigned char *) &(c->win), 1);
   1081	XMoveResizeWindow(dpy, c->win, c->x + 2 * sw, c->y, c->w, c->h); /* some windows require this */
   1082	setclientstate(c, NormalState);
   1083	if (c->mon == selmon)
   1084		unfocus(selmon->sel, 0);
   1085	c->mon->sel = c;
   1086	arrange(c->mon);
   1087	XMapWindow(dpy, c->win);
   1088	focus(NULL);
   1089}
   1090
   1091void
   1092mappingnotify(XEvent *e)
   1093{
   1094	XMappingEvent *ev = &e->xmapping;
   1095
   1096	XRefreshKeyboardMapping(ev);
   1097	if (ev->request == MappingKeyboard)
   1098		grabkeys();
   1099}
   1100
   1101void
   1102maprequest(XEvent *e)
   1103{
   1104	static XWindowAttributes wa;
   1105	XMapRequestEvent *ev = &e->xmaprequest;
   1106
   1107	if (!XGetWindowAttributes(dpy, ev->window, &wa) || wa.override_redirect)
   1108		return;
   1109	if (!wintoclient(ev->window))
   1110		manage(ev->window, &wa);
   1111}
   1112
   1113void
   1114monocle(Monitor *m)
   1115{
   1116	unsigned int n = 0;
   1117	Client *c;
   1118
   1119	for (c = m->clients; c; c = c->next)
   1120		if (ISVISIBLE(c))
   1121			n++;
   1122	if (n > 0) /* override layout symbol */
   1123		snprintf(m->ltsymbol, sizeof m->ltsymbol, "[%d]", n);
   1124	for (c = nexttiled(m->clients); c; c = nexttiled(c->next))
   1125		resize(c, m->wx, m->wy, m->ww - 2 * c->bw, m->wh - 2 * c->bw, 0);
   1126}
   1127
   1128void
   1129motionnotify(XEvent *e)
   1130{
   1131	static Monitor *mon = NULL;
   1132	Monitor *m;
   1133	XMotionEvent *ev = &e->xmotion;
   1134
   1135	if (ev->window != root)
   1136		return;
   1137	if ((m = recttomon(ev->x_root, ev->y_root, 1, 1)) != mon && mon) {
   1138		unfocus(selmon->sel, 1);
   1139		selmon = m;
   1140		focus(NULL);
   1141	}
   1142	mon = m;
   1143}
   1144
   1145void
   1146movemouse(const Arg *arg)
   1147{
   1148	int x, y, ocx, ocy, nx, ny;
   1149	Client *c;
   1150	Monitor *m;
   1151	XEvent ev;
   1152	Time lasttime = 0;
   1153
   1154	if (!(c = selmon->sel))
   1155		return;
   1156	if (c->isfullscreen) /* no support moving fullscreen windows by mouse */
   1157		return;
   1158	restack(selmon);
   1159	ocx = c->x;
   1160	ocy = c->y;
   1161	if (XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync,
   1162		None, cursor[CurMove]->cursor, CurrentTime) != GrabSuccess)
   1163		return;
   1164	if (!getrootptr(&x, &y))
   1165		return;
   1166	do {
   1167		XMaskEvent(dpy, MOUSEMASK|ExposureMask|SubstructureRedirectMask, &ev);
   1168		switch(ev.type) {
   1169		case ConfigureRequest:
   1170		case Expose:
   1171		case MapRequest:
   1172			handler[ev.type](&ev);
   1173			break;
   1174		case MotionNotify:
   1175			if ((ev.xmotion.time - lasttime) <= (1000 / 60))
   1176				continue;
   1177			lasttime = ev.xmotion.time;
   1178
   1179			nx = ocx + (ev.xmotion.x - x);
   1180			ny = ocy + (ev.xmotion.y - y);
   1181			if (abs(selmon->wx - nx) < snap)
   1182				nx = selmon->wx;
   1183			else if (abs((selmon->wx + selmon->ww) - (nx + WIDTH(c))) < snap)
   1184				nx = selmon->wx + selmon->ww - WIDTH(c);
   1185			if (abs(selmon->wy - ny) < snap)
   1186				ny = selmon->wy;
   1187			else if (abs((selmon->wy + selmon->wh) - (ny + HEIGHT(c))) < snap)
   1188				ny = selmon->wy + selmon->wh - HEIGHT(c);
   1189			if (!c->isfloating && selmon->lt[selmon->sellt]->arrange
   1190			&& (abs(nx - c->x) > snap || abs(ny - c->y) > snap))
   1191				togglefloating(NULL);
   1192			if (!selmon->lt[selmon->sellt]->arrange || c->isfloating)
   1193				resize(c, nx, ny, c->w, c->h, 1);
   1194			break;
   1195		}
   1196	} while (ev.type != ButtonRelease);
   1197	XUngrabPointer(dpy, CurrentTime);
   1198	if ((m = recttomon(c->x, c->y, c->w, c->h)) != selmon) {
   1199		sendmon(c, m);
   1200		selmon = m;
   1201		focus(NULL);
   1202	}
   1203}
   1204
   1205Client *
   1206nexttiled(Client *c)
   1207{
   1208	for (; c && (c->isfloating || !ISVISIBLE(c)); c = c->next);
   1209	return c;
   1210}
   1211
   1212void
   1213pop(Client *c)
   1214{
   1215	detach(c);
   1216	attach(c);
   1217	focus(c);
   1218	arrange(c->mon);
   1219}
   1220
   1221void
   1222propertynotify(XEvent *e)
   1223{
   1224	Client *c;
   1225	Window trans;
   1226	XPropertyEvent *ev = &e->xproperty;
   1227
   1228	if ((ev->window == root) && (ev->atom == XA_WM_NAME))
   1229		updatestatus();
   1230	else if (ev->state == PropertyDelete)
   1231		return; /* ignore */
   1232	else if ((c = wintoclient(ev->window))) {
   1233		switch(ev->atom) {
   1234		default: break;
   1235		case XA_WM_TRANSIENT_FOR:
   1236			if (!c->isfloating && (XGetTransientForHint(dpy, c->win, &trans)) &&
   1237				(c->isfloating = (wintoclient(trans)) != NULL))
   1238				arrange(c->mon);
   1239			break;
   1240		case XA_WM_NORMAL_HINTS:
   1241			c->hintsvalid = 0;
   1242			break;
   1243		case XA_WM_HINTS:
   1244			updatewmhints(c);
   1245			drawbars();
   1246			break;
   1247		}
   1248		if (ev->atom == XA_WM_NAME || ev->atom == netatom[NetWMName]) {
   1249			updatetitle(c);
   1250			if (c == c->mon->sel)
   1251				drawbar(c->mon);
   1252		}
   1253		if (ev->atom == netatom[NetWMWindowType])
   1254			updatewindowtype(c);
   1255	}
   1256}
   1257
   1258void
   1259quit(const Arg *arg)
   1260{
   1261	running = 0;
   1262}
   1263
   1264Monitor *
   1265recttomon(int x, int y, int w, int h)
   1266{
   1267	Monitor *m, *r = selmon;
   1268	int a, area = 0;
   1269
   1270	for (m = mons; m; m = m->next)
   1271		if ((a = INTERSECT(x, y, w, h, m)) > area) {
   1272			area = a;
   1273			r = m;
   1274		}
   1275	return r;
   1276}
   1277
   1278void
   1279resize(Client *c, int x, int y, int w, int h, int interact)
   1280{
   1281	if (applysizehints(c, &x, &y, &w, &h, interact))
   1282		resizeclient(c, x, y, w, h);
   1283}
   1284
   1285void
   1286resizeclient(Client *c, int x, int y, int w, int h)
   1287{
   1288	XWindowChanges wc;
   1289
   1290	c->oldx = c->x; c->x = wc.x = x;
   1291	c->oldy = c->y; c->y = wc.y = y;
   1292	c->oldw = c->w; c->w = wc.width = w;
   1293	c->oldh = c->h; c->h = wc.height = h;
   1294	wc.border_width = c->bw;
   1295	XConfigureWindow(dpy, c->win, CWX|CWY|CWWidth|CWHeight|CWBorderWidth, &wc);
   1296	configure(c);
   1297	XSync(dpy, False);
   1298}
   1299
   1300void
   1301resizemouse(const Arg *arg)
   1302{
   1303	int ocx, ocy, nw, nh;
   1304	Client *c;
   1305	Monitor *m;
   1306	XEvent ev;
   1307	Time lasttime = 0;
   1308
   1309	if (!(c = selmon->sel))
   1310		return;
   1311	if (c->isfullscreen) /* no support resizing fullscreen windows by mouse */
   1312		return;
   1313	restack(selmon);
   1314	ocx = c->x;
   1315	ocy = c->y;
   1316	if (XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync,
   1317		None, cursor[CurResize]->cursor, CurrentTime) != GrabSuccess)
   1318		return;
   1319	XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->bw - 1, c->h + c->bw - 1);
   1320	do {
   1321		XMaskEvent(dpy, MOUSEMASK|ExposureMask|SubstructureRedirectMask, &ev);
   1322		switch(ev.type) {
   1323		case ConfigureRequest:
   1324		case Expose:
   1325		case MapRequest:
   1326			handler[ev.type](&ev);
   1327			break;
   1328		case MotionNotify:
   1329			if ((ev.xmotion.time - lasttime) <= (1000 / 60))
   1330				continue;
   1331			lasttime = ev.xmotion.time;
   1332
   1333			nw = MAX(ev.xmotion.x - ocx - 2 * c->bw + 1, 1);
   1334			nh = MAX(ev.xmotion.y - ocy - 2 * c->bw + 1, 1);
   1335			if (c->mon->wx + nw >= selmon->wx && c->mon->wx + nw <= selmon->wx + selmon->ww
   1336			&& c->mon->wy + nh >= selmon->wy && c->mon->wy + nh <= selmon->wy + selmon->wh)
   1337			{
   1338				if (!c->isfloating && selmon->lt[selmon->sellt]->arrange
   1339				&& (abs(nw - c->w) > snap || abs(nh - c->h) > snap))
   1340					togglefloating(NULL);
   1341			}
   1342			if (!selmon->lt[selmon->sellt]->arrange || c->isfloating)
   1343				resize(c, c->x, c->y, nw, nh, 1);
   1344			break;
   1345		}
   1346	} while (ev.type != ButtonRelease);
   1347	XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->bw - 1, c->h + c->bw - 1);
   1348	XUngrabPointer(dpy, CurrentTime);
   1349	while (XCheckMaskEvent(dpy, EnterWindowMask, &ev));
   1350	if ((m = recttomon(c->x, c->y, c->w, c->h)) != selmon) {
   1351		sendmon(c, m);
   1352		selmon = m;
   1353		focus(NULL);
   1354	}
   1355}
   1356
   1357void
   1358restack(Monitor *m)
   1359{
   1360	Client *c;
   1361	XEvent ev;
   1362	XWindowChanges wc;
   1363
   1364	drawbar(m);
   1365	if (!m->sel)
   1366		return;
   1367	if (m->sel->isfloating || !m->lt[m->sellt]->arrange)
   1368		XRaiseWindow(dpy, m->sel->win);
   1369	if (m->lt[m->sellt]->arrange) {
   1370		wc.stack_mode = Below;
   1371		wc.sibling = m->barwin;
   1372		for (c = m->stack; c; c = c->snext)
   1373			if (!c->isfloating && ISVISIBLE(c)) {
   1374				XConfigureWindow(dpy, c->win, CWSibling|CWStackMode, &wc);
   1375				wc.sibling = c->win;
   1376			}
   1377	}
   1378	XSync(dpy, False);
   1379	while (XCheckMaskEvent(dpy, EnterWindowMask, &ev));
   1380}
   1381
   1382void
   1383run(void)
   1384{
   1385	XEvent ev;
   1386	/* main event loop */
   1387	XSync(dpy, False);
   1388	while (running && !XNextEvent(dpy, &ev))
   1389		if (handler[ev.type])
   1390			handler[ev.type](&ev); /* call handler */
   1391}
   1392
   1393void
   1394scan(void)
   1395{
   1396	unsigned int i, num;
   1397	Window d1, d2, *wins = NULL;
   1398	XWindowAttributes wa;
   1399
   1400	if (XQueryTree(dpy, root, &d1, &d2, &wins, &num)) {
   1401		for (i = 0; i < num; i++) {
   1402			if (!XGetWindowAttributes(dpy, wins[i], &wa)
   1403			|| wa.override_redirect || XGetTransientForHint(dpy, wins[i], &d1))
   1404				continue;
   1405			if (wa.map_state == IsViewable || getstate(wins[i]) == IconicState)
   1406				manage(wins[i], &wa);
   1407		}
   1408		for (i = 0; i < num; i++) { /* now the transients */
   1409			if (!XGetWindowAttributes(dpy, wins[i], &wa))
   1410				continue;
   1411			if (XGetTransientForHint(dpy, wins[i], &d1)
   1412			&& (wa.map_state == IsViewable || getstate(wins[i]) == IconicState))
   1413				manage(wins[i], &wa);
   1414		}
   1415		if (wins)
   1416			XFree(wins);
   1417	}
   1418}
   1419
   1420void
   1421sendmon(Client *c, Monitor *m)
   1422{
   1423	if (c->mon == m)
   1424		return;
   1425	unfocus(c, 1);
   1426	detach(c);
   1427	detachstack(c);
   1428	c->mon = m;
   1429	c->tags = m->tagset[m->seltags]; /* assign tags of target monitor */
   1430	attach(c);
   1431	attachstack(c);
   1432	focus(NULL);
   1433	arrange(NULL);
   1434}
   1435
   1436void
   1437setclientstate(Client *c, long state)
   1438{
   1439	long data[] = { state, None };
   1440
   1441	XChangeProperty(dpy, c->win, wmatom[WMState], wmatom[WMState], 32,
   1442		PropModeReplace, (unsigned char *)data, 2);
   1443}
   1444
   1445int
   1446sendevent(Client *c, Atom proto)
   1447{
   1448	int n;
   1449	Atom *protocols;
   1450	int exists = 0;
   1451	XEvent ev;
   1452
   1453	if (XGetWMProtocols(dpy, c->win, &protocols, &n)) {
   1454		while (!exists && n--)
   1455			exists = protocols[n] == proto;
   1456		XFree(protocols);
   1457	}
   1458	if (exists) {
   1459		ev.type = ClientMessage;
   1460		ev.xclient.window = c->win;
   1461		ev.xclient.message_type = wmatom[WMProtocols];
   1462		ev.xclient.format = 32;
   1463		ev.xclient.data.l[0] = proto;
   1464		ev.xclient.data.l[1] = CurrentTime;
   1465		XSendEvent(dpy, c->win, False, NoEventMask, &ev);
   1466	}
   1467	return exists;
   1468}
   1469
   1470void
   1471setfocus(Client *c)
   1472{
   1473	if (!c->neverfocus) {
   1474		XSetInputFocus(dpy, c->win, RevertToPointerRoot, CurrentTime);
   1475		XChangeProperty(dpy, root, netatom[NetActiveWindow],
   1476			XA_WINDOW, 32, PropModeReplace,
   1477			(unsigned char *) &(c->win), 1);
   1478	}
   1479	sendevent(c, wmatom[WMTakeFocus]);
   1480}
   1481
   1482void
   1483setfullscreen(Client *c, int fullscreen)
   1484{
   1485	if (fullscreen && !c->isfullscreen) {
   1486		XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32,
   1487			PropModeReplace, (unsigned char*)&netatom[NetWMFullscreen], 1);
   1488		c->isfullscreen = 1;
   1489		c->oldstate = c->isfloating;
   1490		c->oldbw = c->bw;
   1491		c->bw = 0;
   1492		c->isfloating = 1;
   1493		resizeclient(c, c->mon->mx, c->mon->my, c->mon->mw, c->mon->mh);
   1494		XRaiseWindow(dpy, c->win);
   1495	} else if (!fullscreen && c->isfullscreen){
   1496		XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32,
   1497			PropModeReplace, (unsigned char*)0, 0);
   1498		c->isfullscreen = 0;
   1499		c->isfloating = c->oldstate;
   1500		c->bw = c->oldbw;
   1501		c->x = c->oldx;
   1502		c->y = c->oldy;
   1503		c->w = c->oldw;
   1504		c->h = c->oldh;
   1505		resizeclient(c, c->x, c->y, c->w, c->h);
   1506		arrange(c->mon);
   1507	}
   1508}
   1509
   1510void
   1511setlayout(const Arg *arg)
   1512{
   1513	if (!arg || !arg->v || arg->v != selmon->lt[selmon->sellt])
   1514		selmon->sellt ^= 1;
   1515	if (arg && arg->v)
   1516		selmon->lt[selmon->sellt] = (Layout *)arg->v;
   1517	strncpy(selmon->ltsymbol, selmon->lt[selmon->sellt]->symbol, sizeof selmon->ltsymbol);
   1518	if (selmon->sel)
   1519		arrange(selmon);
   1520	else
   1521		drawbar(selmon);
   1522}
   1523
   1524/* arg > 1.0 will set mfact absolutely */
   1525void
   1526setmfact(const Arg *arg)
   1527{
   1528	float f;
   1529
   1530	if (!arg || !selmon->lt[selmon->sellt]->arrange)
   1531		return;
   1532	f = arg->f < 1.0 ? arg->f + selmon->mfact : arg->f - 1.0;
   1533	if (f < 0.05 || f > 0.95)
   1534		return;
   1535	selmon->mfact = f;
   1536	arrange(selmon);
   1537}
   1538
   1539void
   1540setup(void)
   1541{
   1542	int i;
   1543	XSetWindowAttributes wa;
   1544	Atom utf8string;
   1545	struct sigaction sa;
   1546
   1547	/* do not transform children into zombies when they terminate */
   1548	sigemptyset(&sa.sa_mask);
   1549	sa.sa_flags = SA_NOCLDSTOP | SA_NOCLDWAIT | SA_RESTART;
   1550	sa.sa_handler = SIG_IGN;
   1551	sigaction(SIGCHLD, &sa, NULL);
   1552
   1553	/* clean up any zombies (inherited from .xinitrc etc) immediately */
   1554	while (waitpid(-1, NULL, WNOHANG) > 0);
   1555
   1556	/* init screen */
   1557	screen = DefaultScreen(dpy);
   1558	sw = DisplayWidth(dpy, screen);
   1559	sh = DisplayHeight(dpy, screen);
   1560	root = RootWindow(dpy, screen);
   1561	drw = drw_create(dpy, screen, root, sw, sh);
   1562	if (!drw_fontset_create(drw, fonts, LENGTH(fonts)))
   1563		die("no fonts could be loaded.");
   1564	lrpad = drw->fonts->h;
   1565	bh = drw->fonts->h + 2;
   1566	updategeom();
   1567	/* init atoms */
   1568	utf8string = XInternAtom(dpy, "UTF8_STRING", False);
   1569	wmatom[WMProtocols] = XInternAtom(dpy, "WM_PROTOCOLS", False);
   1570	wmatom[WMDelete] = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
   1571	wmatom[WMState] = XInternAtom(dpy, "WM_STATE", False);
   1572	wmatom[WMTakeFocus] = XInternAtom(dpy, "WM_TAKE_FOCUS", False);
   1573	netatom[NetActiveWindow] = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", False);
   1574	netatom[NetSupported] = XInternAtom(dpy, "_NET_SUPPORTED", False);
   1575	netatom[NetWMName] = XInternAtom(dpy, "_NET_WM_NAME", False);
   1576	netatom[NetWMState] = XInternAtom(dpy, "_NET_WM_STATE", False);
   1577	netatom[NetWMCheck] = XInternAtom(dpy, "_NET_SUPPORTING_WM_CHECK", False);
   1578	netatom[NetWMFullscreen] = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False);
   1579	netatom[NetWMWindowType] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False);
   1580	netatom[NetWMWindowTypeDialog] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DIALOG", False);
   1581	netatom[NetClientList] = XInternAtom(dpy, "_NET_CLIENT_LIST", False);
   1582	/* init cursors */
   1583	cursor[CurNormal] = drw_cur_create(drw, XC_left_ptr);
   1584	cursor[CurResize] = drw_cur_create(drw, XC_sizing);
   1585	cursor[CurMove] = drw_cur_create(drw, XC_fleur);
   1586	/* init appearance */
   1587	scheme = ecalloc(LENGTH(colors), sizeof(Clr *));
   1588	for (i = 0; i < LENGTH(colors); i++)
   1589		scheme[i] = drw_scm_create(drw, colors[i], 3);
   1590	/* init bars */
   1591	updatebars();
   1592	updatestatus();
   1593	/* supporting window for NetWMCheck */
   1594	wmcheckwin = XCreateSimpleWindow(dpy, root, 0, 0, 1, 1, 0, 0, 0);
   1595	XChangeProperty(dpy, wmcheckwin, netatom[NetWMCheck], XA_WINDOW, 32,
   1596		PropModeReplace, (unsigned char *) &wmcheckwin, 1);
   1597	XChangeProperty(dpy, wmcheckwin, netatom[NetWMName], utf8string, 8,
   1598		PropModeReplace, (unsigned char *) "dwm", 3);
   1599	XChangeProperty(dpy, root, netatom[NetWMCheck], XA_WINDOW, 32,
   1600		PropModeReplace, (unsigned char *) &wmcheckwin, 1);
   1601	/* EWMH support per view */
   1602	XChangeProperty(dpy, root, netatom[NetSupported], XA_ATOM, 32,
   1603		PropModeReplace, (unsigned char *) netatom, NetLast);
   1604	XDeleteProperty(dpy, root, netatom[NetClientList]);
   1605	/* select events */
   1606	wa.cursor = cursor[CurNormal]->cursor;
   1607	wa.event_mask = SubstructureRedirectMask|SubstructureNotifyMask
   1608		|ButtonPressMask|PointerMotionMask|EnterWindowMask
   1609		|LeaveWindowMask|StructureNotifyMask|PropertyChangeMask;
   1610	XChangeWindowAttributes(dpy, root, CWEventMask|CWCursor, &wa);
   1611	XSelectInput(dpy, root, wa.event_mask);
   1612	grabkeys();
   1613	focus(NULL);
   1614}
   1615
   1616void
   1617seturgent(Client *c, int urg)
   1618{
   1619	XWMHints *wmh;
   1620
   1621	c->isurgent = urg;
   1622	if (!(wmh = XGetWMHints(dpy, c->win)))
   1623		return;
   1624	wmh->flags = urg ? (wmh->flags | XUrgencyHint) : (wmh->flags & ~XUrgencyHint);
   1625	XSetWMHints(dpy, c->win, wmh);
   1626	XFree(wmh);
   1627}
   1628
   1629void
   1630showhide(Client *c)
   1631{
   1632	if (!c)
   1633		return;
   1634	if (ISVISIBLE(c)) {
   1635		/* show clients top down */
   1636		XMoveWindow(dpy, c->win, c->x, c->y);
   1637		if ((!c->mon->lt[c->mon->sellt]->arrange || c->isfloating) && !c->isfullscreen)
   1638			resize(c, c->x, c->y, c->w, c->h, 0);
   1639		showhide(c->snext);
   1640	} else {
   1641		/* hide clients bottom up */
   1642		showhide(c->snext);
   1643		XMoveWindow(dpy, c->win, WIDTH(c) * -2, c->y);
   1644	}
   1645}
   1646
   1647void
   1648spawn(const Arg *arg)
   1649{
   1650	struct sigaction sa;
   1651
   1652	if (arg->v == dmenucmd)
   1653		dmenumon[0] = '0' + selmon->num;
   1654	if (fork() == 0) {
   1655		if (dpy)
   1656			close(ConnectionNumber(dpy));
   1657		setsid();
   1658
   1659		sigemptyset(&sa.sa_mask);
   1660		sa.sa_flags = 0;
   1661		sa.sa_handler = SIG_DFL;
   1662		sigaction(SIGCHLD, &sa, NULL);
   1663
   1664		execvp(((char **)arg->v)[0], (char **)arg->v);
   1665		die("dwm: execvp '%s' failed:", ((char **)arg->v)[0]);
   1666	}
   1667}
   1668
   1669void
   1670tag(const Arg *arg)
   1671{
   1672	if (selmon->sel && arg->ui & TAGMASK) {
   1673		selmon->sel->tags = arg->ui & TAGMASK;
   1674		focus(NULL);
   1675		arrange(selmon);
   1676	}
   1677}
   1678
   1679void
   1680tagmon(const Arg *arg)
   1681{
   1682	if (!selmon->sel || !mons->next)
   1683		return;
   1684	sendmon(selmon->sel, dirtomon(arg->i));
   1685}
   1686
   1687void
   1688tile(Monitor *m)
   1689{
   1690	unsigned int i, n, h, mw, my, ty;
   1691	Client *c;
   1692
   1693	for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++);
   1694	if (n == 0)
   1695		return;
   1696
   1697	if (n > m->nmaster)
   1698		mw = m->nmaster ? m->ww * m->mfact : 0;
   1699	else
   1700		mw = m->ww;
   1701	for (i = my = ty = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++)
   1702		if (i < m->nmaster) {
   1703			h = (m->wh - my) / (MIN(n, m->nmaster) - i);
   1704			resize(c, m->wx, m->wy + my, mw - (2*c->bw), h - (2*c->bw), 0);
   1705			if (my + HEIGHT(c) < m->wh)
   1706				my += HEIGHT(c);
   1707		} else {
   1708			h = (m->wh - ty) / (n - i);
   1709			resize(c, m->wx + mw, m->wy + ty, m->ww - mw - (2*c->bw), h - (2*c->bw), 0);
   1710			if (ty + HEIGHT(c) < m->wh)
   1711				ty += HEIGHT(c);
   1712		}
   1713}
   1714
   1715void
   1716togglebar(const Arg *arg)
   1717{
   1718	selmon->showbar = !selmon->showbar;
   1719	updatebarpos(selmon);
   1720	XMoveResizeWindow(dpy, selmon->barwin, selmon->wx, selmon->by, selmon->ww, bh);
   1721	arrange(selmon);
   1722}
   1723
   1724void
   1725togglefloating(const Arg *arg)
   1726{
   1727	if (!selmon->sel)
   1728		return;
   1729	if (selmon->sel->isfullscreen) /* no support for fullscreen windows */
   1730		return;
   1731	selmon->sel->isfloating = !selmon->sel->isfloating || selmon->sel->isfixed;
   1732	if (selmon->sel->isfloating)
   1733		resize(selmon->sel, selmon->sel->x, selmon->sel->y,
   1734			selmon->sel->w, selmon->sel->h, 0);
   1735	arrange(selmon);
   1736}
   1737
   1738void
   1739toggletag(const Arg *arg)
   1740{
   1741	unsigned int newtags;
   1742
   1743	if (!selmon->sel)
   1744		return;
   1745	newtags = selmon->sel->tags ^ (arg->ui & TAGMASK);
   1746	if (newtags) {
   1747		selmon->sel->tags = newtags;
   1748		focus(NULL);
   1749		arrange(selmon);
   1750	}
   1751}
   1752
   1753void
   1754toggleview(const Arg *arg)
   1755{
   1756	unsigned int newtagset = selmon->tagset[selmon->seltags] ^ (arg->ui & TAGMASK);
   1757
   1758	if (newtagset) {
   1759		selmon->tagset[selmon->seltags] = newtagset;
   1760		focus(NULL);
   1761		arrange(selmon);
   1762	}
   1763}
   1764
   1765void
   1766unfocus(Client *c, int setfocus)
   1767{
   1768	if (!c)
   1769		return;
   1770	grabbuttons(c, 0);
   1771	XSetWindowBorder(dpy, c->win, scheme[SchemeNorm][ColBorder].pixel);
   1772	if (setfocus) {
   1773		XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime);
   1774		XDeleteProperty(dpy, root, netatom[NetActiveWindow]);
   1775	}
   1776}
   1777
   1778void
   1779unmanage(Client *c, int destroyed)
   1780{
   1781	Monitor *m = c->mon;
   1782	XWindowChanges wc;
   1783
   1784	detach(c);
   1785	detachstack(c);
   1786	if (!destroyed) {
   1787		wc.border_width = c->oldbw;
   1788		XGrabServer(dpy); /* avoid race conditions */
   1789		XSetErrorHandler(xerrordummy);
   1790		XSelectInput(dpy, c->win, NoEventMask);
   1791		XConfigureWindow(dpy, c->win, CWBorderWidth, &wc); /* restore border */
   1792		XUngrabButton(dpy, AnyButton, AnyModifier, c->win);
   1793		setclientstate(c, WithdrawnState);
   1794		XSync(dpy, False);
   1795		XSetErrorHandler(xerror);
   1796		XUngrabServer(dpy);
   1797	}
   1798	free(c);
   1799	focus(NULL);
   1800	updateclientlist();
   1801	arrange(m);
   1802}
   1803
   1804void
   1805unmapnotify(XEvent *e)
   1806{
   1807	Client *c;
   1808	XUnmapEvent *ev = &e->xunmap;
   1809
   1810	if ((c = wintoclient(ev->window))) {
   1811		if (ev->send_event)
   1812			setclientstate(c, WithdrawnState);
   1813		else
   1814			unmanage(c, 0);
   1815	}
   1816}
   1817
   1818void
   1819updatebars(void)
   1820{
   1821	Monitor *m;
   1822	XSetWindowAttributes wa = {
   1823		.override_redirect = True,
   1824		.background_pixmap = ParentRelative,
   1825		.event_mask = ButtonPressMask|ExposureMask
   1826	};
   1827	XClassHint ch = {"dwm", "dwm"};
   1828	for (m = mons; m; m = m->next) {
   1829		if (m->barwin)
   1830			continue;
   1831		m->barwin = XCreateWindow(dpy, root, m->wx, m->by, m->ww, bh, 0, DefaultDepth(dpy, screen),
   1832				CopyFromParent, DefaultVisual(dpy, screen),
   1833				CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa);
   1834		XDefineCursor(dpy, m->barwin, cursor[CurNormal]->cursor);
   1835		XMapRaised(dpy, m->barwin);
   1836		XSetClassHint(dpy, m->barwin, &ch);
   1837	}
   1838}
   1839
   1840void
   1841updatebarpos(Monitor *m)
   1842{
   1843	m->wy = m->my;
   1844	m->wh = m->mh;
   1845	if (m->showbar) {
   1846		m->wh -= bh;
   1847		m->by = m->topbar ? m->wy : m->wy + m->wh;
   1848		m->wy = m->topbar ? m->wy + bh : m->wy;
   1849	} else
   1850		m->by = -bh;
   1851}
   1852
   1853void
   1854updateclientlist()
   1855{
   1856	Client *c;
   1857	Monitor *m;
   1858
   1859	XDeleteProperty(dpy, root, netatom[NetClientList]);
   1860	for (m = mons; m; m = m->next)
   1861		for (c = m->clients; c; c = c->next)
   1862			XChangeProperty(dpy, root, netatom[NetClientList],
   1863				XA_WINDOW, 32, PropModeAppend,
   1864				(unsigned char *) &(c->win), 1);
   1865}
   1866
   1867int
   1868updategeom(void)
   1869{
   1870	int dirty = 0;
   1871
   1872#ifdef XINERAMA
   1873	if (XineramaIsActive(dpy)) {
   1874		int i, j, n, nn;
   1875		Client *c;
   1876		Monitor *m;
   1877		XineramaScreenInfo *info = XineramaQueryScreens(dpy, &nn);
   1878		XineramaScreenInfo *unique = NULL;
   1879
   1880		for (n = 0, m = mons; m; m = m->next, n++);
   1881		/* only consider unique geometries as separate screens */
   1882		unique = ecalloc(nn, sizeof(XineramaScreenInfo));
   1883		for (i = 0, j = 0; i < nn; i++)
   1884			if (isuniquegeom(unique, j, &info[i]))
   1885				memcpy(&unique[j++], &info[i], sizeof(XineramaScreenInfo));
   1886		XFree(info);
   1887		nn = j;
   1888
   1889		/* new monitors if nn > n */
   1890		for (i = n; i < nn; i++) {
   1891			for (m = mons; m && m->next; m = m->next);
   1892			if (m)
   1893				m->next = createmon();
   1894			else
   1895				mons = createmon();
   1896		}
   1897		for (i = 0, m = mons; i < nn && m; m = m->next, i++)
   1898			if (i >= n
   1899			|| unique[i].x_org != m->mx || unique[i].y_org != m->my
   1900			|| unique[i].width != m->mw || unique[i].height != m->mh)
   1901			{
   1902				dirty = 1;
   1903				m->num = i;
   1904				m->mx = m->wx = unique[i].x_org;
   1905				m->my = m->wy = unique[i].y_org;
   1906				m->mw = m->ww = unique[i].width;
   1907				m->mh = m->wh = unique[i].height;
   1908				updatebarpos(m);
   1909			}
   1910		/* removed monitors if n > nn */
   1911		for (i = nn; i < n; i++) {
   1912			for (m = mons; m && m->next; m = m->next);
   1913			while ((c = m->clients)) {
   1914				dirty = 1;
   1915				m->clients = c->next;
   1916				detachstack(c);
   1917				c->mon = mons;
   1918				attach(c);
   1919				attachstack(c);
   1920			}
   1921			if (m == selmon)
   1922				selmon = mons;
   1923			cleanupmon(m);
   1924		}
   1925		free(unique);
   1926	} else
   1927#endif /* XINERAMA */
   1928	{ /* default monitor setup */
   1929		if (!mons)
   1930			mons = createmon();
   1931		if (mons->mw != sw || mons->mh != sh) {
   1932			dirty = 1;
   1933			mons->mw = mons->ww = sw;
   1934			mons->mh = mons->wh = sh;
   1935			updatebarpos(mons);
   1936		}
   1937	}
   1938	if (dirty) {
   1939		selmon = mons;
   1940		selmon = wintomon(root);
   1941	}
   1942	return dirty;
   1943}
   1944
   1945void
   1946updatenumlockmask(void)
   1947{
   1948	unsigned int i, j;
   1949	XModifierKeymap *modmap;
   1950
   1951	numlockmask = 0;
   1952	modmap = XGetModifierMapping(dpy);
   1953	for (i = 0; i < 8; i++)
   1954		for (j = 0; j < modmap->max_keypermod; j++)
   1955			if (modmap->modifiermap[i * modmap->max_keypermod + j]
   1956				== XKeysymToKeycode(dpy, XK_Num_Lock))
   1957				numlockmask = (1 << i);
   1958	XFreeModifiermap(modmap);
   1959}
   1960
   1961void
   1962updatesizehints(Client *c)
   1963{
   1964	long msize;
   1965	XSizeHints size;
   1966
   1967	if (!XGetWMNormalHints(dpy, c->win, &size, &msize))
   1968		/* size is uninitialized, ensure that size.flags aren't used */
   1969		size.flags = PSize;
   1970	if (size.flags & PBaseSize) {
   1971		c->basew = size.base_width;
   1972		c->baseh = size.base_height;
   1973	} else if (size.flags & PMinSize) {
   1974		c->basew = size.min_width;
   1975		c->baseh = size.min_height;
   1976	} else
   1977		c->basew = c->baseh = 0;
   1978	if (size.flags & PResizeInc) {
   1979		c->incw = size.width_inc;
   1980		c->inch = size.height_inc;
   1981	} else
   1982		c->incw = c->inch = 0;
   1983	if (size.flags & PMaxSize) {
   1984		c->maxw = size.max_width;
   1985		c->maxh = size.max_height;
   1986	} else
   1987		c->maxw = c->maxh = 0;
   1988	if (size.flags & PMinSize) {
   1989		c->minw = size.min_width;
   1990		c->minh = size.min_height;
   1991	} else if (size.flags & PBaseSize) {
   1992		c->minw = size.base_width;
   1993		c->minh = size.base_height;
   1994	} else
   1995		c->minw = c->minh = 0;
   1996	if (size.flags & PAspect) {
   1997		c->mina = (float)size.min_aspect.y / size.min_aspect.x;
   1998		c->maxa = (float)size.max_aspect.x / size.max_aspect.y;
   1999	} else
   2000		c->maxa = c->mina = 0.0;
   2001	c->isfixed = (c->maxw && c->maxh && c->maxw == c->minw && c->maxh == c->minh);
   2002	c->hintsvalid = 1;
   2003}
   2004
   2005void
   2006updatestatus(void)
   2007{
   2008	if (!gettextprop(root, XA_WM_NAME, stext, sizeof(stext)))
   2009		strcpy(stext, "dwm-"VERSION);
   2010	drawbar(selmon);
   2011}
   2012
   2013void
   2014updatetitle(Client *c)
   2015{
   2016	if (!gettextprop(c->win, netatom[NetWMName], c->name, sizeof c->name))
   2017		gettextprop(c->win, XA_WM_NAME, c->name, sizeof c->name);
   2018	if (c->name[0] == '\0') /* hack to mark broken clients */
   2019		strcpy(c->name, broken);
   2020}
   2021
   2022void
   2023updatewindowtype(Client *c)
   2024{
   2025	Atom state = getatomprop(c, netatom[NetWMState]);
   2026	Atom wtype = getatomprop(c, netatom[NetWMWindowType]);
   2027
   2028	if (state == netatom[NetWMFullscreen])
   2029		setfullscreen(c, 1);
   2030	if (wtype == netatom[NetWMWindowTypeDialog])
   2031		c->isfloating = 1;
   2032}
   2033
   2034void
   2035updatewmhints(Client *c)
   2036{
   2037	XWMHints *wmh;
   2038
   2039	if ((wmh = XGetWMHints(dpy, c->win))) {
   2040		if (c == selmon->sel && wmh->flags & XUrgencyHint) {
   2041			wmh->flags &= ~XUrgencyHint;
   2042			XSetWMHints(dpy, c->win, wmh);
   2043		} else
   2044			c->isurgent = (wmh->flags & XUrgencyHint) ? 1 : 0;
   2045		if (wmh->flags & InputHint)
   2046			c->neverfocus = !wmh->input;
   2047		else
   2048			c->neverfocus = 0;
   2049		XFree(wmh);
   2050	}
   2051}
   2052
   2053void
   2054view(const Arg *arg)
   2055{
   2056	if ((arg->ui & TAGMASK) == selmon->tagset[selmon->seltags])
   2057		return;
   2058	selmon->seltags ^= 1; /* toggle sel tagset */
   2059	if (arg->ui & TAGMASK)
   2060		selmon->tagset[selmon->seltags] = arg->ui & TAGMASK;
   2061	focus(NULL);
   2062	arrange(selmon);
   2063}
   2064
   2065Client *
   2066wintoclient(Window w)
   2067{
   2068	Client *c;
   2069	Monitor *m;
   2070
   2071	for (m = mons; m; m = m->next)
   2072		for (c = m->clients; c; c = c->next)
   2073			if (c->win == w)
   2074				return c;
   2075	return NULL;
   2076}
   2077
   2078Monitor *
   2079wintomon(Window w)
   2080{
   2081	int x, y;
   2082	Client *c;
   2083	Monitor *m;
   2084
   2085	if (w == root && getrootptr(&x, &y))
   2086		return recttomon(x, y, 1, 1);
   2087	for (m = mons; m; m = m->next)
   2088		if (w == m->barwin)
   2089			return m;
   2090	if ((c = wintoclient(w)))
   2091		return c->mon;
   2092	return selmon;
   2093}
   2094
   2095/* There's no way to check accesses to destroyed windows, thus those cases are
   2096 * ignored (especially on UnmapNotify's). Other types of errors call Xlibs
   2097 * default error handler, which may call exit. */
   2098int
   2099xerror(Display *dpy, XErrorEvent *ee)
   2100{
   2101	if (ee->error_code == BadWindow
   2102	|| (ee->request_code == X_SetInputFocus && ee->error_code == BadMatch)
   2103	|| (ee->request_code == X_PolyText8 && ee->error_code == BadDrawable)
   2104	|| (ee->request_code == X_PolyFillRectangle && ee->error_code == BadDrawable)
   2105	|| (ee->request_code == X_PolySegment && ee->error_code == BadDrawable)
   2106	|| (ee->request_code == X_ConfigureWindow && ee->error_code == BadMatch)
   2107	|| (ee->request_code == X_GrabButton && ee->error_code == BadAccess)
   2108	|| (ee->request_code == X_GrabKey && ee->error_code == BadAccess)
   2109	|| (ee->request_code == X_CopyArea && ee->error_code == BadDrawable))
   2110		return 0;
   2111	fprintf(stderr, "dwm: fatal error: request code=%d, error code=%d\n",
   2112		ee->request_code, ee->error_code);
   2113	return xerrorxlib(dpy, ee); /* may call exit */
   2114}
   2115
   2116int
   2117xerrordummy(Display *dpy, XErrorEvent *ee)
   2118{
   2119	return 0;
   2120}
   2121
   2122/* Startup Error handler to check if another window manager
   2123 * is already running. */
   2124int
   2125xerrorstart(Display *dpy, XErrorEvent *ee)
   2126{
   2127	die("dwm: another window manager is already running");
   2128	return -1;
   2129}
   2130
   2131void
   2132zoom(const Arg *arg)
   2133{
   2134	Client *c = selmon->sel;
   2135
   2136	if (!selmon->lt[selmon->sellt]->arrange || !c || c->isfloating)
   2137		return;
   2138	if (c == nexttiled(selmon->clients) && !(c = nexttiled(c->next)))
   2139		return;
   2140	pop(c);
   2141}
   2142
   2143int
   2144main(int argc, char *argv[])
   2145{
   2146	if (argc == 2 && !strcmp("-v", argv[1]))
   2147		die("dwm-"VERSION);
   2148	else if (argc != 1)
   2149		die("usage: dwm [-v]");
   2150	if (!setlocale(LC_CTYPE, "") || !XSupportsLocale())
   2151		fputs("warning: no locale support\n", stderr);
   2152	if (!(dpy = XOpenDisplay(NULL)))
   2153		die("dwm: cannot open display");
   2154	checkotherwm();
   2155	setup();
   2156#ifdef __OpenBSD__
   2157	if (pledge("stdio rpath proc exec", NULL) == -1)
   2158		die("pledge");
   2159#endif /* __OpenBSD__ */
   2160	scan();
   2161	run();
   2162	cleanup();
   2163	XCloseDisplay(dpy);
   2164	return EXIT_SUCCESS;
   2165}