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