nixos-dotfiles

nixos-dotfiles

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