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