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