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