nixos-dotfiles

nixos-dotfiles

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