JasonWoof Got questions, comments, patches, etc.? Contact Jason Woofenden
applied Eric Mertens patch to mainstream dwm, however this needs testing
[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                 /* set minimum possible */
1040                 if (w < 1)
1041                         w = 1;
1042                 if (h < 1)
1043                         h = 1;
1044
1045                 /* temporarily remove base dimensions */
1046                 w -= c->basew;
1047                 h -= c->baseh;
1048
1049                 /* adjust for aspect limits */
1050                 if (c->minay > 0 && c->maxay > 0 && c->minax > 0 && c->maxax > 0) {
1051                         if (w * c->maxay > h * c->maxax)
1052                                 w = h * c->maxax / c->maxay;
1053                         else if (w * c->minay < h * c->minax)
1054                                 h = w * c->minay / c->minax;
1055                 }
1056
1057                 /* adjust for increment value */
1058                 if(c->incw)
1059                         w -= w % c->incw;
1060                 if(c->inch)
1061                         h -= h % c->inch;
1062
1063                 /* restore base dimensions */
1064                 w += c->basew;
1065                 h += c->baseh;
1066
1067                 if(c->minw > 0 && w < c->minw)
1068                         w = c->minw;
1069                 if(c->minh > 0 && h < c->minh)
1070                         h = c->minh;
1071                 if(c->maxw > 0 && w > c->maxw)
1072                         w = c->maxw;
1073                 if(c->maxh > 0 && h > c->maxh)
1074                         h = c->maxh;
1075         }
1076         if(w <= 0 || h <= 0)
1077                 return;
1078         /* offscreen appearance fixes */
1079         if(x > sw)
1080                 x = sw - w - 2 * c->border;
1081         if(y > sh)
1082                 y = sh - h - 2 * c->border;
1083         if(x + w + 2 * c->border < sx)
1084                 x = sx;
1085         if(y + h + 2 * c->border < sy)
1086                 y = sy;
1087         if(c->x != x || c->y != y || c->w != w || c->h != h) {
1088                 c->x = wc.x = x;
1089                 c->y = wc.y = y;
1090                 c->w = wc.width = w;
1091                 c->h = wc.height = h;
1092                 wc.border_width = c->border;
1093                 XConfigureWindow(dpy, c->win, CWX | CWY | CWWidth | CWHeight | CWBorderWidth, &wc);
1094                 configure(c);
1095                 XSync(dpy, False);
1096         }
1097 }
1098
1099 void
1100 resizemouse(Client *c) {
1101         int ocx, ocy;
1102         int nw, nh;
1103         XEvent ev;
1104
1105         ocx = c->x;
1106         ocy = c->y;
1107         if(XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync,
1108                         None, cursor[CurResize], CurrentTime) != GrabSuccess)
1109                 return;
1110         c->ismax = False;
1111         XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->border - 1, c->h + c->border - 1);
1112         for(;;) {
1113                 XMaskEvent(dpy, MOUSEMASK | ExposureMask | SubstructureRedirectMask , &ev);
1114                 switch(ev.type) {
1115                 case ButtonRelease:
1116                         XWarpPointer(dpy, None, c->win, 0, 0, 0, 0,
1117                                         c->w + c->border - 1, c->h + c->border - 1);
1118                         XUngrabPointer(dpy, CurrentTime);
1119                         while(XCheckMaskEvent(dpy, EnterWindowMask, &ev));
1120                         return;
1121                 case ConfigureRequest:
1122                 case Expose:
1123                 case MapRequest:
1124                         handler[ev.type](&ev);
1125                         break;
1126                 case MotionNotify:
1127                         XSync(dpy, False);
1128                         if((nw = ev.xmotion.x - ocx - 2 * c->border + 1) <= 0)
1129                                 nw = 1;
1130                         if((nh = ev.xmotion.y - ocy - 2 * c->border + 1) <= 0)
1131                                 nh = 1;
1132                         resize(c, c->x, c->y, nw, nh, True);
1133                         break;
1134                 }
1135         }
1136 }
1137
1138 void
1139 restack(void) {
1140         Client *c;
1141         XEvent ev;
1142         XWindowChanges wc;
1143
1144         drawbar();
1145         if(!sel)
1146                 return;
1147         if(sel->isfloating || isarrange(floating))
1148                 XRaiseWindow(dpy, sel->win);
1149         if(!isarrange(floating)) {
1150                 wc.stack_mode = Below;
1151                 wc.sibling = barwin;
1152                 if(!sel->isfloating) {
1153                         XConfigureWindow(dpy, sel->win, CWSibling | CWStackMode, &wc);
1154                         wc.sibling = sel->win;
1155                 }
1156                 for(c = nexttiled(clients); c; c = nexttiled(c->next)) {
1157                         if(c == sel)
1158                                 continue;
1159                         XConfigureWindow(dpy, c->win, CWSibling | CWStackMode, &wc);
1160                         wc.sibling = c->win;
1161                 }
1162         }
1163         XSync(dpy, False);
1164         while(XCheckMaskEvent(dpy, EnterWindowMask, &ev));
1165 }
1166
1167 void
1168 run(void) {
1169         char *p;
1170         int r, xfd;
1171         fd_set rd;
1172         XEvent ev;
1173
1174         /* main event loop, also reads status text from stdin */
1175         XSync(dpy, False);
1176         xfd = ConnectionNumber(dpy);
1177         readin = True;
1178         while(running) {
1179                 FD_ZERO(&rd);
1180                 if(readin)
1181                         FD_SET(STDIN_FILENO, &rd);
1182                 FD_SET(xfd, &rd);
1183                 if(select(xfd + 1, &rd, NULL, NULL, NULL) == -1) {
1184                         if(errno == EINTR)
1185                                 continue;
1186                         eprint("select failed\n");
1187                 }
1188                 if(FD_ISSET(STDIN_FILENO, &rd)) {
1189                         switch(r = read(STDIN_FILENO, stext, sizeof stext - 1)) {
1190                         case -1:
1191                                 strncpy(stext, strerror(errno), sizeof stext - 1);
1192                                 stext[sizeof stext - 1] = '\0';
1193                                 readin = False;
1194                                 break;
1195                         case 0:
1196                                 strncpy(stext, "EOF", 4);
1197                                 readin = False;
1198                                 break;
1199                         default:
1200                                 for(stext[r] = '\0', p = stext + strlen(stext) - 1; p >= stext && *p == '\n'; *p-- = '\0');
1201                                 for(; p >= stext && *p != '\n'; --p);
1202                                 if(p > stext)
1203                                         strncpy(stext, p + 1, sizeof stext);
1204                         }
1205                         drawbar();
1206                 }
1207                 while(XPending(dpy)) {
1208                         XNextEvent(dpy, &ev);
1209                         if(handler[ev.type])
1210                                 (handler[ev.type])(&ev); /* call handler */
1211                 }
1212         }
1213 }
1214
1215 void
1216 scan(void) {
1217         unsigned int i, num;
1218         Window *wins, d1, d2;
1219         XWindowAttributes wa;
1220
1221         wins = NULL;
1222         if(XQueryTree(dpy, root, &d1, &d2, &wins, &num)) {
1223                 for(i = 0; i < num; i++) {
1224                         if(!XGetWindowAttributes(dpy, wins[i], &wa)
1225                         || wa.override_redirect || XGetTransientForHint(dpy, wins[i], &d1))
1226                                 continue;
1227                         if(wa.map_state == IsViewable || getstate(wins[i]) == IconicState)
1228                                 manage(wins[i], &wa);
1229                 }
1230                 for(i = 0; i < num; i++) { /* now the transients */
1231                         if(!XGetWindowAttributes(dpy, wins[i], &wa))
1232                                 continue;
1233                         if(XGetTransientForHint(dpy, wins[i], &d1)
1234                         && (wa.map_state == IsViewable || getstate(wins[i]) == IconicState))
1235                                 manage(wins[i], &wa);
1236                 }
1237         }
1238         if(wins)
1239                 XFree(wins);
1240 }
1241
1242 void
1243 setclientstate(Client *c, long state) {
1244         long data[] = {state, None};
1245
1246         XChangeProperty(dpy, c->win, wmatom[WMState], wmatom[WMState], 32,
1247                         PropModeReplace, (unsigned char *)data, 2);
1248 }
1249
1250 void
1251 setlayout(const char *arg) {
1252         unsigned int i;
1253
1254         if(!arg) {
1255                 if(++ltidx == nlayouts)
1256                         ltidx = 0;;
1257         }
1258         else {
1259                 for(i = 0; i < nlayouts; i++)
1260                         if(!strcmp(arg, layouts[i].symbol))
1261                                 break;
1262                 if(i == nlayouts)
1263                         return;
1264                 ltidx = i;
1265         }
1266         if(sel)
1267                 arrange();
1268         else
1269                 drawbar();
1270 }
1271
1272 void
1273 setmwfact(const char *arg) {
1274         double delta;
1275
1276         if(!ISTILE)
1277                 return;
1278         /* arg handling, manipulate mwfact */
1279         if(arg == NULL)
1280                 mwfact = MWFACT;
1281         else if(1 == sscanf(arg, "%lf", &delta)) {
1282                 if(arg[0] == '+' || arg[0] == '-')
1283                         mwfact += delta;
1284                 else
1285                         mwfact = delta;
1286                 if(mwfact < 0.1)
1287                         mwfact = 0.1;
1288                 else if(mwfact > 0.9)
1289                         mwfact = 0.9;
1290         }
1291         arrange();
1292 }
1293
1294 void
1295 setup(void) {
1296         int d;
1297         unsigned int i, j, mask;
1298         Window w;
1299         XModifierKeymap *modmap;
1300         XSetWindowAttributes wa;
1301
1302         /* init atoms */
1303         wmatom[WMProtocols] = XInternAtom(dpy, "WM_PROTOCOLS", False);
1304         wmatom[WMDelete] = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
1305         wmatom[WMName] = XInternAtom(dpy, "WM_NAME", False);
1306         wmatom[WMState] = XInternAtom(dpy, "WM_STATE", False);
1307         netatom[NetSupported] = XInternAtom(dpy, "_NET_SUPPORTED", False);
1308         netatom[NetWMName] = XInternAtom(dpy, "_NET_WM_NAME", False);
1309         XChangeProperty(dpy, root, netatom[NetSupported], XA_ATOM, 32,
1310                         PropModeReplace, (unsigned char *) netatom, NetLast);
1311
1312         /* init cursors */
1313         cursor[CurNormal] = XCreateFontCursor(dpy, XC_left_ptr);
1314         cursor[CurResize] = XCreateFontCursor(dpy, XC_sizing);
1315         cursor[CurMove] = XCreateFontCursor(dpy, XC_fleur);
1316
1317         /* init geometry */
1318         sx = sy = 0;
1319         sw = DisplayWidth(dpy, screen);
1320         sh = DisplayHeight(dpy, screen);
1321
1322         /* init modifier map */
1323         modmap = XGetModifierMapping(dpy);
1324         for(i = 0; i < 8; i++)
1325                 for(j = 0; j < modmap->max_keypermod; j++) {
1326                         if(modmap->modifiermap[i * modmap->max_keypermod + j]
1327                         == XKeysymToKeycode(dpy, XK_Num_Lock))
1328                                 numlockmask = (1 << i);
1329                 }
1330         XFreeModifiermap(modmap);
1331
1332         /* select for events */
1333         wa.event_mask = SubstructureRedirectMask | SubstructureNotifyMask
1334                 | EnterWindowMask | LeaveWindowMask | StructureNotifyMask;
1335         wa.cursor = cursor[CurNormal];
1336         XChangeWindowAttributes(dpy, root, CWEventMask | CWCursor, &wa);
1337         XSelectInput(dpy, root, wa.event_mask);
1338
1339         /* grab keys */
1340         keypress(NULL);
1341
1342         /* init tags */
1343         compileregs();
1344
1345         /* init appearance */
1346         dc.norm[ColBorder] = getcolor(NORMBORDERCOLOR);
1347         dc.norm[ColBG] = getcolor(NORMBGCOLOR);
1348         dc.norm[ColFG] = getcolor(NORMFGCOLOR);
1349         dc.sel[ColBorder] = getcolor(SELBORDERCOLOR);
1350         dc.sel[ColBG] = getcolor(SELBGCOLOR);
1351         dc.sel[ColFG] = getcolor(SELFGCOLOR);
1352         initfont(FONT);
1353         dc.h = bh = dc.font.height + 2;
1354
1355         /* init layouts */
1356         mwfact = MWFACT;
1357         nlayouts = sizeof layouts / sizeof layouts[0];
1358         for(blw = i = 0; i < nlayouts; i++) {
1359                 j = textw(layouts[i].symbol);
1360                 if(j > blw)
1361                         blw = j;
1362         }
1363
1364         /* init bar */
1365         bpos = BARPOS;
1366         wa.override_redirect = 1;
1367         wa.background_pixmap = ParentRelative;
1368         wa.event_mask = ButtonPressMask | ExposureMask;
1369         barwin = XCreateWindow(dpy, root, sx, sy, sw, bh, 0,
1370                         DefaultDepth(dpy, screen), CopyFromParent, DefaultVisual(dpy, screen),
1371                         CWOverrideRedirect | CWBackPixmap | CWEventMask, &wa);
1372         XDefineCursor(dpy, barwin, cursor[CurNormal]);
1373         updatebarpos();
1374         XMapRaised(dpy, barwin);
1375         strcpy(stext, "dwm-"VERSION);
1376         dc.drawable = XCreatePixmap(dpy, root, sw, bh, DefaultDepth(dpy, screen));
1377         dc.gc = XCreateGC(dpy, root, 0, 0);
1378         XSetLineAttributes(dpy, dc.gc, 1, LineSolid, CapButt, JoinMiter);
1379         if(!dc.font.set)
1380                 XSetFont(dpy, dc.gc, dc.font.xfont->fid);
1381
1382         /* multihead support */
1383         selscreen = XQueryPointer(dpy, root, &w, &w, &d, &d, &d, &d, &mask);
1384 }
1385
1386 void
1387 spawn(const char *arg) {
1388         static char *shell = NULL;
1389
1390         if(!shell && !(shell = getenv("SHELL")))
1391                 shell = "/bin/sh";
1392         if(!arg)
1393                 return;
1394         /* The double-fork construct avoids zombie processes and keeps the code
1395          * clean from stupid signal handlers. */
1396         if(fork() == 0) {
1397                 if(fork() == 0) {
1398                         if(dpy)
1399                                 close(ConnectionNumber(dpy));
1400                         setsid();
1401                         execl(shell, shell, "-c", arg, (char *)NULL);
1402                         fprintf(stderr, "dwm: execl '%s -c %s'", shell, arg);
1403                         perror(" failed");
1404                 }
1405                 exit(0);
1406         }
1407         wait(0);
1408 }
1409
1410 void
1411 tag(const char *arg) {
1412         unsigned int i;
1413
1414         if(!sel)
1415                 return;
1416         for(i = 0; i < ntags; i++)
1417                 sel->tags[i] = arg == NULL;
1418         i = idxoftag(arg);
1419         if(i >= 0 && i < ntags)
1420                 sel->tags[i] = True;
1421         arrange();
1422 }
1423
1424 unsigned int
1425 textnw(const char *text, unsigned int len) {
1426         XRectangle r;
1427
1428         if(dc.font.set) {
1429                 XmbTextExtents(dc.font.set, text, len, NULL, &r);
1430                 return r.width;
1431         }
1432         return XTextWidth(dc.font.xfont, text, len);
1433 }
1434
1435 unsigned int
1436 textw(const char *text) {
1437         return textnw(text, strlen(text)) + dc.font.height;
1438 }
1439
1440 void
1441 tile(void) {
1442         unsigned int i, n, nx, ny, nw, nh, mw, th;
1443         Client *c, *mc;
1444
1445         for(n = 0, c = nexttiled(clients); c; c = nexttiled(c->next))
1446                 n++;
1447
1448         /* window geoms */
1449         mw = (n == 1) ? waw : mwfact * waw;
1450         th = (n > 1) ? wah / (n - 1) : 0;
1451         if(n > 1 && th < bh)
1452                 th = wah;
1453
1454         nx = wax;
1455         ny = way;
1456         nw = 0; /* gcc stupidity requires this */
1457         for(i = 0, c = mc = nexttiled(clients); c; c = nexttiled(c->next), i++) {
1458                 c->ismax = False;
1459                 if(i == 0) { /* master */
1460                         nw = mw - 2 * c->border;
1461                         nh = wah - 2 * c->border;
1462                 }
1463                 else {  /* tile window */
1464                         if(i == 1) {
1465                                 ny = way;
1466                                 nx += mc->w + 2 * mc->border;
1467                                 nw = waw - nx - 2 * c->border;
1468                         }
1469                         if(i + 1 == n) /* remainder */
1470                                 nh = (way + wah) - ny - 2 * c->border;
1471                         else
1472                                 nh = th - 2 * c->border;
1473                 }
1474                 resize(c, nx, ny, nw, nh, RESIZEHINTS);
1475                 if(n > 1 && th != wah)
1476                         ny = c->y + c->h + 2 * c->border;
1477         }
1478 }
1479
1480 void
1481 togglebar(const char *arg) {
1482         if(bpos == BarOff)
1483                 bpos = (BARPOS == BarOff) ? BarTop : BARPOS;
1484         else
1485                 bpos = BarOff;
1486         updatebarpos();
1487         arrange();
1488 }
1489
1490 void
1491 togglefloating(const char *arg) {
1492         if(!sel)
1493                 return;
1494         sel->isfloating = !sel->isfloating;
1495         if(sel->isfloating)
1496                 resize(sel, sel->x, sel->y, sel->w, sel->h, True);
1497         arrange();
1498 }
1499
1500 void
1501 togglemax(const char *arg) {
1502         XEvent ev;
1503
1504         if(!sel || sel->isfixed)
1505                 return;
1506         if((sel->ismax = !sel->ismax)) {
1507                 if(isarrange(floating) || sel->isfloating)
1508                         sel->wasfloating = True;
1509                 else {
1510                         togglefloating(NULL);
1511                         sel->wasfloating = False;
1512                 }
1513                 sel->rx = sel->x;
1514                 sel->ry = sel->y;
1515                 sel->rw = sel->w;
1516                 sel->rh = sel->h;
1517                 resize(sel, wax, way, waw - 2 * sel->border, wah - 2 * sel->border, True);
1518         }
1519         else {
1520                 resize(sel, sel->rx, sel->ry, sel->rw, sel->rh, True);
1521                 if(!sel->wasfloating)
1522                         togglefloating(NULL);
1523         }
1524         drawbar();
1525         while(XCheckMaskEvent(dpy, EnterWindowMask, &ev));
1526 }
1527
1528 void
1529 toggletag(const char *arg) {
1530         unsigned int i, j;
1531
1532         if(!sel)
1533                 return;
1534         i = idxoftag(arg);
1535         sel->tags[i] = !sel->tags[i];
1536         for(j = 0; j < ntags && !sel->tags[j]; j++);
1537         if(j == ntags)
1538                 sel->tags[i] = True;
1539         arrange();
1540 }
1541
1542 void
1543 toggleview(const char *arg) {
1544         unsigned int i, j;
1545
1546         i = idxoftag(arg);
1547         seltags[i] = !seltags[i];
1548         for(j = 0; j < ntags && !seltags[j]; j++);
1549         if(j == ntags)
1550                 seltags[i] = True; /* at least one tag must be viewed */
1551         arrange();
1552 }
1553
1554 void
1555 unban(Client *c) {
1556         if(!c->isbanned)
1557                 return;
1558         XMoveWindow(dpy, c->win, c->x, c->y);
1559         c->isbanned = False;
1560 }
1561
1562 void
1563 unmanage(Client *c) {
1564         XWindowChanges wc;
1565
1566         wc.border_width = c->oldborder;
1567         /* The server grab construct avoids race conditions. */
1568         XGrabServer(dpy);
1569         XSetErrorHandler(xerrordummy);
1570         XConfigureWindow(dpy, c->win, CWBorderWidth, &wc); /* restore border */
1571         detach(c);
1572         detachstack(c);
1573         if(sel == c)
1574                 focus(NULL);
1575         XUngrabButton(dpy, AnyButton, AnyModifier, c->win);
1576         setclientstate(c, WithdrawnState);
1577         free(c->tags);
1578         free(c);
1579         XSync(dpy, False);
1580         XSetErrorHandler(xerror);
1581         XUngrabServer(dpy);
1582         arrange();
1583 }
1584
1585 void
1586 unmapnotify(XEvent *e) {
1587         Client *c;
1588         XUnmapEvent *ev = &e->xunmap;
1589
1590         if((c = getclient(ev->window)))
1591                 unmanage(c);
1592 }
1593
1594 void
1595 updatebarpos(void) {
1596         XEvent ev;
1597
1598         wax = sx;
1599         way = sy;
1600         wah = sh;
1601         waw = sw;
1602         switch(bpos) {
1603         default:
1604                 wah -= bh;
1605                 way += bh;
1606                 XMoveWindow(dpy, barwin, sx, sy);
1607                 break;
1608         case BarBot:
1609                 wah -= bh;
1610                 XMoveWindow(dpy, barwin, sx, sy + wah);
1611                 break;
1612         case BarOff:
1613                 XMoveWindow(dpy, barwin, sx, sy - bh);
1614                 break;
1615         }
1616         XSync(dpy, False);
1617         while(XCheckMaskEvent(dpy, EnterWindowMask, &ev));
1618 }
1619
1620 void
1621 updatesizehints(Client *c) {
1622         long msize;
1623         XSizeHints size;
1624
1625         if(!XGetWMNormalHints(dpy, c->win, &size, &msize) || !size.flags)
1626                 size.flags = PSize;
1627         c->flags = size.flags;
1628         if(c->flags & PBaseSize) {
1629                 c->basew = size.base_width;
1630                 c->baseh = size.base_height;
1631         }
1632         else if(c->flags & PMinSize) {
1633                 c->basew = size.min_width;
1634                 c->baseh = size.min_height;
1635         }
1636         else
1637                 c->basew = c->baseh = 0;
1638         if(c->flags & PResizeInc) {
1639                 c->incw = size.width_inc;
1640                 c->inch = size.height_inc;
1641         }
1642         else
1643                 c->incw = c->inch = 0;
1644         if(c->flags & PMaxSize) {
1645                 c->maxw = size.max_width;
1646                 c->maxh = size.max_height;
1647         }
1648         else
1649                 c->maxw = c->maxh = 0;
1650         if(c->flags & PMinSize) {
1651                 c->minw = size.min_width;
1652                 c->minh = size.min_height;
1653         }
1654         else if(c->flags & PBaseSize) {
1655                 c->minw = size.base_width;
1656                 c->minh = size.base_height;
1657         }
1658         else
1659                 c->minw = c->minh = 0;
1660         if(c->flags & PAspect) {
1661                 c->minax = size.min_aspect.x;
1662                 c->maxax = size.max_aspect.x;
1663                 c->minay = size.min_aspect.y;
1664                 c->maxay = size.max_aspect.y;
1665         }
1666         else
1667                 c->minax = c->maxax = c->minay = c->maxay = 0;
1668         c->isfixed = (c->maxw && c->minw && c->maxh && c->minh
1669                         && c->maxw == c->minw && c->maxh == c->minh);
1670 }
1671
1672 void
1673 updatetitle(Client *c) {
1674         if(!gettextprop(c->win, netatom[NetWMName], c->name, sizeof c->name))
1675                 gettextprop(c->win, wmatom[WMName], c->name, sizeof c->name);
1676 }
1677
1678 /* There's no way to check accesses to destroyed windows, thus those cases are
1679  * ignored (especially on UnmapNotify's).  Other types of errors call Xlibs
1680  * default error handler, which may call exit.  */
1681 int
1682 xerror(Display *dpy, XErrorEvent *ee) {
1683         if(ee->error_code == BadWindow
1684         || (ee->request_code == X_SetInputFocus && ee->error_code == BadMatch)
1685         || (ee->request_code == X_PolyText8 && ee->error_code == BadDrawable)
1686         || (ee->request_code == X_PolyFillRectangle && ee->error_code == BadDrawable)
1687         || (ee->request_code == X_PolySegment && ee->error_code == BadDrawable)
1688         || (ee->request_code == X_ConfigureWindow && ee->error_code == BadMatch)
1689         || (ee->request_code == X_GrabKey && ee->error_code == BadAccess)
1690         || (ee->request_code == X_CopyArea && ee->error_code == BadDrawable))
1691                 return 0;
1692         fprintf(stderr, "dwm: fatal error: request code=%d, error code=%d\n",
1693                 ee->request_code, ee->error_code);
1694         return xerrorxlib(dpy, ee); /* may call exit */
1695 }
1696
1697 int
1698 xerrordummy(Display *dsply, XErrorEvent *ee) {
1699         return 0;
1700 }
1701
1702 /* Startup Error handler to check if another window manager
1703  * is already running. */
1704 int
1705 xerrorstart(Display *dsply, XErrorEvent *ee) {
1706         otherwm = True;
1707         return -1;
1708 }
1709
1710 void
1711 view(const char *arg) {
1712         unsigned int i;
1713
1714         memcpy(prevtags, seltags, sizeof seltags);
1715         for(i = 0; i < ntags; i++)
1716                 seltags[i] = arg == NULL;
1717         i = idxoftag(arg);
1718         if(i >= 0 && i < ntags)
1719                 seltags[i] = True;
1720         arrange();
1721 }
1722
1723 void
1724 viewprevtag(const char *arg) {
1725         static Bool tmptags[sizeof tags / sizeof tags[0]];
1726
1727         memcpy(tmptags, seltags, sizeof seltags);
1728         memcpy(seltags, prevtags, sizeof seltags);
1729         memcpy(prevtags, tmptags, sizeof seltags);
1730         arrange();
1731 }
1732
1733 void
1734 zoom(const char *arg) {
1735         Client *c;
1736
1737         if(!sel || !ISTILE || sel->isfloating)
1738                 return;
1739         if((c = sel) == nexttiled(clients))
1740                 if(!(c = nexttiled(c->next)))
1741                         return;
1742         detach(c);
1743         attach(c);
1744         focus(c);
1745         arrange();
1746 }
1747
1748 int
1749 main(int argc, char *argv[]) {
1750         if(argc == 2 && !strcmp("-v", argv[1]))
1751                 eprint("dwm-"VERSION", © 2006-2007 A. R. Garbe, S. van Dijk, J. Salmi, P. Hruby, S. Nagy\n");
1752         else if(argc != 1)
1753                 eprint("usage: dwm [-v]\n");
1754
1755         setlocale(LC_CTYPE, "");
1756         if(!(dpy = XOpenDisplay(0)))
1757                 eprint("dwm: cannot open display\n");
1758         screen = DefaultScreen(dpy);
1759         root = RootWindow(dpy, screen);
1760
1761         checkotherwm();
1762         setup();
1763         drawbar();
1764         scan();
1765         run();
1766         cleanup();
1767
1768         XCloseDisplay(dpy);
1769         return 0;
1770 }