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