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