JasonWoof Got questions, comments, patches, etc.? Contact Jason Woofenden
micromizing dwm step 1
[dwm.git] / dwm.c
1 /* See LICENSE file for copyright and license details. */
2 #include <errno.h>
3 #include <locale.h>
4 #include <regex.h>
5 #include <stdarg.h>
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <string.h>
9 #include <unistd.h>
10 #include <sys/select.h>
11 #include <sys/wait.h>
12 #include <X11/cursorfont.h>
13 #include <X11/keysym.h>
14 #include <X11/Xatom.h>
15 #include <X11/Xproto.h>
16 #include <X11/Xutil.h>
17
18 /* macros */
19 #define BUTTONMASK              (ButtonPressMask | ButtonReleaseMask)
20 #define CLEANMASK(mask)         (mask & ~(numlockmask | LockMask))
21 #define MOUSEMASK               (BUTTONMASK | PointerMotionMask)
22
23 /* enums */
24 enum { BarTop, BarBot, BarOff };                        /* bar position */
25 enum { CurNormal, CurResize, CurMove, CurLast };        /* cursor */
26 enum { ColBorder, ColFG, ColBG, ColLast };              /* color */
27 enum { NetSupported, NetWMName, NetLast };              /* EWMH atoms */
28 enum { WMProtocols, WMDelete, WMName, WMState, WMLast };/* default atoms */
29
30 /* typedefs */
31 typedef struct Client Client;
32 struct Client {
33         char name[256];
34         int x, y, w, h;
35         int rx, ry, rw, rh; /* revert geometry */
36         int basew, baseh, incw, inch, maxw, maxh, minw, minh;
37         int minax, maxax, minay, maxay;
38         long flags; 
39         unsigned int border, oldborder;
40         Bool isbanned, isfixed, ismax, isfloating;
41         Bool *tags;
42         Client *next;
43         Client *prev;
44         Client *snext;
45         Window win;
46 };
47
48 typedef struct {
49         int x, y, w, h;
50         unsigned long norm[ColLast];
51         unsigned long sel[ColLast];
52         Drawable drawable;
53         GC gc;
54         struct {
55                 int ascent;
56                 int descent;
57                 int height;
58                 XFontSet set;
59                 XFontStruct *xfont;
60         } font;
61 } DC; /* draw context */
62
63 typedef struct {
64         unsigned long mod;
65         KeySym keysym;
66         void (*func)(const char *arg);
67         const char *arg;
68 } Key;
69
70 typedef struct {
71         const char *symbol;
72         void (*arrange)(void);
73 } Layout;
74
75 typedef struct {
76         const char *prop;
77         const char *tags;
78         Bool isfloating;
79 } Rule;
80
81 typedef struct {
82         regex_t *propregex;
83         regex_t *tagregex;
84 } Regs;
85
86 /* functions */
87 static void eprint(const char *errstr, ...);
88 static void *emallocz(unsigned int size);
89 static void spawn(const char *arg);
90 static void drawsquare(Bool filled, Bool empty, unsigned long col[ColLast]);
91 static unsigned long initcolor(const char *colstr);
92 static void initfont(const char *fontstr);
93 static Bool isoccupied(unsigned int t);
94 static unsigned int textnw(const char *text, unsigned int len);
95 static void drawtext(const char *text, unsigned long col[ColLast]);
96 static void drawbar(void);
97 static void initstyle(void);
98 static void initbar(void);
99 static unsigned int textw(const char *text);
100 static void togglebar(const char *arg);
101 static void updatebarpos(void);
102 static void attachstack(Client *c);
103 static void detachstack(Client *c);
104 static void grabbuttons(Client *c, Bool focused);
105 static Bool isprotodel(Client *c);
106 static void setclientstate(Client *c, long state);
107 static int xerrordummy(Display *dsply, XErrorEvent *ee);
108 static void ban(Client *c);
109 static void configure(Client *c);
110 static void killclient(const char *arg);
111 static void manage(Window w, XWindowAttributes *wa);
112 static void resize(Client *c, int x, int y, int w, int h, Bool sizehints);
113 static void unban(Client *c);
114 static void unmanage(Client *c);
115 static void updatesizehints(Client *c);
116 static void updatetitle(Client *c);
117 static Client *getclient(Window w);
118 static void movemouse(Client *c);
119 static void resizemouse(Client *c);
120 static void buttonpress(XEvent *e);
121 static void configurerequest(XEvent *e);
122 static void configurenotify(XEvent *e);
123 static void destroynotify(XEvent *e);
124 static void enternotify(XEvent *e);
125 static void expose(XEvent *e);
126 static void keypress(XEvent *e);
127 static void leavenotify(XEvent *e);
128 static void mappingnotify(XEvent *e);
129 static void maprequest(XEvent *e);
130 static void propertynotify(XEvent *e);
131 static void unmapnotify(XEvent *e);
132 static void grabkeys(void);
133 static unsigned int idxoftag(const char *tag);
134 static void floating(void); /* default floating layout */
135 static void applyrules(Client *c);
136 static void compileregs(void);
137 static void focusnext(const char *arg);
138 static void focusprev(const char *arg);
139 static void initlayouts(void);
140 static Bool isfloating(void);
141 static Bool isvisible(Client *c);
142 static void restack(void);
143 static void setlayout(const char *arg);
144 static void tag(const char *arg);
145 static void togglefloating(const char *arg);
146 static void togglemax(const char *arg);
147 static void toggletag(const char *arg);
148 static void toggleview(const char *arg);
149 static void view(const char *arg);
150 static void cleanup(void);
151 static long getstate(Window w);
152 static void scan(void);
153 static void setup(void);
154 static int xerrorstart(Display *dsply, XErrorEvent *ee);
155 static Bool gettextprop(Window w, Atom atom, char *text, unsigned int size);
156 static void quit(const char *arg);
157 static int xerror(Display *dpy, XErrorEvent *ee);
158 static void arrange(void);
159 static void attach(Client *c);
160 static void detach(Client *c);
161 static void focus(Client *c);
162 static Bool isarrange(void (*func)());
163 static Client *nexttiled(Client *c);
164 static void setmwfact(const char *arg);
165 static void tile(void);
166 static void zoom(const char *arg);
167
168 #include "config.h"
169
170 /* variables */
171 static char stext[256];
172 static double mwfact = MWFACT;
173 static int screen, sx, sy, sw, sh, wax, way, waw, wah;
174 static int (*xerrorxlib)(Display *, XErrorEvent *);
175 static unsigned int bh;
176 static unsigned int blw = 0;
177 static unsigned int bpos = BARPOS;
178 static unsigned int ltidx = 0; /* default */
179 static unsigned int nlayouts = 0;
180 static unsigned int nrules = 0;
181 static unsigned int ntags;
182 static unsigned int numlockmask = 0;
183 static void (*handler[LASTEvent]) (XEvent *) = {
184         [ButtonPress] = buttonpress,
185         [ConfigureRequest] = configurerequest,
186         [ConfigureNotify] = configurenotify,
187         [DestroyNotify] = destroynotify,
188         [EnterNotify] = enternotify,
189         [LeaveNotify] = leavenotify,
190         [Expose] = expose,
191         [KeyPress] = keypress,
192         [MappingNotify] = mappingnotify,
193         [MapRequest] = maprequest,
194         [PropertyNotify] = propertynotify,
195         [UnmapNotify] = unmapnotify
196 };
197 static Atom wmatom[WMLast], netatom[NetLast];
198 static Bool otherwm, readin;
199 static Bool running = True;
200 static Bool *seltags;
201 static Bool selscreen = True;
202 static Client *clients = NULL;
203 static Client *sel = NULL;
204 static Client *stack = NULL;
205 static Cursor cursor[CurLast];
206 static Display *dpy;
207 static DC dc = {0};
208 static Window barwin, root;
209 static Regs *regs = NULL;
210
211 static void
212 eprint(const char *errstr, ...) {
213         va_list ap;
214
215         va_start(ap, errstr);
216         vfprintf(stderr, errstr, ap);
217         va_end(ap);
218         exit(EXIT_FAILURE);
219 }
220
221 static void *
222 emallocz(unsigned int size) {
223         void *res = calloc(1, size);
224
225         if(!res)
226                 eprint("fatal: could not malloc() %u bytes\n", size);
227         return res;
228 }
229
230 static void
231 spawn(const char *arg) {
232         static char *shell = NULL;
233
234         if(!shell && !(shell = getenv("SHELL")))
235                 shell = "/bin/sh";
236         if(!arg)
237                 return;
238         /* The double-fork construct avoids zombie processes and keeps the code
239          * clean from stupid signal handlers. */
240         if(fork() == 0) {
241                 if(fork() == 0) {
242                         if(dpy)
243                                 close(ConnectionNumber(dpy));
244                         setsid();
245                         execl(shell, shell, "-c", arg, (char *)NULL);
246                         fprintf(stderr, "dwm: execl '%s -c %s'", shell, arg);
247                         perror(" failed");
248                 }
249                 exit(0);
250         }
251         wait(0);
252 }
253
254 static void
255 drawsquare(Bool filled, Bool empty, unsigned long col[ColLast]) {
256         int x;
257         XGCValues gcv;
258         XRectangle r = { dc.x, dc.y, dc.w, dc.h };
259
260         gcv.foreground = col[ColFG];
261         XChangeGC(dpy, dc.gc, GCForeground, &gcv);
262         x = (dc.font.ascent + dc.font.descent + 2) / 4;
263         r.x = dc.x + 1;
264         r.y = dc.y + 1;
265         if(filled) {
266                 r.width = r.height = x + 1;
267                 XFillRectangles(dpy, dc.drawable, dc.gc, &r, 1);
268         }
269         else if(empty) {
270                 r.width = r.height = x;
271                 XDrawRectangles(dpy, dc.drawable, dc.gc, &r, 1);
272         }
273 }
274
275 static unsigned long
276 initcolor(const char *colstr) {
277         Colormap cmap = DefaultColormap(dpy, screen);
278         XColor color;
279
280         if(!XAllocNamedColor(dpy, cmap, colstr, &color, &color))
281                 eprint("error, cannot allocate color '%s'\n", colstr);
282         return color.pixel;
283 }
284
285 static void
286 initfont(const char *fontstr) {
287         char *def, **missing;
288         int i, n;
289
290         missing = NULL;
291         if(dc.font.set)
292                 XFreeFontSet(dpy, dc.font.set);
293         dc.font.set = XCreateFontSet(dpy, fontstr, &missing, &n, &def);
294         if(missing) {
295                 while(n--)
296                         fprintf(stderr, "dwm: missing fontset: %s\n", missing[n]);
297                 XFreeStringList(missing);
298         }
299         if(dc.font.set) {
300                 XFontSetExtents *font_extents;
301                 XFontStruct **xfonts;
302                 char **font_names;
303                 dc.font.ascent = dc.font.descent = 0;
304                 font_extents = XExtentsOfFontSet(dc.font.set);
305                 n = XFontsOfFontSet(dc.font.set, &xfonts, &font_names);
306                 for(i = 0, dc.font.ascent = 0, dc.font.descent = 0; i < n; i++) {
307                         if(dc.font.ascent < (*xfonts)->ascent)
308                                 dc.font.ascent = (*xfonts)->ascent;
309                         if(dc.font.descent < (*xfonts)->descent)
310                                 dc.font.descent = (*xfonts)->descent;
311                         xfonts++;
312                 }
313         }
314         else {
315                 if(dc.font.xfont)
316                         XFreeFont(dpy, dc.font.xfont);
317                 dc.font.xfont = NULL;
318                 if(!(dc.font.xfont = XLoadQueryFont(dpy, fontstr))
319                 || !(dc.font.xfont = XLoadQueryFont(dpy, "fixed")))
320                         eprint("error, cannot load font: '%s'\n", fontstr);
321                 dc.font.ascent = dc.font.xfont->ascent;
322                 dc.font.descent = dc.font.xfont->descent;
323         }
324         dc.font.height = dc.font.ascent + dc.font.descent;
325 }
326
327 static Bool
328 isoccupied(unsigned int t) {
329         Client *c;
330
331         for(c = clients; c; c = c->next)
332                 if(c->tags[t])
333                         return True;
334         return False;
335 }
336
337 static unsigned int
338 textnw(const char *text, unsigned int len) {
339         XRectangle r;
340
341         if(dc.font.set) {
342                 XmbTextExtents(dc.font.set, text, len, NULL, &r);
343                 return r.width;
344         }
345         return XTextWidth(dc.font.xfont, text, len);
346 }
347
348 static void
349 drawtext(const char *text, unsigned long col[ColLast]) {
350         int x, y, w, h;
351         static char buf[256];
352         unsigned int len, olen;
353         XRectangle r = { dc.x, dc.y, dc.w, dc.h };
354
355         XSetForeground(dpy, dc.gc, col[ColBG]);
356         XFillRectangles(dpy, dc.drawable, dc.gc, &r, 1);
357         if(!text)
358                 return;
359         w = 0;
360         olen = len = strlen(text);
361         if(len >= sizeof buf)
362                 len = sizeof buf - 1;
363         memcpy(buf, text, len);
364         buf[len] = 0;
365         h = dc.font.ascent + dc.font.descent;
366         y = dc.y + (dc.h / 2) - (h / 2) + dc.font.ascent;
367         x = dc.x + (h / 2);
368         /* shorten text if necessary */
369         while(len && (w = textnw(buf, len)) > dc.w - h)
370                 buf[--len] = 0;
371         if(len < olen) {
372                 if(len > 1)
373                         buf[len - 1] = '.';
374                 if(len > 2)
375                         buf[len - 2] = '.';
376                 if(len > 3)
377                         buf[len - 3] = '.';
378         }
379         if(w > dc.w)
380                 return; /* too long */
381         XSetForeground(dpy, dc.gc, col[ColFG]);
382         if(dc.font.set)
383                 XmbDrawString(dpy, dc.drawable, dc.font.set, dc.gc, x, y, buf, len);
384         else
385                 XDrawString(dpy, dc.drawable, dc.gc, x, y, buf, len);
386 }
387
388 static void
389 drawbar(void) {
390         int i, x;
391
392         dc.x = dc.y = 0;
393         for(i = 0; i < ntags; i++) {
394                 dc.w = textw(tags[i]);
395                 if(seltags[i]) {
396                         drawtext(tags[i], dc.sel);
397                         drawsquare(sel && sel->tags[i], isoccupied(i), dc.sel);
398                 }
399                 else {
400                         drawtext(tags[i], dc.norm);
401                         drawsquare(sel && sel->tags[i], isoccupied(i), dc.norm);
402                 }
403                 dc.x += dc.w;
404         }
405         dc.w = blw;
406         drawtext(layouts[ltidx].symbol, dc.norm);
407         x = dc.x + dc.w;
408         dc.w = textw(stext);
409         dc.x = sw - dc.w;
410         if(dc.x < x) {
411                 dc.x = x;
412                 dc.w = sw - x;
413         }
414         drawtext(stext, dc.norm);
415         if((dc.w = dc.x - x) > bh) {
416                 dc.x = x;
417                 if(sel) {
418                         drawtext(sel->name, dc.sel);
419                         drawsquare(sel->ismax, sel->isfloating, dc.sel);
420                 }
421                 else
422                         drawtext(NULL, dc.norm);
423         }
424         XCopyArea(dpy, dc.drawable, barwin, dc.gc, 0, 0, sw, bh, 0, 0);
425         XSync(dpy, False);
426 }
427
428 static void
429 initstyle(void) {
430         dc.norm[ColBorder] = initcolor(NORMBORDERCOLOR);
431         dc.norm[ColBG] = initcolor(NORMBGCOLOR);
432         dc.norm[ColFG] = initcolor(NORMFGCOLOR);
433         dc.sel[ColBorder] = initcolor(SELBORDERCOLOR);
434         dc.sel[ColBG] = initcolor(SELBGCOLOR);
435         dc.sel[ColFG] = initcolor(SELFGCOLOR);
436         initfont(FONT);
437         dc.h = bh = dc.font.height + 2;
438 }
439
440 static void
441 initbar(void) {
442         XSetWindowAttributes wa;
443
444         wa.override_redirect = 1;
445         wa.background_pixmap = ParentRelative;
446         wa.event_mask = ButtonPressMask | ExposureMask;
447         barwin = XCreateWindow(dpy, root, sx, sy, sw, bh, 0,
448                         DefaultDepth(dpy, screen), CopyFromParent, DefaultVisual(dpy, screen),
449                         CWOverrideRedirect | CWBackPixmap | CWEventMask, &wa);
450         XDefineCursor(dpy, barwin, cursor[CurNormal]);
451         updatebarpos();
452         XMapRaised(dpy, barwin);
453         strcpy(stext, "dwm-"VERSION);
454         dc.drawable = XCreatePixmap(dpy, root, sw, bh, DefaultDepth(dpy, screen));
455         dc.gc = XCreateGC(dpy, root, 0, 0);
456         XSetLineAttributes(dpy, dc.gc, 1, LineSolid, CapButt, JoinMiter);
457         if(!dc.font.set)
458                 XSetFont(dpy, dc.gc, dc.font.xfont->fid);
459 }
460
461 static unsigned int
462 textw(const char *text) {
463         return textnw(text, strlen(text)) + dc.font.height;
464 }
465
466 static void
467 togglebar(const char *arg) {
468         if(bpos == BarOff)
469                 bpos = (BARPOS == BarOff) ? BarTop : BARPOS;
470         else
471                 bpos = BarOff;
472         updatebarpos();
473         arrange();
474 }
475
476 static void
477 updatebarpos(void) {
478         XEvent ev;
479
480         wax = sx;
481         way = sy;
482         wah = sh;
483         waw = sw;
484         switch(bpos) {
485         default:
486                 wah -= bh;
487                 way += bh;
488                 XMoveWindow(dpy, barwin, sx, sy);
489                 break;
490         case BarBot:
491                 wah -= bh;
492                 XMoveWindow(dpy, barwin, sx, sy + wah);
493                 break;
494         case BarOff:
495                 XMoveWindow(dpy, barwin, sx, sy - bh);
496                 break;
497         }
498         XSync(dpy, False);
499         while(XCheckMaskEvent(dpy, EnterWindowMask, &ev));
500 }
501
502 static void
503 attachstack(Client *c) {
504         c->snext = stack;
505         stack = c;
506 }
507
508 static void
509 detachstack(Client *c) {
510         Client **tc;
511
512         for(tc=&stack; *tc && *tc != c; tc=&(*tc)->snext);
513         *tc = c->snext;
514 }
515
516 static void
517 grabbuttons(Client *c, Bool focused) {
518         XUngrabButton(dpy, AnyButton, AnyModifier, c->win);
519
520         if(focused) {
521                 XGrabButton(dpy, Button1, MODKEY, c->win, False, BUTTONMASK,
522                                 GrabModeAsync, GrabModeSync, None, None);
523                 XGrabButton(dpy, Button1, MODKEY | LockMask, c->win, False, BUTTONMASK,
524                                 GrabModeAsync, GrabModeSync, None, None);
525                 XGrabButton(dpy, Button1, MODKEY | numlockmask, c->win, False, BUTTONMASK,
526                                 GrabModeAsync, GrabModeSync, None, None);
527                 XGrabButton(dpy, Button1, MODKEY | numlockmask | LockMask, c->win, False, BUTTONMASK,
528                                 GrabModeAsync, GrabModeSync, None, None);
529
530                 XGrabButton(dpy, Button2, MODKEY, c->win, False, BUTTONMASK,
531                                 GrabModeAsync, GrabModeSync, None, None);
532                 XGrabButton(dpy, Button2, MODKEY | LockMask, c->win, False, BUTTONMASK,
533                                 GrabModeAsync, GrabModeSync, None, None);
534                 XGrabButton(dpy, Button2, MODKEY | numlockmask, c->win, False, BUTTONMASK,
535                                 GrabModeAsync, GrabModeSync, None, None);
536                 XGrabButton(dpy, Button2, MODKEY | numlockmask | LockMask, c->win, False, BUTTONMASK,
537                                 GrabModeAsync, GrabModeSync, None, None);
538
539                 XGrabButton(dpy, Button3, MODKEY, c->win, False, BUTTONMASK,
540                                 GrabModeAsync, GrabModeSync, None, None);
541                 XGrabButton(dpy, Button3, MODKEY | LockMask, c->win, False, BUTTONMASK,
542                                 GrabModeAsync, GrabModeSync, None, None);
543                 XGrabButton(dpy, Button3, MODKEY | numlockmask, c->win, False, BUTTONMASK,
544                                 GrabModeAsync, GrabModeSync, None, None);
545                 XGrabButton(dpy, Button3, MODKEY | numlockmask | LockMask, c->win, False, BUTTONMASK,
546                                 GrabModeAsync, GrabModeSync, None, None);
547         }
548         else
549                 XGrabButton(dpy, AnyButton, AnyModifier, c->win, False, BUTTONMASK,
550                                 GrabModeAsync, GrabModeSync, None, None);
551 }
552
553 static Bool
554 isprotodel(Client *c) {
555         int i, n;
556         Atom *protocols;
557         Bool ret = False;
558
559         if(XGetWMProtocols(dpy, c->win, &protocols, &n)) {
560                 for(i = 0; !ret && i < n; i++)
561                         if(protocols[i] == wmatom[WMDelete])
562                                 ret = True;
563                 XFree(protocols);
564         }
565         return ret;
566 }
567
568 static void
569 setclientstate(Client *c, long state) {
570         long data[] = {state, None};
571
572         XChangeProperty(dpy, c->win, wmatom[WMState], wmatom[WMState], 32,
573                         PropModeReplace, (unsigned char *)data, 2);
574 }
575
576 static int
577 xerrordummy(Display *dsply, XErrorEvent *ee) {
578         return 0;
579 }
580
581 static void
582 ban(Client *c) {
583         if(c->isbanned)
584                 return;
585         XMoveWindow(dpy, c->win, c->x + 2 * sw, c->y);
586         c->isbanned = True;
587 }
588
589 static void
590 configure(Client *c) {
591         XConfigureEvent ce;
592
593         ce.type = ConfigureNotify;
594         ce.display = dpy;
595         ce.event = c->win;
596         ce.window = c->win;
597         ce.x = c->x;
598         ce.y = c->y;
599         ce.width = c->w;
600         ce.height = c->h;
601         ce.border_width = c->border;
602         ce.above = None;
603         ce.override_redirect = False;
604         XSendEvent(dpy, c->win, False, StructureNotifyMask, (XEvent *)&ce);
605 }
606
607 static void
608 killclient(const char *arg) {
609         XEvent ev;
610
611         if(!sel)
612                 return;
613         if(isprotodel(sel)) {
614                 ev.type = ClientMessage;
615                 ev.xclient.window = sel->win;
616                 ev.xclient.message_type = wmatom[WMProtocols];
617                 ev.xclient.format = 32;
618                 ev.xclient.data.l[0] = wmatom[WMDelete];
619                 ev.xclient.data.l[1] = CurrentTime;
620                 XSendEvent(dpy, sel->win, False, NoEventMask, &ev);
621         }
622         else
623                 XKillClient(dpy, sel->win);
624 }
625
626 static void
627 manage(Window w, XWindowAttributes *wa) {
628         unsigned int i;
629         Client *c, *t = NULL;
630         Window trans;
631         Status rettrans;
632         XWindowChanges wc;
633
634         c = emallocz(sizeof(Client));
635         c->tags = emallocz(ntags * sizeof(Bool));
636         c->win = w;
637         c->x = wa->x;
638         c->y = wa->y;
639         c->w = wa->width;
640         c->h = wa->height;
641         c->oldborder = wa->border_width;
642         if(c->w == sw && c->h == sh) {
643                 c->x = sx;
644                 c->y = sy;
645                 c->border = wa->border_width;
646         }
647         else {
648                 if(c->x + c->w + 2 * c->border > wax + waw)
649                         c->x = wax + waw - c->w - 2 * c->border;
650                 if(c->y + c->h + 2 * c->border > way + wah)
651                         c->y = way + wah - c->h - 2 * c->border;
652                 if(c->x < wax)
653                         c->x = wax;
654                 if(c->y < way)
655                         c->y = way;
656                 c->border = BORDERPX;
657         }
658         wc.border_width = c->border;
659         XConfigureWindow(dpy, w, CWBorderWidth, &wc);
660         XSetWindowBorder(dpy, w, dc.norm[ColBorder]);
661         configure(c); /* propagates border_width, if size doesn't change */
662         updatesizehints(c);
663         XSelectInput(dpy, w,
664                 StructureNotifyMask | PropertyChangeMask | EnterWindowMask);
665         grabbuttons(c, False);
666         updatetitle(c);
667         if((rettrans = XGetTransientForHint(dpy, w, &trans) == Success))
668                 for(t = clients; t && t->win != trans; t = t->next);
669         if(t)
670                 for(i = 0; i < ntags; i++)
671                         c->tags[i] = t->tags[i];
672         applyrules(c);
673         if(!c->isfloating)
674                 c->isfloating = (rettrans == Success) || c->isfixed;
675         attach(c);
676         attachstack(c);
677         XMoveResizeWindow(dpy, c->win, c->x, c->y, c->w, c->h); /* some windows require this */
678         ban(c);
679         XMapWindow(dpy, c->win);
680         setclientstate(c, NormalState);
681         arrange();
682 }
683
684 static void
685 resize(Client *c, int x, int y, int w, int h, Bool sizehints) {
686         double dx, dy, max, min, ratio;
687         XWindowChanges wc; 
688
689         if(sizehints) {
690                 if(c->minay > 0 && c->maxay > 0 && (h - c->baseh) > 0 && (w - c->basew) > 0) {
691                         dx = (double)(w - c->basew);
692                         dy = (double)(h - c->baseh);
693                         min = (double)(c->minax) / (double)(c->minay);
694                         max = (double)(c->maxax) / (double)(c->maxay);
695                         ratio = dx / dy;
696                         if(max > 0 && min > 0 && ratio > 0) {
697                                 if(ratio < min) {
698                                         dy = (dx * min + dy) / (min * min + 1);
699                                         dx = dy * min;
700                                         w = (int)dx + c->basew;
701                                         h = (int)dy + c->baseh;
702                                 }
703                                 else if(ratio > max) {
704                                         dy = (dx * min + dy) / (max * max + 1);
705                                         dx = dy * min;
706                                         w = (int)dx + c->basew;
707                                         h = (int)dy + c->baseh;
708                                 }
709                         }
710                 }
711                 if(c->minw && w < c->minw)
712                         w = c->minw;
713                 if(c->minh && h < c->minh)
714                         h = c->minh;
715                 if(c->maxw && w > c->maxw)
716                         w = c->maxw;
717                 if(c->maxh && h > c->maxh)
718                         h = c->maxh;
719                 if(c->incw)
720                         w -= (w - c->basew) % c->incw;
721                 if(c->inch)
722                         h -= (h - c->baseh) % c->inch;
723         }
724         if(w <= 0 || h <= 0)
725                 return;
726         /* offscreen appearance fixes */
727         if(x > sw)
728                 x = sw - w - 2 * c->border;
729         if(y > sh)
730                 y = sh - h - 2 * c->border;
731         if(x + w + 2 * c->border < sx)
732                 x = sx;
733         if(y + h + 2 * c->border < sy)
734                 y = sy;
735         if(c->x != x || c->y != y || c->w != w || c->h != h) {
736                 c->x = wc.x = x;
737                 c->y = wc.y = y;
738                 c->w = wc.width = w;
739                 c->h = wc.height = h;
740                 wc.border_width = c->border;
741                 XConfigureWindow(dpy, c->win, CWX | CWY | CWWidth | CWHeight | CWBorderWidth, &wc);
742                 configure(c);
743                 XSync(dpy, False);
744         }
745 }
746
747 static void
748 unban(Client *c) {
749         if(!c->isbanned)
750                 return;
751         XMoveWindow(dpy, c->win, c->x, c->y);
752         c->isbanned = False;
753 }
754
755 static void
756 unmanage(Client *c) {
757         XWindowChanges wc;
758
759         wc.border_width = c->oldborder;
760         /* The server grab construct avoids race conditions. */
761         XGrabServer(dpy);
762         XSetErrorHandler(xerrordummy);
763         XConfigureWindow(dpy, c->win, CWBorderWidth, &wc); /* restore border */
764         detach(c);
765         detachstack(c);
766         if(sel == c)
767                 focus(NULL);
768         XUngrabButton(dpy, AnyButton, AnyModifier, c->win);
769         setclientstate(c, WithdrawnState);
770         free(c->tags);
771         free(c);
772         XSync(dpy, False);
773         XSetErrorHandler(xerror);
774         XUngrabServer(dpy);
775         arrange();
776 }
777
778 static void
779 updatesizehints(Client *c) {
780         long msize;
781         XSizeHints size;
782
783         if(!XGetWMNormalHints(dpy, c->win, &size, &msize) || !size.flags)
784                 size.flags = PSize;
785         c->flags = size.flags;
786         if(c->flags & PBaseSize) {
787                 c->basew = size.base_width;
788                 c->baseh = size.base_height;
789         }
790         else if(c->flags & PMinSize) {
791                 c->basew = size.min_width;
792                 c->baseh = size.min_height;
793         }
794         else
795                 c->basew = c->baseh = 0;
796         if(c->flags & PResizeInc) {
797                 c->incw = size.width_inc;
798                 c->inch = size.height_inc;
799         }
800         else
801                 c->incw = c->inch = 0;
802         if(c->flags & PMaxSize) {
803                 c->maxw = size.max_width;
804                 c->maxh = size.max_height;
805         }
806         else
807                 c->maxw = c->maxh = 0;
808         if(c->flags & PMinSize) {
809                 c->minw = size.min_width;
810                 c->minh = size.min_height;
811         }
812         else if(c->flags & PBaseSize) {
813                 c->minw = size.base_width;
814                 c->minh = size.base_height;
815         }
816         else
817                 c->minw = c->minh = 0;
818         if(c->flags & PAspect) {
819                 c->minax = size.min_aspect.x;
820                 c->maxax = size.max_aspect.x;
821                 c->minay = size.min_aspect.y;
822                 c->maxay = size.max_aspect.y;
823         }
824         else
825                 c->minax = c->maxax = c->minay = c->maxay = 0;
826         c->isfixed = (c->maxw && c->minw && c->maxh && c->minh
827                         && c->maxw == c->minw && c->maxh == c->minh);
828 }
829
830 static void
831 updatetitle(Client *c) {
832         if(!gettextprop(c->win, netatom[NetWMName], c->name, sizeof c->name))
833                 gettextprop(c->win, wmatom[WMName], c->name, sizeof c->name);
834 }
835
836 static Client *
837 getclient(Window w) {
838         Client *c;
839
840         for(c = clients; c && c->win != w; c = c->next);
841         return c;
842 }
843
844 static void
845 movemouse(Client *c) {
846         int x1, y1, ocx, ocy, di, nx, ny;
847         unsigned int dui;
848         Window dummy;
849         XEvent ev;
850
851         ocx = nx = c->x;
852         ocy = ny = c->y;
853         if(XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync,
854                         None, cursor[CurMove], CurrentTime) != GrabSuccess)
855                 return;
856         c->ismax = False;
857         XQueryPointer(dpy, root, &dummy, &dummy, &x1, &y1, &di, &di, &dui);
858         for(;;) {
859                 XMaskEvent(dpy, MOUSEMASK | ExposureMask | SubstructureRedirectMask, &ev);
860                 switch (ev.type) {
861                 case ButtonRelease:
862                         XUngrabPointer(dpy, CurrentTime);
863                         return;
864                 case ConfigureRequest:
865                 case Expose:
866                 case MapRequest:
867                         handler[ev.type](&ev);
868                         break;
869                 case MotionNotify:
870                         XSync(dpy, False);
871                         nx = ocx + (ev.xmotion.x - x1);
872                         ny = ocy + (ev.xmotion.y - y1);
873                         if(abs(wax + nx) < SNAP)
874                                 nx = wax;
875                         else if(abs((wax + waw) - (nx + c->w + 2 * c->border)) < SNAP)
876                                 nx = wax + waw - c->w - 2 * c->border;
877                         if(abs(way - ny) < SNAP)
878                                 ny = way;
879                         else if(abs((way + wah) - (ny + c->h + 2 * c->border)) < SNAP)
880                                 ny = way + wah - c->h - 2 * c->border;
881                         resize(c, nx, ny, c->w, c->h, False);
882                         break;
883                 }
884         }
885 }
886
887 static void
888 resizemouse(Client *c) {
889         int ocx, ocy;
890         int nw, nh;
891         XEvent ev;
892
893         ocx = c->x;
894         ocy = c->y;
895         if(XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync,
896                         None, cursor[CurResize], CurrentTime) != GrabSuccess)
897                 return;
898         c->ismax = False;
899         XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->border - 1, c->h + c->border - 1);
900         for(;;) {
901                 XMaskEvent(dpy, MOUSEMASK | ExposureMask | SubstructureRedirectMask , &ev);
902                 switch(ev.type) {
903                 case ButtonRelease:
904                         XWarpPointer(dpy, None, c->win, 0, 0, 0, 0,
905                                         c->w + c->border - 1, c->h + c->border - 1);
906                         XUngrabPointer(dpy, CurrentTime);
907                         while(XCheckMaskEvent(dpy, EnterWindowMask, &ev));
908                         return;
909                 case ConfigureRequest:
910                 case Expose:
911                 case MapRequest:
912                         handler[ev.type](&ev);
913                         break;
914                 case MotionNotify:
915                         XSync(dpy, False);
916                         if((nw = ev.xmotion.x - ocx - 2 * c->border + 1) <= 0)
917                                 nw = 1;
918                         if((nh = ev.xmotion.y - ocy - 2 * c->border + 1) <= 0)
919                                 nh = 1;
920                         resize(c, c->x, c->y, nw, nh, True);
921                         break;
922                 }
923         }
924 }
925
926 static void
927 buttonpress(XEvent *e) {
928         unsigned int i, x;
929         Client *c;
930         XButtonPressedEvent *ev = &e->xbutton;
931
932         if(barwin == ev->window) {
933                 x = 0;
934                 for(i = 0; i < ntags; i++) {
935                         x += textw(tags[i]);
936                         if(ev->x < x) {
937                                 if(ev->button == Button1) {
938                                         if(ev->state & MODKEY)
939                                                 tag(tags[i]);
940                                         else
941                                                 view(tags[i]);
942                                 }
943                                 else if(ev->button == Button3) {
944                                         if(ev->state & MODKEY)
945                                                 toggletag(tags[i]);
946                                         else
947                                                 toggleview(tags[i]);
948                                 }
949                                 return;
950                         }
951                 }
952                 if((ev->x < x + blw) && ev->button == Button1)
953                         setlayout(NULL);
954         }
955         else if((c = getclient(ev->window))) {
956                 focus(c);
957                 if(CLEANMASK(ev->state) != MODKEY)
958                         return;
959                 if(ev->button == Button1 && (isfloating() || c->isfloating)) {
960                         restack();
961                         movemouse(c);
962                 }
963                 else if(ev->button == Button2)
964                         zoom(NULL);
965                 else if(ev->button == Button3
966                 && (isfloating() || c->isfloating) && !c->isfixed)
967                 {
968                         restack();
969                         resizemouse(c);
970                 }
971         }
972 }
973
974 static void
975 configurerequest(XEvent *e) {
976         Client *c;
977         XConfigureRequestEvent *ev = &e->xconfigurerequest;
978         XWindowChanges wc;
979
980         if((c = getclient(ev->window))) {
981                 c->ismax = False;
982                 if(ev->value_mask & CWBorderWidth)
983                         c->border = ev->border_width;
984                 if(c->isfixed || c->isfloating || isfloating()) {
985                         if(ev->value_mask & CWX)
986                                 c->x = ev->x;
987                         if(ev->value_mask & CWY)
988                                 c->y = ev->y;
989                         if(ev->value_mask & CWWidth)
990                                 c->w = ev->width;
991                         if(ev->value_mask & CWHeight)
992                                 c->h = ev->height;
993                         if((c->x + c->w) > sw && c->isfloating)
994                                 c->x = sw / 2 - c->w / 2; /* center in x direction */
995                         if((c->y + c->h) > sh && c->isfloating)
996                                 c->y = sh / 2 - c->h / 2; /* center in y direction */
997                         if((ev->value_mask & (CWX | CWY))
998                         && !(ev->value_mask & (CWWidth | CWHeight)))
999                                 configure(c);
1000                         if(isvisible(c))
1001                                 XMoveResizeWindow(dpy, c->win, c->x, c->y, c->w, c->h);
1002                 }
1003                 else
1004                         configure(c);
1005         }
1006         else {
1007                 wc.x = ev->x;
1008                 wc.y = ev->y;
1009                 wc.width = ev->width;
1010                 wc.height = ev->height;
1011                 wc.border_width = ev->border_width;
1012                 wc.sibling = ev->above;
1013                 wc.stack_mode = ev->detail;
1014                 XConfigureWindow(dpy, ev->window, ev->value_mask, &wc);
1015         }
1016         XSync(dpy, False);
1017 }
1018
1019 static void
1020 configurenotify(XEvent *e) {
1021         XConfigureEvent *ev = &e->xconfigure;
1022
1023         if (ev->window == root && (ev->width != sw || ev->height != sh)) {
1024                 sw = ev->width;
1025                 sh = ev->height;
1026                 XFreePixmap(dpy, dc.drawable);
1027                 dc.drawable = XCreatePixmap(dpy, root, sw, bh, DefaultDepth(dpy, screen));
1028                 XResizeWindow(dpy, barwin, sw, bh);
1029                 updatebarpos();
1030                 arrange();
1031         }
1032 }
1033
1034 static void
1035 destroynotify(XEvent *e) {
1036         Client *c;
1037         XDestroyWindowEvent *ev = &e->xdestroywindow;
1038
1039         if((c = getclient(ev->window)))
1040                 unmanage(c);
1041 }
1042
1043 static void
1044 enternotify(XEvent *e) {
1045         Client *c;
1046         XCrossingEvent *ev = &e->xcrossing;
1047
1048         if(ev->mode != NotifyNormal || ev->detail == NotifyInferior)
1049                 return;
1050         if((c = getclient(ev->window)))
1051                 focus(c);
1052         else if(ev->window == root) {
1053                 selscreen = True;
1054                 focus(NULL);
1055         }
1056 }
1057
1058 static void
1059 expose(XEvent *e) {
1060         XExposeEvent *ev = &e->xexpose;
1061
1062         if(ev->count == 0) {
1063                 if(barwin == ev->window)
1064                         drawbar();
1065         }
1066 }
1067
1068 static void
1069 keypress(XEvent *e) {
1070         KEYS
1071         unsigned int len = sizeof keys / sizeof keys[0];
1072         unsigned int i;
1073         KeySym keysym;
1074         XKeyEvent *ev = &e->xkey;
1075
1076         keysym = XKeycodeToKeysym(dpy, (KeyCode)ev->keycode, 0);
1077         for(i = 0; i < len; i++)
1078                 if(keysym == keys[i].keysym
1079                 && CLEANMASK(keys[i].mod) == CLEANMASK(ev->state))
1080                 {
1081                         if(keys[i].func)
1082                                 keys[i].func(keys[i].arg);
1083                 }
1084 }
1085
1086 static void
1087 leavenotify(XEvent *e) {
1088         XCrossingEvent *ev = &e->xcrossing;
1089
1090         if((ev->window == root) && !ev->same_screen) {
1091                 selscreen = False;
1092                 focus(NULL);
1093         }
1094 }
1095
1096 static void
1097 mappingnotify(XEvent *e) {
1098         XMappingEvent *ev = &e->xmapping;
1099
1100         XRefreshKeyboardMapping(ev);
1101         if(ev->request == MappingKeyboard)
1102                 grabkeys();
1103 }
1104
1105 static void
1106 maprequest(XEvent *e) {
1107         static XWindowAttributes wa;
1108         XMapRequestEvent *ev = &e->xmaprequest;
1109
1110         if(!XGetWindowAttributes(dpy, ev->window, &wa))
1111                 return;
1112         if(wa.override_redirect)
1113                 return;
1114         if(!getclient(ev->window))
1115                 manage(ev->window, &wa);
1116 }
1117
1118 static void
1119 propertynotify(XEvent *e) {
1120         Client *c;
1121         Window trans;
1122         XPropertyEvent *ev = &e->xproperty;
1123
1124         if(ev->state == PropertyDelete)
1125                 return; /* ignore */
1126         if((c = getclient(ev->window))) {
1127                 switch (ev->atom) {
1128                         default: break;
1129                         case XA_WM_TRANSIENT_FOR:
1130                                 XGetTransientForHint(dpy, c->win, &trans);
1131                                 if(!c->isfloating && (c->isfloating = (getclient(trans) != NULL)))
1132                                         arrange();
1133                                 break;
1134                         case XA_WM_NORMAL_HINTS:
1135                                 updatesizehints(c);
1136                                 break;
1137                 }
1138                 if(ev->atom == XA_WM_NAME || ev->atom == netatom[NetWMName]) {
1139                         updatetitle(c);
1140                         if(c == sel)
1141                                 drawbar();
1142                 }
1143         }
1144 }
1145
1146 static void
1147 unmapnotify(XEvent *e) {
1148         Client *c;
1149         XUnmapEvent *ev = &e->xunmap;
1150
1151         if((c = getclient(ev->window)))
1152                 unmanage(c);
1153 }
1154
1155 static void
1156 grabkeys(void) {
1157         KEYS
1158         unsigned int len = sizeof keys / sizeof keys[0];
1159         unsigned int i;
1160         KeyCode code;
1161
1162         XUngrabKey(dpy, AnyKey, AnyModifier, root);
1163         for(i = 0; i < len; i++) {
1164                 code = XKeysymToKeycode(dpy, keys[i].keysym);
1165                 XGrabKey(dpy, code, keys[i].mod, root, True,
1166                                 GrabModeAsync, GrabModeAsync);
1167                 XGrabKey(dpy, code, keys[i].mod | LockMask, root, True,
1168                                 GrabModeAsync, GrabModeAsync);
1169                 XGrabKey(dpy, code, keys[i].mod | numlockmask, root, True,
1170                                 GrabModeAsync, GrabModeAsync);
1171                 XGrabKey(dpy, code, keys[i].mod | numlockmask | LockMask, root, True,
1172                                 GrabModeAsync, GrabModeAsync);
1173         }
1174 }
1175
1176 static unsigned int
1177 idxoftag(const char *tag) {
1178         unsigned int i;
1179
1180         for(i = 0; i < ntags; i++)
1181                 if(tags[i] == tag)
1182                         return i;
1183         return 0;
1184 }
1185
1186 static void
1187 floating(void) { /* default floating layout */
1188         Client *c;
1189
1190         for(c = clients; c; c = c->next)
1191                 if(isvisible(c))
1192                         resize(c, c->x, c->y, c->w, c->h, True);
1193 }
1194
1195 static void
1196 applyrules(Client *c) {
1197         static char buf[512];
1198         unsigned int i, j;
1199         regmatch_t tmp;
1200         Bool matched = False;
1201         XClassHint ch = { 0 };
1202
1203         /* rule matching */
1204         XGetClassHint(dpy, c->win, &ch);
1205         snprintf(buf, sizeof buf, "%s:%s:%s",
1206                         ch.res_class ? ch.res_class : "",
1207                         ch.res_name ? ch.res_name : "", c->name);
1208         for(i = 0; i < nrules; i++)
1209                 if(regs[i].propregex && !regexec(regs[i].propregex, buf, 1, &tmp, 0)) {
1210                         c->isfloating = rules[i].isfloating;
1211                         for(j = 0; regs[i].tagregex && j < ntags; j++) {
1212                                 if(!regexec(regs[i].tagregex, tags[j], 1, &tmp, 0)) {
1213                                         matched = True;
1214                                         c->tags[j] = True;
1215                                 }
1216                         }
1217                 }
1218         if(ch.res_class)
1219                 XFree(ch.res_class);
1220         if(ch.res_name)
1221                 XFree(ch.res_name);
1222         if(!matched)
1223                 for(i = 0; i < ntags; i++)
1224                         c->tags[i] = seltags[i];
1225 }
1226
1227 static void
1228 compileregs(void) {
1229         unsigned int i;
1230         regex_t *reg;
1231
1232         if(regs)
1233                 return;
1234         nrules = sizeof rules / sizeof rules[0];
1235         regs = emallocz(nrules * sizeof(Regs));
1236         for(i = 0; i < nrules; i++) {
1237                 if(rules[i].prop) {
1238                         reg = emallocz(sizeof(regex_t));
1239                         if(regcomp(reg, rules[i].prop, REG_EXTENDED))
1240                                 free(reg);
1241                         else
1242                                 regs[i].propregex = reg;
1243                 }
1244                 if(rules[i].tags) {
1245                         reg = emallocz(sizeof(regex_t));
1246                         if(regcomp(reg, rules[i].tags, REG_EXTENDED))
1247                                 free(reg);
1248                         else
1249                                 regs[i].tagregex = reg;
1250                 }
1251         }
1252 }
1253
1254 static void
1255 focusnext(const char *arg) {
1256         Client *c;
1257
1258         if(!sel)
1259                 return;
1260         for(c = sel->next; c && !isvisible(c); c = c->next);
1261         if(!c)
1262                 for(c = clients; c && !isvisible(c); c = c->next);
1263         if(c) {
1264                 focus(c);
1265                 restack();
1266         }
1267 }
1268
1269 static void
1270 focusprev(const char *arg) {
1271         Client *c;
1272
1273         if(!sel)
1274                 return;
1275         for(c = sel->prev; c && !isvisible(c); c = c->prev);
1276         if(!c) {
1277                 for(c = clients; c && c->next; c = c->next);
1278                 for(; c && !isvisible(c); c = c->prev);
1279         }
1280         if(c) {
1281                 focus(c);
1282                 restack();
1283         }
1284 }
1285
1286 static void
1287 initlayouts(void) {
1288         unsigned int i, w;
1289
1290         nlayouts = sizeof layouts / sizeof layouts[0];
1291         for(blw = i = 0; i < nlayouts; i++) {
1292                 w = textw(layouts[i].symbol);
1293                 if(w > blw)
1294                         blw = w;
1295         }
1296 }
1297
1298 static Bool
1299 isfloating(void) {
1300         return layouts[ltidx].arrange == floating;
1301 }
1302
1303 static Bool
1304 isvisible(Client *c) {
1305         unsigned int i;
1306
1307         for(i = 0; i < ntags; i++)
1308                 if(c->tags[i] && seltags[i])
1309                         return True;
1310         return False;
1311 }
1312
1313 static void
1314 restack(void) {
1315         Client *c;
1316         XEvent ev;
1317         XWindowChanges wc;
1318
1319         drawbar();
1320         if(!sel)
1321                 return;
1322         if(sel->isfloating || isfloating())
1323                 XRaiseWindow(dpy, sel->win);
1324         if(!isfloating()) {
1325                 wc.stack_mode = Below;
1326                 wc.sibling = barwin;
1327                 if(!sel->isfloating) {
1328                         XConfigureWindow(dpy, sel->win, CWSibling | CWStackMode, &wc);
1329                         wc.sibling = sel->win;
1330                 }
1331                 for(c = nexttiled(clients); c; c = nexttiled(c->next)) {
1332                         if(c == sel)
1333                                 continue;
1334                         XConfigureWindow(dpy, c->win, CWSibling | CWStackMode, &wc);
1335                         wc.sibling = c->win;
1336                 }
1337         }
1338         XSync(dpy, False);
1339         while(XCheckMaskEvent(dpy, EnterWindowMask, &ev));
1340 }
1341
1342 static void
1343 setlayout(const char *arg) {
1344         unsigned int i;
1345
1346         if(!arg) {
1347                 if(++ltidx == nlayouts)
1348                         ltidx = 0;;
1349         }
1350         else {
1351                 for(i = 0; i < nlayouts; i++)
1352                         if(!strcmp(arg, layouts[i].symbol))
1353                                 break;
1354                 if(i == nlayouts)
1355                         return;
1356                 ltidx = i;
1357         }
1358         if(sel)
1359                 arrange();
1360         else
1361                 drawbar();
1362 }
1363
1364 static void
1365 tag(const char *arg) {
1366         unsigned int i;
1367
1368         if(!sel)
1369                 return;
1370         for(i = 0; i < ntags; i++)
1371                 sel->tags[i] = arg == NULL;
1372         i = idxoftag(arg);
1373         if(i >= 0 && i < ntags)
1374                 sel->tags[i] = True;
1375         arrange();
1376 }
1377
1378 static void
1379 togglefloating(const char *arg) {
1380         if(!sel)
1381                 return;
1382         sel->isfloating = !sel->isfloating;
1383         if(sel->isfloating)
1384                 resize(sel, sel->x, sel->y, sel->w, sel->h, True);
1385         arrange();
1386 }
1387
1388 static void
1389 togglemax(const char *arg) {
1390         XEvent ev;
1391
1392         if(!sel || (!isfloating() && !sel->isfloating) || sel->isfixed)
1393                 return;
1394         if((sel->ismax = !sel->ismax)) {
1395                 sel->rx = sel->x;
1396                 sel->ry = sel->y;
1397                 sel->rw = sel->w;
1398                 sel->rh = sel->h;
1399                 resize(sel, wax, way, waw - 2 * sel->border, wah - 2 * sel->border, True);
1400         }
1401         else
1402                 resize(sel, sel->rx, sel->ry, sel->rw, sel->rh, True);
1403         drawbar();
1404         while(XCheckMaskEvent(dpy, EnterWindowMask, &ev));
1405 }
1406
1407 static void
1408 toggletag(const char *arg) {
1409         unsigned int i, j;
1410
1411         if(!sel)
1412                 return;
1413         i = idxoftag(arg);
1414         sel->tags[i] = !sel->tags[i];
1415         for(j = 0; j < ntags && !sel->tags[j]; j++);
1416         if(j == ntags)
1417                 sel->tags[i] = True;
1418         arrange();
1419 }
1420
1421 static void
1422 toggleview(const char *arg) {
1423         unsigned int i, j;
1424
1425         i = idxoftag(arg);
1426         seltags[i] = !seltags[i];
1427         for(j = 0; j < ntags && !seltags[j]; j++);
1428         if(j == ntags)
1429                 seltags[i] = True; /* cannot toggle last view */
1430         arrange();
1431 }
1432
1433 static void
1434 view(const char *arg) {
1435         unsigned int i;
1436
1437         for(i = 0; i < ntags; i++)
1438                 seltags[i] = arg == NULL;
1439         i = idxoftag(arg);
1440         if(i >= 0 && i < ntags)
1441                 seltags[i] = True;
1442         arrange();
1443 }
1444
1445 static void
1446 cleanup(void) {
1447         close(STDIN_FILENO);
1448         while(stack) {
1449                 unban(stack);
1450                 unmanage(stack);
1451         }
1452         if(dc.font.set)
1453                 XFreeFontSet(dpy, dc.font.set);
1454         else
1455                 XFreeFont(dpy, dc.font.xfont);
1456         XUngrabKey(dpy, AnyKey, AnyModifier, root);
1457         XFreePixmap(dpy, dc.drawable);
1458         XFreeGC(dpy, dc.gc);
1459         XDestroyWindow(dpy, barwin);
1460         XFreeCursor(dpy, cursor[CurNormal]);
1461         XFreeCursor(dpy, cursor[CurResize]);
1462         XFreeCursor(dpy, cursor[CurMove]);
1463         XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime);
1464         XSync(dpy, False);
1465         free(seltags);
1466 }
1467
1468 static long
1469 getstate(Window w) {
1470         int format, status;
1471         long result = -1;
1472         unsigned char *p = NULL;
1473         unsigned long n, extra;
1474         Atom real;
1475
1476         status = XGetWindowProperty(dpy, w, wmatom[WMState], 0L, 2L, False, wmatom[WMState],
1477                         &real, &format, &n, &extra, (unsigned char **)&p);
1478         if(status != Success)
1479                 return -1;
1480         if(n != 0)
1481                 result = *p;
1482         XFree(p);
1483         return result;
1484 }
1485
1486 static void
1487 scan(void) {
1488         unsigned int i, num;
1489         Window *wins, d1, d2;
1490         XWindowAttributes wa;
1491
1492         wins = NULL;
1493         if(XQueryTree(dpy, root, &d1, &d2, &wins, &num)) {
1494                 for(i = 0; i < num; i++) {
1495                         if(!XGetWindowAttributes(dpy, wins[i], &wa)
1496                         || wa.override_redirect || XGetTransientForHint(dpy, wins[i], &d1))
1497                                 continue;
1498                         if(wa.map_state == IsViewable || getstate(wins[i]) == IconicState)
1499                                 manage(wins[i], &wa);
1500                 }
1501                 for(i = 0; i < num; i++) { /* now the transients */
1502                         if(!XGetWindowAttributes(dpy, wins[i], &wa))
1503                                 continue;
1504                         if(XGetTransientForHint(dpy, wins[i], &d1)
1505                         && (wa.map_state == IsViewable || getstate(wins[i]) == IconicState))
1506                                 manage(wins[i], &wa);
1507                 }
1508         }
1509         if(wins)
1510                 XFree(wins);
1511 }
1512
1513 static void
1514 setup(void) {
1515         int i, j;
1516         unsigned int mask;
1517         Window w;
1518         XModifierKeymap *modmap;
1519         XSetWindowAttributes wa;
1520
1521         /* init atoms */
1522         wmatom[WMProtocols] = XInternAtom(dpy, "WM_PROTOCOLS", False);
1523         wmatom[WMDelete] = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
1524         wmatom[WMName] = XInternAtom(dpy, "WM_NAME", False);
1525         wmatom[WMState] = XInternAtom(dpy, "WM_STATE", False);
1526         netatom[NetSupported] = XInternAtom(dpy, "_NET_SUPPORTED", False);
1527         netatom[NetWMName] = XInternAtom(dpy, "_NET_WM_NAME", False);
1528         XChangeProperty(dpy, root, netatom[NetSupported], XA_ATOM, 32,
1529                         PropModeReplace, (unsigned char *) netatom, NetLast);
1530         /* init cursors */
1531         cursor[CurNormal] = XCreateFontCursor(dpy, XC_left_ptr);
1532         cursor[CurResize] = XCreateFontCursor(dpy, XC_sizing);
1533         cursor[CurMove] = XCreateFontCursor(dpy, XC_fleur);
1534         /* init modifier map */
1535         modmap = XGetModifierMapping(dpy);
1536         for (i = 0; i < 8; i++)
1537                 for (j = 0; j < modmap->max_keypermod; j++) {
1538                         if(modmap->modifiermap[i * modmap->max_keypermod + j]
1539                                         == XKeysymToKeycode(dpy, XK_Num_Lock))
1540                                 numlockmask = (1 << i);
1541                 }
1542         XFreeModifiermap(modmap);
1543         /* select for events */
1544         wa.event_mask = SubstructureRedirectMask | SubstructureNotifyMask
1545                 | EnterWindowMask | LeaveWindowMask | StructureNotifyMask;
1546         wa.cursor = cursor[CurNormal];
1547         XChangeWindowAttributes(dpy, root, CWEventMask | CWCursor, &wa);
1548         XSelectInput(dpy, root, wa.event_mask);
1549         grabkeys();
1550         compileregs();
1551         for(ntags = 0; tags[ntags]; ntags++);
1552         seltags = emallocz(sizeof(Bool) * ntags);
1553         seltags[0] = True;
1554         /* geometry */
1555         sx = sy = 0;
1556         sw = DisplayWidth(dpy, screen);
1557         sh = DisplayHeight(dpy, screen);
1558         initstyle();
1559         initlayouts();
1560         initbar();
1561         /* multihead support */
1562         selscreen = XQueryPointer(dpy, root, &w, &w, &i, &i, &i, &i, &mask);
1563 }
1564
1565 /*
1566  * Startup Error handler to check if another window manager
1567  * is already running.
1568  */
1569 static int
1570 xerrorstart(Display *dsply, XErrorEvent *ee) {
1571         otherwm = True;
1572         return -1;
1573 }
1574
1575 static Bool
1576 gettextprop(Window w, Atom atom, char *text, unsigned int size) {
1577         char **list = NULL;
1578         int n;
1579         XTextProperty name;
1580
1581         if(!text || size == 0)
1582                 return False;
1583         text[0] = '\0';
1584         XGetTextProperty(dpy, w, &name, atom);
1585         if(!name.nitems)
1586                 return False;
1587         if(name.encoding == XA_STRING)
1588                 strncpy(text, (char *)name.value, size - 1);
1589         else {
1590                 if(XmbTextPropertyToTextList(dpy, &name, &list, &n) >= Success
1591                 && n > 0 && *list)
1592                 {
1593                         strncpy(text, *list, size - 1);
1594                         XFreeStringList(list);
1595                 }
1596         }
1597         text[size - 1] = '\0';
1598         XFree(name.value);
1599         return True;
1600 }
1601
1602 static void
1603 quit(const char *arg) {
1604         readin = running = False;
1605 }
1606
1607 /* There's no way to check accesses to destroyed windows, thus those cases are
1608  * ignored (especially on UnmapNotify's).  Other types of errors call Xlibs
1609  * default error handler, which may call exit.
1610  */
1611 static int
1612 xerror(Display *dpy, XErrorEvent *ee) {
1613         if(ee->error_code == BadWindow
1614         || (ee->request_code == X_SetInputFocus && ee->error_code == BadMatch)
1615         || (ee->request_code == X_PolyText8 && ee->error_code == BadDrawable)
1616         || (ee->request_code == X_PolyFillRectangle && ee->error_code == BadDrawable)
1617         || (ee->request_code == X_PolySegment && ee->error_code == BadDrawable)
1618         || (ee->request_code == X_ConfigureWindow && ee->error_code == BadMatch)
1619         || (ee->request_code == X_GrabKey && ee->error_code == BadAccess)
1620         || (ee->request_code == X_CopyArea && ee->error_code == BadDrawable))
1621                 return 0;
1622         fprintf(stderr, "dwm: fatal error: request code=%d, error code=%d\n",
1623                 ee->request_code, ee->error_code);
1624         return xerrorxlib(dpy, ee); /* may call exit */
1625 }
1626
1627 static void
1628 arrange(void) {
1629         Client *c;
1630
1631         for(c = clients; c; c = c->next)
1632                 if(isvisible(c))
1633                         unban(c);
1634                 else
1635                         ban(c);
1636         layouts[ltidx].arrange();
1637         focus(NULL);
1638         restack();
1639 }
1640
1641 static void
1642 attach(Client *c) {
1643         if(clients)
1644                 clients->prev = c;
1645         c->next = clients;
1646         clients = c;
1647 }
1648
1649 static void
1650 detach(Client *c) {
1651         if(c->prev)
1652                 c->prev->next = c->next;
1653         if(c->next)
1654                 c->next->prev = c->prev;
1655         if(c == clients)
1656                 clients = c->next;
1657         c->next = c->prev = NULL;
1658 }
1659
1660 static void
1661 focus(Client *c) {
1662         if((!c && selscreen) || (c && !isvisible(c)))
1663                 for(c = stack; c && !isvisible(c); c = c->snext);
1664         if(sel && sel != c) {
1665                 grabbuttons(sel, False);
1666                 XSetWindowBorder(dpy, sel->win, dc.norm[ColBorder]);
1667         }
1668         if(c) {
1669                 detachstack(c);
1670                 attachstack(c);
1671                 grabbuttons(c, True);
1672         }
1673         sel = c;
1674         drawbar();
1675         if(!selscreen)
1676                 return;
1677         if(c) {
1678                 XSetWindowBorder(dpy, c->win, dc.sel[ColBorder]);
1679                 XSetInputFocus(dpy, c->win, RevertToPointerRoot, CurrentTime);
1680         }
1681         else
1682                 XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime);
1683 }
1684
1685 static Bool
1686 isarrange(void (*func)())
1687 {
1688         return func == layouts[ltidx].arrange;
1689 }
1690
1691 static Client *
1692 nexttiled(Client *c) {
1693         for(; c && (c->isfloating || !isvisible(c)); c = c->next);
1694         return c;
1695 }
1696
1697 static void
1698 setmwfact(const char *arg) {
1699         double delta;
1700
1701         if(!isarrange(tile))
1702                 return;
1703         /* arg handling, manipulate mwfact */
1704         if(arg == NULL)
1705                 mwfact = MWFACT;
1706         else if(1 == sscanf(arg, "%lf", &delta)) {
1707                 if(arg[0] != '+' && arg[0] != '-')
1708                         mwfact = delta;
1709                 else
1710                         mwfact += delta;
1711                 if(mwfact < 0.1)
1712                         mwfact = 0.1;
1713                 else if(mwfact > 0.9)
1714                         mwfact = 0.9;
1715         }
1716         arrange();
1717 }
1718
1719 static void
1720 tile(void) {
1721         unsigned int i, n, nx, ny, nw, nh, mw, th;
1722         Client *c;
1723
1724         for(n = 0, c = nexttiled(clients); c; c = nexttiled(c->next))
1725                 n++;
1726
1727         /* window geoms */
1728         mw = (n == 1) ? waw : mwfact * waw;
1729         th = (n > 1) ? wah / (n - 1) : 0;
1730         if(n > 1 && th < bh)
1731                 th = wah;
1732
1733         nx = wax;
1734         ny = way;
1735         for(i = 0, c = nexttiled(clients); c; c = nexttiled(c->next), i++) {
1736                 c->ismax = False;
1737                 if(i == 0) { /* master */
1738                         nw = mw - 2 * c->border;
1739                         nh = wah - 2 * c->border;
1740                 }
1741                 else {  /* tile window */
1742                         if(i == 1) {
1743                                 ny = way;
1744                                 nx += mw;
1745                         }
1746                         nw = waw - mw - 2 * c->border;
1747                         if(i + 1 == n) /* remainder */
1748                                 nh = (way + wah) - ny - 2 * c->border;
1749                         else
1750                                 nh = th - 2 * c->border;
1751                 }
1752                 resize(c, nx, ny, nw, nh, RESIZEHINTS);
1753                 if(n > 1 && th != wah)
1754                         ny += nh + 2 * c->border;
1755         }
1756 }
1757
1758 static void
1759 zoom(const char *arg) {
1760         Client *c;
1761
1762         if(!sel || !isarrange(tile) || sel->isfloating)
1763                 return;
1764         if((c = sel) == nexttiled(clients))
1765                 if(!(c = nexttiled(c->next)))
1766                         return;
1767         detach(c);
1768         attach(c);
1769         focus(c);
1770         arrange();
1771 }
1772
1773 int
1774 main(int argc, char *argv[]) {
1775         char *p;
1776         int r, xfd;
1777         fd_set rd;
1778         XEvent ev;
1779
1780         if(argc == 2 && !strcmp("-v", argv[1]))
1781                 eprint("dwm-"VERSION", © 2006-2007 A. R. Garbe, S. van Dijk, J. Salmi, P. Hruby, S. Nagy\n");
1782         else if(argc != 1)
1783                 eprint("usage: dwm [-v]\n");
1784         setlocale(LC_CTYPE, "");
1785         if(!(dpy = XOpenDisplay(0)))
1786                 eprint("dwm: cannot open display\n");
1787         xfd = ConnectionNumber(dpy);
1788         screen = DefaultScreen(dpy);
1789         root = RootWindow(dpy, screen);
1790         otherwm = False;
1791         XSetErrorHandler(xerrorstart);
1792         /* this causes an error if some other window manager is running */
1793         XSelectInput(dpy, root, SubstructureRedirectMask);
1794         XSync(dpy, False);
1795         if(otherwm)
1796                 eprint("dwm: another window manager is already running\n");
1797
1798         XSync(dpy, False);
1799         XSetErrorHandler(NULL);
1800         xerrorxlib = XSetErrorHandler(xerror);
1801         XSync(dpy, False);
1802         setup();
1803         drawbar();
1804         scan();
1805
1806         /* main event loop, also reads status text from stdin */
1807         XSync(dpy, False);
1808         readin = True;
1809         while(running) {
1810                 FD_ZERO(&rd);
1811                 if(readin)
1812                         FD_SET(STDIN_FILENO, &rd);
1813                 FD_SET(xfd, &rd);
1814                 if(select(xfd + 1, &rd, NULL, NULL, NULL) == -1) {
1815                         if(errno == EINTR)
1816                                 continue;
1817                         eprint("select failed\n");
1818                 }
1819                 if(FD_ISSET(STDIN_FILENO, &rd)) {
1820                         switch(r = read(STDIN_FILENO, stext, sizeof stext - 1)) {
1821                         case -1:
1822                                 strncpy(stext, strerror(errno), sizeof stext - 1);
1823                                 stext[sizeof stext - 1] = '\0';
1824                                 readin = False;
1825                                 break;
1826                         case 0:
1827                                 strncpy(stext, "EOF", 4);
1828                                 readin = False;
1829                                 break;
1830                         default:
1831                                 for(stext[r] = '\0', p = stext + strlen(stext) - 1; p >= stext && *p == '\n'; *p-- = '\0');
1832                                 for(; p >= stext && *p != '\n'; --p);
1833                                 if(p > stext)
1834                                         strncpy(stext, p + 1, sizeof stext);
1835                         }
1836                         drawbar();
1837                 }
1838                 while(XPending(dpy)) {
1839                         XNextEvent(dpy, &ev);
1840                         if(handler[ev.type])
1841                                 (handler[ev.type])(&ev); /* call handler */
1842                 }
1843         }
1844         cleanup();
1845         XCloseDisplay(dpy);
1846         return 0;
1847 }