JasonWoof Got questions, comments, patches, etc.? Contact Jason Woofenden
d7fa44ea6f60db5cfe96f35bae64929e5e66c1fa
[spectrwm.git] / scrotwm.c
1 /* $scrotwm$ */
2 /*
3  * Copyright (c) 2009 Marco Peereboom <marco@peereboom.us>
4  * Copyright (c) 2009 Ryan McBride <mcbride@countersiege.com>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 /*
19  * Much code and ideas taken from dwm under the following license:
20  * MIT/X Consortium License
21  * 
22  * 2006-2008 Anselm R Garbe <garbeam at gmail dot com>
23  * 2006-2007 Sander van Dijk <a dot h dot vandijk at gmail dot com>
24  * 2006-2007 Jukka Salmi <jukka at salmi dot ch>
25  * 2007 Premysl Hruby <dfenze at gmail dot com>
26  * 2007 Szabolcs Nagy <nszabolcs at gmail dot com>
27  * 2007 Christof Musik <christof at sendfax dot de>
28  * 2007-2008 Enno Gottox Boland <gottox at s01 dot de>
29  * 2007-2008 Peter Hartlich <sgkkr at hartlich dot com>
30  * 2008 Martin Hurton <martin dot hurton at gmail dot com>
31  * 
32  * Permission is hereby granted, free of charge, to any person obtaining a
33  * copy of this software and associated documentation files (the "Software"),
34  * to deal in the Software without restriction, including without limitation
35  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
36  * and/or sell copies of the Software, and to permit persons to whom the
37  * Software is furnished to do so, subject to the following conditions:
38  * 
39  * The above copyright notice and this permission notice shall be included in
40  * all copies or substantial portions of the Software.
41  * 
42  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
43  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
44  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
45  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
46  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
47  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
48  * DEALINGS IN THE SOFTWARE.
49  */
50
51 static const char       *cvstag = "$scrotwm$";
52
53 #define SWM_VERSION     "0.7"
54
55 #include <stdio.h>
56 #include <stdlib.h>
57 #include <err.h>
58 #include <errno.h>
59 #include <fcntl.h>
60 #include <locale.h>
61 #include <unistd.h>
62 #include <time.h>
63 #include <signal.h>
64 #include <string.h>
65 #include <util.h>
66 #include <pwd.h>
67 #include <ctype.h>
68
69 #include <sys/types.h>
70 #include <sys/time.h>
71 #include <sys/stat.h>
72 #include <sys/wait.h>
73 #include <sys/queue.h>
74 #include <sys/param.h>
75 #include <sys/select.h>
76
77 #include <X11/cursorfont.h>
78 #include <X11/keysym.h>
79 #include <X11/Xatom.h>
80 #include <X11/Xlib.h>
81 #include <X11/Xproto.h>
82 #include <X11/Xutil.h>
83 #include <X11/extensions/Xrandr.h>
84
85 #if RANDR_MAJOR < 1
86 #  error XRandR versions less than 1.0 are not supported
87 #endif
88
89 #if RANDR_MAJOR >= 1
90 #if RANDR_MINOR >= 2
91 #define SWM_XRR_HAS_CRTC
92 #endif
93 #endif
94
95 /* #define SWM_DEBUG */
96 #ifdef SWM_DEBUG
97 #define DPRINTF(x...)           do { if (swm_debug) fprintf(stderr, x); } while(0)
98 #define DNPRINTF(n,x...)        do { if (swm_debug & n) fprintf(stderr, x); } while(0)
99 #define SWM_D_MISC              0x0001
100 #define SWM_D_EVENT             0x0002
101 #define SWM_D_WS                0x0004
102 #define SWM_D_FOCUS             0x0008
103 #define SWM_D_MOVE              0x0010
104 #define SWM_D_STACK             0x0020
105 #define SWM_D_MOUSE             0x0040
106 #define SWM_D_PROP              0x0080
107 #define SWM_D_CLASS             0x0100
108
109 u_int32_t               swm_debug = 0
110                             | SWM_D_MISC
111                             | SWM_D_EVENT
112                             | SWM_D_WS
113                             | SWM_D_FOCUS
114                             | SWM_D_MOVE
115                             | SWM_D_STACK
116                             | SWM_D_MOUSE
117                             | SWM_D_PROP
118                             | SWM_D_CLASS
119                             ;
120 #else
121 #define DPRINTF(x...)
122 #define DNPRINTF(n,x...)
123 #endif
124
125 #define LENGTH(x)               (sizeof x / sizeof x[0])
126 #define MODKEY                  Mod1Mask
127 #define CLEANMASK(mask)         (mask & ~(numlockmask | LockMask))
128 #define BUTTONMASK              (ButtonPressMask|ButtonReleaseMask)
129 #define MOUSEMASK               (BUTTONMASK|PointerMotionMask)
130 #define SWM_PROPLEN             (16)
131 #define X(r)                    (r)->g.x        
132 #define Y(r)                    (r)->g.y
133 #define WIDTH(r)                (r)->g.w        
134 #define HEIGHT(r)               (r)->g.h
135
136 #ifndef SWM_LIB
137 #define SWM_LIB                 "/usr/X11R6/lib/swmhack.so"
138 #endif
139
140 char                    **start_argv;
141 Atom                    astate;
142 int                     (*xerrorxlib)(Display *, XErrorEvent *);
143 int                     other_wm;
144 int                     running = 1;
145 int                     ss_enabled = 0;
146 int                     xrandr_eventbase;
147 int                     ignore_enter = 0;
148 unsigned int            numlockmask = 0;
149 Display                 *display;
150
151 int                     cycle_empty = 0;
152 int                     cycle_visible = 0;
153
154 /* dialog windows */
155 double                  dialog_ratio = .6;
156 /* status bar */
157 #define SWM_BAR_MAX     (128)
158 char                    *bar_argv[] = { NULL, NULL };
159 int                     bar_pipe[2];
160 char                    bar_ext[SWM_BAR_MAX];
161 char                    bar_vertext[SWM_BAR_MAX];
162 int                     bar_version = 0;
163 sig_atomic_t            bar_alarm = 0;
164 int                     bar_delay = 30;
165 int                     bar_enabled = 1;
166 int                     bar_extra = 1;
167 int                     bar_extra_running = 0;
168 int                     bar_verbose = 1;
169 int                     bar_height = 0;
170 pid_t                   bar_pid;
171 GC                      bar_gc;
172 XGCValues               bar_gcv;
173 int                     bar_fidx = 0;
174 XFontStruct             *bar_fs;
175 char                    *bar_fonts[] = {
176                             "-*-terminus-*-*-*-*-*-*-*-*-*-*-*-*",
177                             "-*-times-medium-r-*-*-*-*-*-*-*-*-*-*",
178                             NULL
179 };
180
181 /* terminal + args */
182 char                    *spawn_term[] = { "xterm", NULL };
183 char                    *spawn_screenshot[] = { "screenshot.sh", NULL, NULL };
184 char                    *spawn_lock[] = { "xlock", NULL };
185 char                    *spawn_menu[] = { "dmenu_run", "-fn", NULL, "-nb", NULL,
186                             "-nf", NULL, "-sb", NULL, "-sf", NULL, NULL };
187
188 #define SWM_MENU_FN     (2)
189 #define SWM_MENU_NB     (4)
190 #define SWM_MENU_NF     (6)
191 #define SWM_MENU_SB     (8)
192 #define SWM_MENU_SF     (10)
193
194 /* layout manager data */
195 struct swm_geometry {
196         int                     x;
197         int                     y;
198         int                     w;
199         int                     h;
200 };
201
202 struct swm_screen;
203 struct workspace;
204
205 /* virtual "screens" */
206 struct swm_region {
207         TAILQ_ENTRY(swm_region) entry;
208         struct swm_geometry     g;
209         Window                  bar_window;
210         struct workspace        *ws;    /* current workspace on this region */
211         struct swm_screen       *s;     /* screen idx */
212 }; 
213 TAILQ_HEAD(swm_region_list, swm_region);
214
215 struct ws_win {
216         TAILQ_ENTRY(ws_win)     entry;
217         Window                  id;
218         struct swm_geometry     g;
219         int                     got_focus;
220         int                     floating;
221         int                     transient;
222         int                     manual;
223         struct workspace        *ws;    /* always valid */
224         struct swm_screen       *s;     /* always valid, never changes */
225         XWindowAttributes       wa;
226         XSizeHints              sh;
227         XClassHint              ch;
228 };
229 TAILQ_HEAD(ws_win_list, ws_win);
230
231 /* layout handlers */
232 void    stack(void);
233 void    vertical_config(struct workspace *, int);
234 void    vertical_stack(struct workspace *, struct swm_geometry *);
235 void    horizontal_config(struct workspace *, int);
236 void    horizontal_stack(struct workspace *, struct swm_geometry *);
237 void    max_stack(struct workspace *, struct swm_geometry *);
238
239 void    grabbuttons(struct ws_win *, int);
240
241 struct layout {
242         void            (*l_stack)(struct workspace *, struct swm_geometry *);
243         void            (*l_config)(struct workspace *, int);
244 } layouts[] =  {
245         /* stack,               configure */
246         { vertical_stack,       vertical_config},
247         { horizontal_stack,     horizontal_config},
248         { max_stack,            NULL},
249         { NULL,                 NULL},
250 };
251
252 #define SWM_H_SLICE             (32)
253 #define SWM_V_SLICE             (32)
254
255 /* define work spaces */
256 struct workspace {
257         int                     idx;            /* workspace index */
258         int                     restack;        /* restack on switch */
259         struct layout           *cur_layout;    /* current layout handlers */
260         struct ws_win           *focus;         /* may be NULL */
261         struct swm_region       *r;             /* may be NULL */
262         struct ws_win_list      winlist;        /* list of windows in ws */
263
264         /* stacker state */
265         struct {
266                                 int horizontal_msize;
267                                 int horizontal_mwin;
268                                 int vertical_msize;
269                                 int vertical_mwin;
270         } l_state;
271 };
272
273 enum    { SWM_S_COLOR_BAR, SWM_S_COLOR_BAR_BORDER, SWM_S_COLOR_BAR_FONT,
274           SWM_S_COLOR_FOCUS, SWM_S_COLOR_UNFOCUS, SWM_S_COLOR_MAX };
275
276 /* physical screen mapping */
277 #define SWM_WS_MAX              (10)            /* XXX Too small? */
278 struct swm_screen {
279         int                     idx;            /* screen index */
280         struct swm_region_list  rl;     /* list of regions on this screen */
281         Window                  root;
282         int                     xrandr_support;
283         struct workspace        ws[SWM_WS_MAX];
284
285         /* colors */
286         struct {
287                 unsigned long   color;
288                 char            *name;
289         } c[SWM_S_COLOR_MAX];
290 };
291 struct swm_screen       *screens;
292 int                     num_screens;
293
294 struct ws_win           *cur_focus = NULL;
295
296 /* args to functions */
297 union arg {
298         int                     id;
299 #define SWM_ARG_ID_FOCUSNEXT    (0)
300 #define SWM_ARG_ID_FOCUSPREV    (1)
301 #define SWM_ARG_ID_FOCUSMAIN    (2)
302 #define SWM_ARG_ID_SWAPNEXT     (3)
303 #define SWM_ARG_ID_SWAPPREV     (4)
304 #define SWM_ARG_ID_SWAPMAIN     (5)
305 #define SWM_ARG_ID_MASTERSHRINK (6)
306 #define SWM_ARG_ID_MASTERGROW   (7)
307 #define SWM_ARG_ID_MASTERADD    (8)
308 #define SWM_ARG_ID_MASTERDEL    (9)
309 #define SWM_ARG_ID_STACKRESET   (10)
310 #define SWM_ARG_ID_STACKINIT    (11)
311 #define SWM_ARG_ID_CYCLEWS_UP   (12)
312 #define SWM_ARG_ID_CYCLEWS_DOWN (13)
313 #define SWM_ARG_ID_CYCLESC_UP   (14)
314 #define SWM_ARG_ID_CYCLESC_DOWN (15)
315 #define SWM_ARG_ID_SS_ALL       (0)
316 #define SWM_ARG_ID_SS_WINDOW    (1)
317 #define SWM_ARG_ID_DONTCENTER   (0)
318 #define SWM_ARG_ID_CENTER       (1)
319         char                    **argv;
320 };
321
322 /* quirks */
323 struct quirk {
324         char                    *class;
325         char                    *name;
326         unsigned long           quirk;
327 #define SWM_Q_FLOAT             (1<<0)
328 } quirks[] = {
329         { "MPlayer",            "xv",           SWM_Q_FLOAT },
330         { "OpenOffice.org 2.4", "VCLSalFrame",  SWM_Q_FLOAT },
331         { NULL,         NULL,           0},
332 };
333
334 /* events */
335 void                    expose(XEvent *);
336 void                    keypress(XEvent *);
337 void                    buttonpress(XEvent *);
338 void                    configurerequest(XEvent *);
339 void                    configurenotify(XEvent *);
340 void                    destroynotify(XEvent *);
341 void                    enternotify(XEvent *);
342 void                    focusin(XEvent *);
343 void                    mappingnotify(XEvent *);
344 void                    maprequest(XEvent *);
345 void                    propertynotify(XEvent *);
346 void                    unmapnotify(XEvent *);
347 void                    visibilitynotify(XEvent *);
348
349 void                    (*handler[LASTEvent])(XEvent *) = {
350                                 [Expose] = expose,
351                                 [KeyPress] = keypress,
352                                 [ButtonPress] = buttonpress,
353                                 [ConfigureRequest] = configurerequest,
354                                 [ConfigureNotify] = configurenotify,
355                                 [DestroyNotify] = destroynotify,
356                                 [EnterNotify] = enternotify,
357                                 [FocusIn] = focusin,
358                                 [MappingNotify] = mappingnotify,
359                                 [MapRequest] = maprequest,
360                                 [PropertyNotify] = propertynotify,
361                                 [UnmapNotify] = unmapnotify,
362                                 [VisibilityNotify] = visibilitynotify,
363 };
364
365 unsigned long
366 name_to_color(char *colorname)
367 {
368         Colormap                cmap;
369         Status                  status;
370         XColor                  screen_def, exact_def;
371         unsigned long           result = 0;
372         char                    cname[32] = "#";
373
374         cmap = DefaultColormap(display, screens[0].idx);
375         status = XAllocNamedColor(display, cmap, colorname,
376             &screen_def, &exact_def);
377         if (!status) {
378                 strlcat(cname, colorname + 2, sizeof cname - 1);
379                 status = XAllocNamedColor(display, cmap, cname, &screen_def,
380                     &exact_def);
381         }
382         if (status)
383                 result = screen_def.pixel;
384         else
385                 fprintf(stderr, "color '%s' not found.\n", colorname);
386
387         return (result);
388 }
389
390 void
391 setscreencolor(char *val, int i, int c)
392 {
393         if (i > 0 && i <= ScreenCount(display)) {
394                 screens[i - 1].c[c].color = name_to_color(val);
395                 if ((screens[i - 1].c[c].name = strdup(val)) == NULL)
396                         errx(1, "strdup");
397         } else if (i == -1) {
398                 for (i = 0; i < ScreenCount(display); i++)
399                         screens[i].c[c].color = name_to_color(val);
400                         if ((screens[i - 1].c[c].name = strdup(val)) == NULL)
401                                 errx(1, "strdup");
402         } else
403                 errx(1, "invalid screen index: %d out of bounds (maximum %d)\n",
404                     i, ScreenCount(display));
405 }
406
407 int
408 varmatch(char *var, char *name, int *index)
409 {
410         char                    *p, buf[5];
411         int                     i;
412
413         i = strncmp(var, name, 255);
414         if (index == NULL)
415                 return (i);
416
417         *index = -1;
418         if (i <= 0)
419                 return (i);
420         p = var + strlen(name);
421         if (*p++ != '[')
422                 return (i);
423         bzero(buf, sizeof buf);
424         i = 0;
425         while (isdigit(*p) && i < sizeof buf)
426                 buf[i++] = *p++;
427         if (i == 0 || i >= sizeof buf || *p != ']')
428                 return (1);
429         *index = strtonum(buf, 0, 99, NULL);
430         return (0);
431 }
432
433 /* conf file stuff */
434 #define SWM_CONF_WS     "\n= \t"
435 #define SWM_CONF_FILE   "scrotwm.conf"
436 int
437 conf_load(char *filename)
438 {
439         FILE                    *config;
440         char                    *line, *cp, *var, *val;
441         size_t                  len, lineno = 0;
442         int                     i, sc;
443
444         DNPRINTF(SWM_D_MISC, "conf_load: filename %s\n", filename);
445
446         if (filename == NULL)
447                 return (1);
448
449         if ((config = fopen(filename, "r")) == NULL)
450                 return (1);
451
452         for (sc = ScreenCount(display);;) {
453                 if ((line = fparseln(config, &len, &lineno, NULL, 0)) == NULL)
454                         if (feof(config))
455                                 break;
456                 cp = line;
457                 cp += (long)strspn(cp, SWM_CONF_WS);
458                 if (cp[0] == '\0') {
459                         /* empty line */
460                         free(line);
461                         continue;
462                 }
463                 if ((var = strsep(&cp, SWM_CONF_WS)) == NULL || cp == NULL)
464                         break;
465                 cp += (long)strspn(cp, SWM_CONF_WS);
466                 if ((val = strsep(&cp, SWM_CONF_WS)) == NULL)
467                         break;
468
469                 DNPRINTF(SWM_D_MISC, "conf_load: %s=%s\n",var ,val);
470                 switch (var[0]) {
471                 case 'b':
472                         if (!strncmp(var, "bar_enabled", strlen("bar_enabled")))
473                                 bar_enabled = atoi(val);
474                         else if (!varmatch(var, "bar_border", &i))
475                                 setscreencolor(val, i, SWM_S_COLOR_BAR_BORDER);
476                         else if (!varmatch(var, "bar_color", &i))
477                                 setscreencolor(val, i, SWM_S_COLOR_BAR);
478                         else if (!varmatch(var, "bar_font_color", &i))
479                                 setscreencolor(val, i, SWM_S_COLOR_BAR_FONT);
480                         else if (!strncmp(var, "bar_font", strlen("bar_font")))
481                                 asprintf(&bar_fonts[0], "%s", val);
482                         else if (!strncmp(var, "bar_action", strlen("bar_action")))
483                                 asprintf(&bar_argv[0], "%s", val);
484                         else if (!strncmp(var, "bar_delay", strlen("bar_delay")))
485                                 bar_delay = atoi(val);
486                         else
487                                 goto bad;
488                         break;
489
490                 case 'c':
491                         if (!varmatch(var, "color_focus", &i))
492                                 setscreencolor(val, i, SWM_S_COLOR_FOCUS);
493                         else if (!varmatch(var, "color_unfocus", &i))
494                                 setscreencolor(val, i, SWM_S_COLOR_UNFOCUS);
495                         else if (!strncmp(var, "cycle_empty", strlen("cycle_empty")))
496                                 cycle_visible = atoi(val);
497                         else if (!strncmp(var, "cycle_visible", strlen("cycle_visible")))
498                                 cycle_visible = atoi(val);
499                         else
500                                 goto bad;
501                         break;
502
503                 case 'd':
504                         if (!strncmp(var, "dialog_ratio",
505                             strlen("dialog_ratio"))) {
506                                 dialog_ratio = atof(val);
507                                 if (dialog_ratio > 1.0 || dialog_ratio <= .3)
508                                         dialog_ratio = .6;
509                         } else
510                                 goto bad;
511                         break;
512
513                 case 's':
514                         if (!strncmp(var, "spawn_term", strlen("spawn_term")))
515                                 asprintf(&spawn_term[0], "%s", val);
516                         if (!strncmp(var, "screenshot_enabled",
517                             strlen("screenshot_enabled")))
518                                 ss_enabled = atoi(val);
519                         if (!strncmp(var, "screenshot_app",
520                             strlen("screenshot_app")))
521                                 asprintf(&spawn_screenshot[0], "%s", val);
522                         break;
523                 default:
524                         goto bad;
525                 }
526                 free(line);
527         }
528
529         fclose(config);
530         return (0);
531
532 bad:
533         errx(1, "invalid conf file entry: %s=%s", var, val);
534 }
535
536 void
537 socket_setnonblock(int fd)
538 {
539         int                     flags;
540
541         if ((flags = fcntl(fd, F_GETFL, 0)) == -1)
542                 err(1, "fcntl F_GETFL");
543         flags |= O_NONBLOCK;
544         if ((flags = fcntl(fd, F_SETFL, flags)) == -1)
545                 err(1, "fcntl F_SETFL");
546 }
547
548 void
549 bar_print(struct swm_region *r, char *s)
550 {
551         XClearWindow(display, r->bar_window);
552         XSetForeground(display, bar_gc, r->s->c[SWM_S_COLOR_BAR_FONT].color);
553         XDrawString(display, r->bar_window, bar_gc, 4, bar_fs->ascent, s,
554             strlen(s));
555 }
556
557 void
558 bar_extra_stop(void)
559 {
560         if (bar_pipe[0]) {
561                 close(bar_pipe[0]);
562                 bzero(bar_pipe, sizeof bar_pipe);
563         }
564         if (bar_pid) {
565                 kill(bar_pid, SIGTERM);
566                 bar_pid = 0;
567         }
568         strlcpy(bar_ext, "", sizeof bar_ext);
569         bar_extra = 0;
570 }
571
572 void
573 bar_update(void)
574 {
575         time_t                  tmt;
576         struct tm               tm;
577         struct swm_region       *r;
578         int                     i, x;
579         size_t                  len;
580         char                    s[SWM_BAR_MAX];
581         char                    loc[SWM_BAR_MAX];
582         char                    *b;
583
584         if (bar_enabled == 0)
585                 return;
586
587         if (bar_extra && bar_extra_running) {
588                 /* ignore short reads; it'll correct itself */
589                 while ((b = fgetln(stdin, &len)) != NULL)
590                         if (b && b[len - 1] == '\n') {
591                                 b[len - 1] = '\0';
592                                 strlcpy(bar_ext, b, sizeof bar_ext);
593                         }
594                 if (b == NULL && errno != EAGAIN) {
595                         fprintf(stderr, "bar_extra failed: errno: %d %s\n",
596                             errno, strerror(errno));
597                         bar_extra_stop();
598                 }
599         } else
600                 strlcpy(bar_ext, "", sizeof bar_ext);
601
602         time(&tmt);
603         localtime_r(&tmt, &tm);
604         strftime(s, sizeof s, "%a %b %d %R %Z %Y", &tm);
605         for (i = 0; i < ScreenCount(display); i++) {
606                 x = 1;
607                 TAILQ_FOREACH(r, &screens[i].rl, entry) {
608                         snprintf(loc, sizeof loc, "%s     %d:%d    %s    %s",
609                             s, x++, r->ws->idx + 1, bar_ext, bar_vertext);
610                         bar_print(r, loc);
611                 }
612         }
613         XSync(display, False);
614         alarm(bar_delay);
615 }
616
617 void
618 bar_signal(int sig)
619 {
620         bar_alarm = 1;
621 }
622
623 void
624 bar_toggle(struct swm_region *r, union arg *args)
625 {
626         struct swm_region       *tmpr;
627         int                     i, j, sc = ScreenCount(display);
628
629         DNPRINTF(SWM_D_MISC, "bar_toggle\n");
630
631         if (bar_enabled) {
632                 for (i = 0; i < sc; i++)
633                         TAILQ_FOREACH(tmpr, &screens[i].rl, entry)
634                                 XUnmapWindow(display, tmpr->bar_window);
635         } else {
636                 for (i = 0; i < sc; i++)
637                         TAILQ_FOREACH(tmpr, &screens[i].rl, entry)
638                                 XMapRaised(display, tmpr->bar_window);
639         }
640         bar_enabled = !bar_enabled;
641         XSync(display, False);
642         for (i = 0; i < sc; i++)
643                 for (j = 0; j < SWM_WS_MAX; j++)
644                         screens[i].ws[j].restack = 1;
645
646         stack();
647         /* must be after stack */
648         for (i = 0; i < sc; i++)
649                 TAILQ_FOREACH(tmpr, &screens[i].rl, entry)
650                         bar_update();
651 }
652
653 void
654 bar_refresh(void)
655 {
656         XSetWindowAttributes    wa;
657         struct swm_region       *r;
658         int                     i;
659
660         /* do this here because the conf file is in memory */
661         if (bar_extra && bar_extra_running == 0 && bar_argv[0]) {
662                 /* launch external status app */
663                 bar_extra_running = 1;
664                 if (pipe(bar_pipe) == -1)
665                         err(1, "pipe error");
666                 socket_setnonblock(bar_pipe[0]);
667                 socket_setnonblock(bar_pipe[1]); /* XXX hmmm, really? */
668                 if (dup2(bar_pipe[0], 0) == -1)
669                         errx(1, "dup2");
670                 if (dup2(bar_pipe[1], 1) == -1)
671                         errx(1, "dup2");
672                 if (signal(SIGPIPE, SIG_IGN) == SIG_ERR)
673                         err(1, "could not disable SIGPIPE");
674                 switch (bar_pid = fork()) {
675                 case -1:
676                         err(1, "cannot fork");
677                         break;
678                 case 0: /* child */
679                         close(bar_pipe[0]);
680                         execvp(bar_argv[0], bar_argv);
681                         err(1, "%s external app failed", bar_argv[0]);
682                         break;
683                 default: /* parent */
684                         close(bar_pipe[1]);
685                         break;
686                 }
687         }
688
689         bzero(&wa, sizeof wa);
690         for (i = 0; i < ScreenCount(display); i++)
691                 TAILQ_FOREACH(r, &screens[i].rl, entry) {
692                         wa.border_pixel =
693                             screens[i].c[SWM_S_COLOR_BAR_BORDER].color;
694                         wa.background_pixel =
695                             screens[i].c[SWM_S_COLOR_BAR].color;
696                         XChangeWindowAttributes(display, r->bar_window,
697                             CWBackPixel | CWBorderPixel, &wa);
698                 }
699         bar_update();
700 }
701
702 void
703 bar_setup(struct swm_region *r)
704 {
705         int                     i;
706
707         for (i = 0; bar_fonts[i] != NULL; i++) {
708                 bar_fs = XLoadQueryFont(display, bar_fonts[i]);
709                 if (bar_fs) {
710                         bar_fidx = i;
711                         break;
712                 }
713         }
714         if (bar_fonts[i] == NULL)
715                         errx(1, "couldn't load font");
716         bar_height = bar_fs->ascent + bar_fs->descent + 3;
717
718         r->bar_window = XCreateSimpleWindow(display, 
719             r->s->root, X(r), Y(r), WIDTH(r) - 2, bar_height - 2,
720             1, r->s->c[SWM_S_COLOR_BAR_BORDER].color,
721             r->s->c[SWM_S_COLOR_BAR].color);
722         bar_gc = XCreateGC(display, r->bar_window, 0, &bar_gcv);
723         XSetFont(display, bar_gc, bar_fs->fid);
724         XSelectInput(display, r->bar_window, VisibilityChangeMask);
725         if (bar_enabled)
726                 XMapRaised(display, r->bar_window);
727         DNPRINTF(SWM_D_MISC, "bar_setup: bar_window %lu\n", r->bar_window);
728
729         if (signal(SIGALRM, bar_signal) == SIG_ERR)
730                 err(1, "could not install bar_signal");
731         bar_refresh();
732 }
733
734 void
735 version(struct swm_region *r, union arg *args)
736 {
737         bar_version = !bar_version;
738         if (bar_version)
739                 strlcpy(bar_vertext, cvstag, sizeof bar_vertext);
740         else
741                 strlcpy(bar_vertext, "", sizeof bar_vertext);
742         bar_update();
743 }
744
745 void
746 config_win(struct ws_win *win)
747 {
748         XConfigureEvent         ce;
749
750         DNPRINTF(SWM_D_MISC, "config_win: win %lu x %d y %d w %d h %d\n",
751             win->id, win->g.x, win->g.y, win->g.w, win->g.h);
752         ce.type = ConfigureNotify;
753         ce.display = display;
754         ce.event = win->id;
755         ce.window = win->id;
756         ce.x = win->g.x;
757         ce.y = win->g.y;
758         ce.width = win->g.w;
759         ce.height = win->g.h;
760         ce.border_width = 1; /* XXX store this! */
761         ce.above = None;
762         ce.override_redirect = False;
763         XSendEvent(display, win->id, False, StructureNotifyMask, (XEvent *)&ce);
764 }
765
766 int
767 count_win(struct workspace *ws, int count_transient)
768 {
769         struct ws_win           *win;
770         int                     count = 0;
771
772         TAILQ_FOREACH(win, &ws->winlist, entry) {
773                 if (count_transient == 0 && win->floating)
774                         continue;
775                 if (count_transient == 0 && win->transient)
776                         continue;
777                 count++;
778         }
779         DNPRINTF(SWM_D_MISC, "count_win: %d\n", count);
780
781         return (count);
782 }
783
784 void
785 quit(struct swm_region *r, union arg *args)
786 {
787         DNPRINTF(SWM_D_MISC, "quit\n");
788         running = 0;
789 }
790
791 void
792 unmap_all(void)
793 {
794         struct ws_win           *win;
795         int                     i, j;
796
797         for (i = 0; i < ScreenCount(display); i++)
798                 for (j = 0; j < SWM_WS_MAX; j++)
799                         TAILQ_FOREACH(win, &screens[i].ws[j].winlist, entry)
800                                 XUnmapWindow(display, win->id);
801 }
802
803 void
804 restart(struct swm_region *r, union arg *args)
805 {
806         DNPRINTF(SWM_D_MISC, "restart: %s\n", start_argv[0]);
807
808         /* disable alarm because the following code may not be interrupted */
809         alarm(0);
810         if (signal(SIGALRM, SIG_IGN) == SIG_ERR)
811                 errx(1, "can't disable alarm");
812
813         bar_extra_stop();
814         bar_extra = 1;
815         unmap_all();
816         XCloseDisplay(display);
817         execvp(start_argv[0], start_argv);
818         fprintf(stderr, "execvp failed\n");
819         perror(" failed");
820         quit(NULL, NULL);
821 }
822
823 struct swm_region *
824 root_to_region(Window root)
825 {
826         struct swm_region       *r;
827         Window                  rr, cr;
828         int                     i, x, y, wx, wy;
829         unsigned int            mask;
830
831         for (i = 0; i < ScreenCount(display); i++)
832                 if (screens[i].root == root)
833                         break;
834
835         if (XQueryPointer(display, screens[i].root, 
836             &rr, &cr, &x, &y, &wx, &wy, &mask) == False) {
837                 /* if we can't query the pointer, grab the first region */
838                 r = TAILQ_FIRST(&screens[i].rl);
839         } else {
840                 /* otherwise, choose a region based on pointer location */
841                 TAILQ_FOREACH(r, &screens[i].rl, entry) {
842                         if (x >= X(r) && x <= X(r) + WIDTH(r) &&
843                             y >= Y(r) && y <= Y(r) + HEIGHT(r))
844                                 break;
845                 }
846
847                 if (r == NULL)
848                         r = TAILQ_FIRST(&screens[i].rl);
849         }
850         return (r);
851 }
852
853 struct ws_win *
854 find_window(Window id)
855 {
856         struct ws_win           *win;
857         int                     i, j;
858
859         for (i = 0; i < ScreenCount(display); i++)
860                 for (j = 0; j < SWM_WS_MAX; j++)
861                         TAILQ_FOREACH(win, &screens[i].ws[j].winlist, entry)
862                                 if (id == win->id)
863                                         return (win);
864         return (NULL);
865 }
866
867 void
868 spawn(struct swm_region *r, union arg *args)
869 {
870         char                    *ret;
871         int                     si;
872
873         DNPRINTF(SWM_D_MISC, "spawn: %s\n", args->argv[0]);
874         /*
875          * The double-fork construct avoids zombie processes and keeps the code
876          * clean from stupid signal handlers.
877          */
878         if (fork() == 0) {
879                 if (fork() == 0) {
880                         if (display)
881                                 close(ConnectionNumber(display));
882                         setenv("LD_PRELOAD", SWM_LIB, 1);
883                         if (asprintf(&ret, "%d", r->ws->idx)) {
884                                 setenv("_SWM_WS", ret, 1);
885                                 free(ret);
886                         }
887                         if (asprintf(&ret, "%d", getpid())) {
888                                 setenv("_SWM_PID", ret, 1);
889                                 free(ret);
890                         }
891                         setsid();
892                         /* kill stdin, mplayer, ssh-add etc. need that */
893                         si = open("/dev/null", O_RDONLY, 0);
894                         if (si == -1)
895                                 err(1, "open /dev/null");
896                         if (dup2(si, 0) == -1)
897                                 err(1, "dup2 /dev/null");
898                         execvp(args->argv[0], args->argv);
899                         fprintf(stderr, "execvp failed\n");
900                         perror(" failed");
901                 }
902                 exit(0);
903         }
904         wait(0);
905 }
906
907 void
908 spawnmenu(struct swm_region *r, union arg *args)
909 {
910         DNPRINTF(SWM_D_MISC, "spawnmenu\n");
911
912         spawn_menu[SWM_MENU_FN] = bar_fonts[bar_fidx];
913         spawn_menu[SWM_MENU_NB] = r->s->c[SWM_S_COLOR_BAR].name;
914         spawn_menu[SWM_MENU_NF] = r->s->c[SWM_S_COLOR_BAR_FONT].name;
915         spawn_menu[SWM_MENU_SB] = r->s->c[SWM_S_COLOR_BAR_BORDER].name;
916         spawn_menu[SWM_MENU_SF] = r->s->c[SWM_S_COLOR_BAR].name;
917
918         spawn(r, args);
919 }
920
921 void
922 unfocus_all(void)
923 {
924         struct ws_win           *win;
925         int                     i, j;
926
927         DNPRINTF(SWM_D_FOCUS, "unfocus_all:\n");
928
929         for (i = 0; i < ScreenCount(display); i++)
930                 for (j = 0; j < SWM_WS_MAX; j++)
931                         TAILQ_FOREACH(win, &screens[i].ws[j].winlist, entry) {
932                                 if (win->ws->r == NULL)
933                                         continue;
934                                 grabbuttons(win, 0);
935                                 XSetWindowBorder(display, win->id,
936                                     win->ws->r->s->c[SWM_S_COLOR_UNFOCUS].color);
937                                 win->got_focus = 0;
938                                 win->ws->focus = NULL;
939                                 cur_focus = NULL;
940                         }
941 }
942
943 void
944 focus_win(struct ws_win *win)
945 {
946         DNPRINTF(SWM_D_FOCUS, "focus_win: id: %lu\n", win ? win->id : 0);
947
948         if (win == NULL)
949                 return;
950
951         unfocus_all();
952         win->ws->focus = win;
953         if (win->ws->r != NULL) {
954                 cur_focus = win;
955                 if (!win->got_focus) {
956                         XSetWindowBorder(display, win->id,
957                             win->ws->r->s->c[SWM_S_COLOR_FOCUS].color);
958                         grabbuttons(win, 1);
959                 }
960                 win->got_focus = 1;
961                 XSetInputFocus(display, win->id,
962                     RevertToPointerRoot, CurrentTime);
963         }
964 }
965
966 void
967 switchws(struct swm_region *r, union arg *args)
968 {
969         int                     wsid = args->id;
970         struct swm_region       *this_r, *other_r;
971         struct ws_win           *win;
972         struct workspace        *new_ws, *old_ws;
973
974         this_r = r;
975         old_ws = this_r->ws;
976         new_ws = &this_r->s->ws[wsid];
977
978         DNPRINTF(SWM_D_WS, "switchws screen %d region %dx%d+%d+%d: "
979             "%d -> %d\n", r->s->idx, WIDTH(r), HEIGHT(r), X(r), Y(r),
980             old_ws->idx, wsid);
981
982         if (new_ws == old_ws)
983                 return;
984
985         other_r = new_ws->r;
986         if (!other_r) {
987                 /* if the other workspace is hidden, switch windows */
988                 /* map new window first to prevent ugly blinking */
989                 TAILQ_FOREACH(win, &new_ws->winlist, entry)
990                         XMapRaised(display, win->id);
991
992                 TAILQ_FOREACH(win, &old_ws->winlist, entry)
993                         XUnmapWindow(display, win->id);
994                 old_ws->r = NULL;
995                 old_ws->restack = 1;
996         } else {
997                 other_r->ws = old_ws;
998                 old_ws->r = other_r;
999         }
1000         this_r->ws = new_ws;
1001         new_ws->r = this_r;
1002
1003         ignore_enter = 1;
1004         /* set focus */
1005         if (new_ws->focus)
1006                 focus_win(new_ws->focus);
1007         stack();
1008         bar_update();
1009 }
1010
1011 void
1012 cyclews(struct swm_region *r, union arg *args)
1013 {
1014         union                   arg a;
1015         struct swm_screen       *s = r->s;
1016
1017         DNPRINTF(SWM_D_WS, "cyclews id %d "
1018             "in screen %d region %dx%d+%d+%d ws %d\n", args->id,
1019             r->s->idx, WIDTH(r), HEIGHT(r), X(r), Y(r), r->ws->idx);
1020
1021         a.id = r->ws->idx;
1022         do {
1023                 switch (args->id) {
1024                 case SWM_ARG_ID_CYCLEWS_UP:
1025                         if (a.id < SWM_WS_MAX - 1)
1026                                 a.id++;
1027                         else
1028                                 a.id = 0;
1029                         break;
1030                 case SWM_ARG_ID_CYCLEWS_DOWN:
1031                         if (a.id > 0)
1032                                 a.id--;
1033                         else
1034                                 a.id = SWM_WS_MAX - 1;
1035                         break;
1036                 default:
1037                         return;
1038                 };
1039
1040                 if (cycle_empty == 0 && TAILQ_EMPTY(&s->ws[a.id].winlist))
1041                         continue;
1042                 if (cycle_visible == 0 && s->ws[a.id].r != NULL)
1043                         continue;
1044
1045                 switchws(r, &a);
1046         } while (a.id != r->ws->idx);
1047 }
1048
1049 void
1050 cyclescr(struct swm_region *r, union arg *args)
1051 {
1052         struct swm_region       *rr;
1053         int                     i;
1054
1055         i = r->s->idx;
1056         switch (args->id) {
1057         case SWM_ARG_ID_CYCLESC_UP:
1058                 rr = TAILQ_NEXT(r, entry);
1059                 if (rr == NULL)
1060                         rr = TAILQ_FIRST(&screens[i].rl);
1061                 break;
1062         case SWM_ARG_ID_CYCLESC_DOWN:
1063                 rr = TAILQ_PREV(r, swm_region_list, entry);
1064                 if (rr == NULL)
1065                         rr = TAILQ_LAST(&screens[i].rl, swm_region_list);
1066                 break;
1067         default:
1068                 return;
1069         };
1070         unfocus_all();
1071         XSetInputFocus(display, PointerRoot, RevertToPointerRoot, CurrentTime);
1072         XWarpPointer(display, None, rr->s[i].root, 0, 0, 0, 0, rr->g.x,
1073             rr->g.y + bar_enabled ? bar_height : 0);
1074 }
1075
1076 void
1077 swapwin(struct swm_region *r, union arg *args)
1078 {
1079         struct ws_win           *target;
1080         struct ws_win_list      *wl;
1081
1082
1083         DNPRINTF(SWM_D_WS, "swapwin id %d "
1084             "in screen %d region %dx%d+%d+%d ws %d\n", args->id,
1085             r->s->idx, WIDTH(r), HEIGHT(r), X(r), Y(r), r->ws->idx);
1086         if (cur_focus == NULL)
1087                 return;
1088
1089         wl = &cur_focus->ws->winlist;
1090
1091         switch (args->id) {
1092         case SWM_ARG_ID_SWAPPREV:
1093                 target = TAILQ_PREV(cur_focus, ws_win_list, entry);
1094                 TAILQ_REMOVE(wl, cur_focus, entry);
1095                 if (target == NULL)
1096                         TAILQ_INSERT_TAIL(wl, cur_focus, entry);
1097                 else
1098                         TAILQ_INSERT_BEFORE(target, cur_focus, entry);
1099                 break;
1100         case SWM_ARG_ID_SWAPNEXT: 
1101                 target = TAILQ_NEXT(cur_focus, entry);
1102                 TAILQ_REMOVE(wl, cur_focus, entry);
1103                 if (target == NULL)
1104                         TAILQ_INSERT_HEAD(wl, cur_focus, entry);
1105                 else
1106                         TAILQ_INSERT_AFTER(wl, target, cur_focus, entry);
1107                 break;
1108         case SWM_ARG_ID_SWAPMAIN:
1109                 target = TAILQ_FIRST(wl);
1110                 if (target == cur_focus)
1111                         return;
1112                 TAILQ_REMOVE(wl, target, entry);
1113                 TAILQ_INSERT_BEFORE(cur_focus, target, entry);
1114                 TAILQ_REMOVE(wl, cur_focus, entry);
1115                 TAILQ_INSERT_HEAD(wl, cur_focus, entry);
1116                 break;
1117         default:
1118                 DNPRINTF(SWM_D_MOVE, "invalid id: %d\n", args->id);
1119                 return;
1120         }
1121
1122         ignore_enter = 2;
1123         stack();
1124 }
1125
1126 void
1127 focus(struct swm_region *r, union arg *args)
1128 {
1129         struct ws_win           *winfocus, *winlostfocus;
1130         struct ws_win_list      *wl;
1131
1132         DNPRINTF(SWM_D_FOCUS, "focus: id %d\n", args->id);
1133         if (cur_focus == NULL)
1134                 return;
1135
1136         wl = &cur_focus->ws->winlist;
1137
1138         winlostfocus = cur_focus;
1139
1140         switch (args->id) {
1141         case SWM_ARG_ID_FOCUSPREV:
1142                 winfocus = TAILQ_PREV(cur_focus, ws_win_list, entry);
1143                 if (winfocus == NULL)
1144                         winfocus = TAILQ_LAST(wl, ws_win_list);
1145                 break;
1146
1147         case SWM_ARG_ID_FOCUSNEXT:
1148                 winfocus = TAILQ_NEXT(cur_focus, entry);
1149                 if (winfocus == NULL)
1150                         winfocus = TAILQ_FIRST(wl);
1151                 break;
1152
1153         case SWM_ARG_ID_FOCUSMAIN:
1154                 winfocus = TAILQ_FIRST(wl);
1155                 break;
1156
1157         default:
1158                 return;
1159         }
1160
1161         if (winfocus == winlostfocus || winfocus == NULL)
1162                 return;
1163
1164         XMapRaised(display, winfocus->id);
1165         focus_win(winfocus);
1166         XSync(display, False);
1167 }
1168
1169 void
1170 cycle_layout(struct swm_region *r, union arg *args)
1171 {
1172         struct workspace        *ws = r->ws;
1173
1174         DNPRINTF(SWM_D_EVENT, "cycle_layout: workspace: %d\n", ws->idx);
1175
1176         ws->cur_layout++;
1177         if (ws->cur_layout->l_stack == NULL)
1178                 ws->cur_layout = &layouts[0];
1179         ignore_enter = 1;
1180         stack();
1181 }
1182
1183 void
1184 stack_config(struct swm_region *r, union arg *args)
1185 {
1186         struct workspace        *ws = r->ws;
1187
1188         DNPRINTF(SWM_D_STACK, "stack_config for workspace %d (id %d\n",
1189             args->id, ws->idx);
1190
1191         if (ws->cur_layout->l_config != NULL)
1192                 ws->cur_layout->l_config(ws, args->id);
1193
1194         if (args->id != SWM_ARG_ID_STACKINIT);
1195                 stack();
1196 }
1197
1198 void
1199 stack(void) {
1200         struct swm_geometry     g;
1201         struct swm_region       *r;
1202         int                     i, j;
1203
1204         DNPRINTF(SWM_D_STACK, "stack\n");
1205
1206         for (i = 0; i < ScreenCount(display); i++) {
1207                 j = 0;
1208                 TAILQ_FOREACH(r, &screens[i].rl, entry) {
1209                         DNPRINTF(SWM_D_STACK, "stacking workspace %d "
1210                             "(screen %d, region %d)\n", r->ws->idx, i, j++);
1211
1212                         /* start with screen geometry, adjust for bar */
1213                         g = r->g;
1214                         g.w -= 2;
1215                         g.h -= 2;
1216                         if (bar_enabled) {
1217                                 g.y += bar_height;
1218                                 g.h -= bar_height;
1219                         } 
1220
1221                         r->ws->restack = 0;
1222                         r->ws->cur_layout->l_stack(r->ws, &g);
1223                 }
1224         }
1225         XSync(display, False);
1226 }
1227
1228 void
1229 stack_floater(struct ws_win *win, struct swm_region *r)
1230 {
1231         unsigned int            mask;
1232         XWindowChanges          wc;
1233
1234         bzero(&wc, sizeof wc);
1235         mask = CWX | CWY | CWBorderWidth | CWWidth | CWHeight;
1236         wc.border_width = 1;
1237         if (win->transient) {
1238                 win->g.w = (double)WIDTH(r) * dialog_ratio;
1239                 win->g.h = (double)HEIGHT(r) * dialog_ratio;
1240         }
1241         wc.width = win->g.w;
1242         wc.height = win->g.h;
1243         if (win->manual) {
1244                 wc.x = win->g.x;
1245                 wc.y = win->g.y;
1246         } else {
1247                 wc.x = (WIDTH(r) - win->g.w) / 2;
1248                 wc.y = (HEIGHT(r) - win->g.h) / 2;
1249         }
1250
1251         DNPRINTF(SWM_D_STACK, "stack_floater: win %lu x %d y %d w %d h %d\n",
1252             win->id, wc.x, wc.y, wc.width, wc.height);
1253
1254         XConfigureWindow(display, win->id, mask, &wc);
1255 }
1256
1257 #define SWAPXY(g)       do {                            \
1258         int tmp;                                        \
1259         tmp = (g)->y; (g)->y = (g)->x; (g)->x = tmp;    \
1260         tmp = (g)->h; (g)->h = (g)->w; (g)->w = tmp;    \
1261 } while (0)
1262 void
1263 stack_master(struct workspace *ws, struct swm_geometry *g, int rot, int flip)
1264 {
1265         XWindowChanges          wc;
1266         struct swm_geometry     win_g, r_g = *g;
1267         struct ws_win           *win, *winfocus;
1268         int                     i, j, w_inc, h_inc, w_base, h_base;
1269         int                     hrh, extra, h_slice, last_h = 0;
1270         int                     split, colno, winno, mwin, msize, mscale;
1271         int                     remain, missing, v_slice;;
1272         unsigned int            mask;
1273
1274         DNPRINTF(SWM_D_STACK, "stack_master: workspace: %d\n rot=%s flip=%s",
1275             ws->idx, rot ? "yes" : "no", flip ? "yes" : "no");
1276
1277         if ((winno = count_win(ws, 0)) == 0)
1278                 return;
1279
1280         if (ws->focus == NULL)
1281                 ws->focus = TAILQ_FIRST(&ws->winlist);
1282         winfocus = cur_focus ? cur_focus : ws->focus;
1283
1284         win = TAILQ_FIRST(&ws->winlist);
1285         if (rot) {
1286                 w_inc = win->sh.width_inc;
1287                 w_base = win->sh.base_width;
1288                 mwin = ws->l_state.horizontal_mwin;
1289                 mscale = ws->l_state.horizontal_msize;
1290                 SWAPXY(&r_g);
1291         } else {
1292                 w_inc = win->sh.height_inc;
1293                 w_base = win->sh.base_height;
1294                 mwin = ws->l_state.vertical_mwin;
1295                 mscale = ws->l_state.vertical_msize;
1296         }
1297         win_g = r_g;
1298
1299         h_slice = r_g.h / SWM_H_SLICE;
1300         if (mwin && winno > mwin) {
1301                 v_slice = r_g.w / SWM_V_SLICE;
1302
1303                 split = mwin;
1304                 colno = split;
1305                 msize = v_slice * mscale;
1306
1307                 if (w_inc > 1 && w_inc < v_slice) {
1308                         /* adjust for window's requested size increment */
1309                         remain = (win_g.w - w_base) % w_inc;
1310                         missing = w_inc - remain;
1311
1312                         if (missing <= extra || j == 0) {
1313                                 extra -= missing;
1314                                 win_g.w += missing;
1315                         } else {
1316                                 win_g.w -= remain;
1317                                 extra += remain;
1318                         }
1319                 }
1320
1321                 win_g.w = msize;
1322                 if (flip) 
1323                         win_g.x += r_g.w - msize;
1324         } else {
1325                 colno = winno;
1326                 split = 0;
1327         }
1328         hrh = r_g.h / colno;
1329         extra = r_g.h - (colno * hrh);
1330         win_g.h = hrh - 2;
1331
1332         /*  stack all the tiled windows */
1333         i = j = 0;
1334         TAILQ_FOREACH(win, &ws->winlist, entry) {
1335                 if (win->transient != 0 || win->floating != 0)
1336                         continue;
1337
1338                 if (split && i == split) {
1339                         colno = winno - split;
1340                         hrh = (r_g.h / colno);
1341                         extra = r_g.h - (colno * hrh);
1342                         if (flip)
1343                                 win_g.x = r_g.x;
1344                         else
1345                                 win_g.x += msize + 2;
1346                         win_g.w = r_g.w - (msize + 2);
1347                         j = 0;
1348                 }
1349                 win_g.h = hrh - 2;
1350                 if (rot) {
1351                         h_inc = win->sh.width_inc;
1352                         h_base = win->sh.base_width;
1353                 } else {
1354                         h_inc = win->sh.height_inc;     
1355                         h_base = win->sh.base_height;
1356                 }
1357                 if (j == colno - 1) {
1358                         win_g.h = hrh + extra;
1359                 } else if (h_inc > 1 && h_inc < h_slice) {
1360                         /* adjust for window's requested size increment */
1361                         remain = (win_g.h - h_base) % h_inc;
1362                         missing = h_inc - remain;
1363
1364                         if (missing <= extra || j == 0) {
1365                                 extra -= missing;
1366                                 win_g.h += missing;
1367                         } else {
1368                                 win_g.h -= remain;
1369                                 extra += remain;
1370                         }
1371                 }
1372                  
1373                 if (j == 0)
1374                         win_g.y = r_g.y;
1375                 else
1376                         win_g.y += last_h + 2;
1377
1378                 bzero(&wc, sizeof wc);
1379                 wc.border_width = 1;
1380                 if (rot) {
1381                         win->g.x = wc.x = win_g.y;
1382                         win->g.y = wc.y = win_g.x;
1383                         win->g.w = wc.width = win_g.h;
1384                         win->g.h = wc.height = win_g.w;
1385                 } else {
1386                         win->g.x = wc.x = win_g.x;
1387                         win->g.y = wc.y = win_g.y;
1388                         win->g.w = wc.width = win_g.w;
1389                         win->g.h = wc.height = win_g.h;
1390                 }
1391                 mask = CWX | CWY | CWWidth | CWHeight | CWBorderWidth;
1392                 XConfigureWindow(display, win->id, mask, &wc);
1393                 XMapRaised(display, win->id);
1394                 /*
1395                 fprintf(stderr, "vertical_stack: win %d x %d y %d w %d h %d bw %d\n", win->id, win->g.x, win->g.y, win->g.w , win->g.h, wc.border_width);
1396                 */
1397
1398                 last_h = win_g.h;
1399                 i++;
1400                 j++;
1401         }
1402
1403         /* now, stack all the floaters and transients */
1404         TAILQ_FOREACH(win, &ws->winlist, entry) {
1405                 if (win->transient == 0 && win->floating == 0)
1406                         continue;
1407
1408                 stack_floater(win, ws->r);
1409                 XMapRaised(display, win->id);
1410         }
1411
1412         if (winfocus)
1413                 focus_win(winfocus); /* has to be done outside of the loop */
1414 }
1415
1416 void
1417 vertical_config(struct workspace *ws, int id)
1418 {
1419         DNPRINTF(SWM_D_STACK, "vertical_resize: workspace: %d\n", ws->idx);
1420
1421         switch (id) {
1422         case SWM_ARG_ID_STACKRESET:
1423         case SWM_ARG_ID_STACKINIT:
1424                 ws->l_state.vertical_msize = SWM_V_SLICE / 2;
1425                 ws->l_state.vertical_mwin = 1;
1426                 break;
1427         case SWM_ARG_ID_MASTERSHRINK:
1428                 if (ws->l_state.vertical_msize > 1)
1429                         ws->l_state.vertical_msize--;
1430                 break;
1431         case SWM_ARG_ID_MASTERGROW:
1432                 if (ws->l_state.vertical_msize < SWM_V_SLICE - 1)
1433                         ws->l_state.vertical_msize++;
1434                 break;
1435         case SWM_ARG_ID_MASTERADD:
1436                 ws->l_state.vertical_mwin++;
1437                 break;
1438         case SWM_ARG_ID_MASTERDEL:
1439                 if (ws->l_state.vertical_mwin > 0)
1440                         ws->l_state.vertical_mwin--;
1441                 break;
1442         default:
1443                 return;
1444         }
1445 }
1446
1447 void
1448 vertical_stack(struct workspace *ws, struct swm_geometry *g)
1449 {
1450         DNPRINTF(SWM_D_STACK, "vertical_stack: workspace: %d\n", ws->idx);
1451
1452         stack_master(ws, g, 0, 0);
1453 }
1454
1455 void
1456 horizontal_config(struct workspace *ws, int id)
1457 {
1458         DNPRINTF(SWM_D_STACK, "horizontal_config: workspace: %d\n", ws->idx);
1459
1460         switch (id) {
1461         case SWM_ARG_ID_STACKRESET:
1462         case SWM_ARG_ID_STACKINIT:
1463                 ws->l_state.horizontal_mwin = 1;
1464                 ws->l_state.horizontal_msize = SWM_H_SLICE / 2;
1465                 break;
1466         case SWM_ARG_ID_MASTERSHRINK:
1467                 if (ws->l_state.horizontal_msize > 1)
1468                         ws->l_state.horizontal_msize--;
1469                 break;
1470         case SWM_ARG_ID_MASTERGROW:
1471                 if (ws->l_state.horizontal_msize < SWM_H_SLICE - 1)
1472                         ws->l_state.horizontal_msize++;
1473                 break;
1474         case SWM_ARG_ID_MASTERADD:
1475                 ws->l_state.horizontal_mwin++;
1476                 break;
1477         case SWM_ARG_ID_MASTERDEL:
1478                 if (ws->l_state.horizontal_mwin > 0)
1479                         ws->l_state.horizontal_mwin--;
1480                 break;
1481         default:
1482                 return;
1483         }
1484 }
1485
1486 void
1487 horizontal_stack(struct workspace *ws, struct swm_geometry *g)
1488 {
1489         DNPRINTF(SWM_D_STACK, "vertical_stack: workspace: %d\n", ws->idx);
1490
1491         stack_master(ws, g, 1, 0);
1492 }
1493
1494 /* fullscreen view */
1495 void
1496 max_stack(struct workspace *ws, struct swm_geometry *g) {
1497         XWindowChanges          wc;
1498         struct swm_geometry     gg = *g;
1499         struct ws_win           *win, *winfocus;
1500         unsigned int            mask;
1501
1502         DNPRINTF(SWM_D_STACK, "max_stack: workspace: %d\n", ws->idx);
1503
1504         if (count_win(ws, 0) == 0)
1505                 return;
1506
1507         if (ws->focus == NULL)
1508                 ws->focus = TAILQ_FIRST(&ws->winlist);
1509         winfocus = cur_focus ? cur_focus : ws->focus;
1510
1511         TAILQ_FOREACH(win, &ws->winlist, entry) {
1512                 if (win->transient != 0 || win->floating != 0) {
1513                         if (win == ws->focus) {
1514                                 /* XXX maximize? */
1515                                 stack_floater(win, ws->r);
1516                                 XMapRaised(display, win->id);
1517                         } else
1518                                 XUnmapWindow(display, win->id);
1519                 } else {
1520                         bzero(&wc, sizeof wc);
1521                         wc.border_width = 1;
1522                         win->g.x = wc.x = gg.x;
1523                         win->g.y = wc.y = gg.y;
1524                         win->g.w = wc.width = gg.w;
1525                         win->g.h = wc.height = gg.h;
1526                         mask = CWX | CWY | CWWidth | CWHeight | CWBorderWidth;
1527                         XConfigureWindow(display, win->id, mask, &wc);
1528
1529                         if (win == ws->focus) {
1530                                 XMapRaised(display, win->id);
1531                         } else
1532                                 XUnmapWindow(display, win->id);
1533                 }
1534         }
1535
1536         if (winfocus)
1537                 focus_win(winfocus); /* has to be done outside of the loop */
1538 }
1539
1540 void
1541 send_to_ws(struct swm_region *r, union arg *args)
1542 {
1543         int                     wsid = args->id;
1544         struct ws_win           *win = cur_focus;
1545         struct workspace        *ws, *nws;
1546         Atom                    ws_idx_atom = 0;
1547         unsigned char           ws_idx_str[SWM_PROPLEN];
1548
1549         if (win == NULL)
1550                 return;
1551
1552         DNPRINTF(SWM_D_MOVE, "send_to_ws: win: %lu\n", win->id);
1553
1554         ws = win->ws;
1555         nws = &win->s->ws[wsid];
1556
1557         XUnmapWindow(display, win->id);
1558
1559         /* find a window to focus */
1560         ws->focus = TAILQ_PREV(win, ws_win_list, entry);
1561         if (ws->focus == NULL)
1562                 ws->focus = TAILQ_FIRST(&ws->winlist);
1563         if (ws->focus == win)
1564                 ws->focus = NULL;
1565
1566         TAILQ_REMOVE(&ws->winlist, win, entry);
1567
1568         TAILQ_INSERT_TAIL(&nws->winlist, win, entry);
1569         win->ws = nws;
1570
1571         /* Try to update the window's workspace property */
1572         ws_idx_atom = XInternAtom(display, "_SWM_WS", False);
1573         if (ws_idx_atom &&
1574             snprintf(ws_idx_str, SWM_PROPLEN, "%d", nws->idx) < SWM_PROPLEN) {
1575                 DNPRINTF(SWM_D_PROP, "setting property _SWM_WS to %s\n",
1576                     ws_idx_str);
1577                 XChangeProperty(display, win->id, ws_idx_atom, XA_STRING, 8,
1578                     PropModeReplace, ws_idx_str, SWM_PROPLEN);
1579         }
1580
1581         if (count_win(nws, 1) == 1)
1582                 nws->focus = win;
1583         ws->restack = 1;
1584         nws->restack = 1;
1585
1586         stack();
1587 }
1588
1589 void
1590 wkill(struct swm_region *r, union arg *args)
1591 {
1592         DNPRINTF(SWM_D_MISC, "wkill\n");
1593         if(r->ws->focus != NULL)
1594                 XKillClient(display, r->ws->focus->id);
1595 }
1596
1597 void
1598 screenshot(struct swm_region *r, union arg *args)
1599 {
1600         union arg                       a;
1601
1602         DNPRINTF(SWM_D_MISC, "screenshot\n");
1603
1604         if (ss_enabled == 0)
1605                 return;
1606
1607         switch (args->id) {
1608         case SWM_ARG_ID_SS_ALL:
1609                 spawn_screenshot[1] = "full";
1610                 break;
1611         case SWM_ARG_ID_SS_WINDOW:
1612                 spawn_screenshot[1] = "window";
1613                 break;
1614         default:
1615                 return;
1616         }
1617         a.argv = spawn_screenshot;
1618         spawn(r, &a);
1619 }
1620
1621 void
1622 floating_toggle(struct swm_region *r, union arg *args)
1623 {
1624         struct ws_win   *win = cur_focus;
1625
1626         if (win == NULL)
1627                 return;
1628
1629         win->floating = !win->floating;
1630         win->manual = 0;
1631         stack();
1632         focus_win(win);
1633 }
1634
1635 /* key definitions */
1636 struct key {
1637         unsigned int            mod;
1638         KeySym                  keysym;
1639         void                    (*func)(struct swm_region *r, union arg *);
1640         union arg               args;
1641 } keys[] = {
1642         /* modifier             key     function                argument */
1643         { MODKEY,               XK_space,       cycle_layout,   {0} }, 
1644         { MODKEY | ShiftMask,   XK_space,       stack_config,   {.id = SWM_ARG_ID_STACKRESET} }, 
1645         { MODKEY,               XK_h,           stack_config,   {.id = SWM_ARG_ID_MASTERSHRINK} },
1646         { MODKEY,               XK_l,           stack_config,   {.id = SWM_ARG_ID_MASTERGROW} },
1647         { MODKEY,               XK_comma,       stack_config,   {.id = SWM_ARG_ID_MASTERADD} },
1648         { MODKEY,               XK_period,      stack_config,   {.id = SWM_ARG_ID_MASTERDEL} },
1649         { MODKEY,               XK_Return,      swapwin,        {.id = SWM_ARG_ID_SWAPMAIN} },
1650         { MODKEY,               XK_j,           focus,          {.id = SWM_ARG_ID_FOCUSNEXT} },
1651         { MODKEY,               XK_k,           focus,          {.id = SWM_ARG_ID_FOCUSPREV} },
1652         { MODKEY | ShiftMask,   XK_j,           swapwin,        {.id = SWM_ARG_ID_SWAPNEXT} },
1653         { MODKEY | ShiftMask,   XK_k,           swapwin,        {.id = SWM_ARG_ID_SWAPPREV} },
1654         { MODKEY | ShiftMask,   XK_Return,      spawn,          {.argv = spawn_term} },
1655         { MODKEY,               XK_p,           spawnmenu,      {.argv = spawn_menu} },
1656         { MODKEY | ShiftMask,   XK_q,           quit,           {0} },
1657         { MODKEY,               XK_q,           restart,        {0} },
1658         { MODKEY,               XK_m,           focus,          {.id = SWM_ARG_ID_FOCUSMAIN} },
1659         { MODKEY,               XK_1,           switchws,       {.id = 0} },
1660         { MODKEY,               XK_2,           switchws,       {.id = 1} },
1661         { MODKEY,               XK_3,           switchws,       {.id = 2} },
1662         { MODKEY,               XK_4,           switchws,       {.id = 3} },
1663         { MODKEY,               XK_5,           switchws,       {.id = 4} },
1664         { MODKEY,               XK_6,           switchws,       {.id = 5} },
1665         { MODKEY,               XK_7,           switchws,       {.id = 6} },
1666         { MODKEY,               XK_8,           switchws,       {.id = 7} },
1667         { MODKEY,               XK_9,           switchws,       {.id = 8} },
1668         { MODKEY,               XK_0,           switchws,       {.id = 9} },
1669         { MODKEY,               XK_Right,       cyclews,        {.id = SWM_ARG_ID_CYCLEWS_UP} }, 
1670         { MODKEY,               XK_Left,        cyclews,        {.id = SWM_ARG_ID_CYCLEWS_DOWN} }, 
1671         { MODKEY | ShiftMask,   XK_Right,       cyclescr,       {.id = SWM_ARG_ID_CYCLESC_UP} }, 
1672         { MODKEY | ShiftMask,   XK_Left,        cyclescr,       {.id = SWM_ARG_ID_CYCLESC_DOWN} }, 
1673         { MODKEY | ShiftMask,   XK_1,           send_to_ws,     {.id = 0} },
1674         { MODKEY | ShiftMask,   XK_2,           send_to_ws,     {.id = 1} },
1675         { MODKEY | ShiftMask,   XK_3,           send_to_ws,     {.id = 2} },
1676         { MODKEY | ShiftMask,   XK_4,           send_to_ws,     {.id = 3} },
1677         { MODKEY | ShiftMask,   XK_5,           send_to_ws,     {.id = 4} },
1678         { MODKEY | ShiftMask,   XK_6,           send_to_ws,     {.id = 5} },
1679         { MODKEY | ShiftMask,   XK_7,           send_to_ws,     {.id = 6} },
1680         { MODKEY | ShiftMask,   XK_8,           send_to_ws,     {.id = 7} },
1681         { MODKEY | ShiftMask,   XK_9,           send_to_ws,     {.id = 8} },
1682         { MODKEY | ShiftMask,   XK_0,           send_to_ws,     {.id = 9} },
1683         { MODKEY,               XK_b,           bar_toggle,     {0} },
1684         { MODKEY,               XK_Tab,         focus,          {.id = SWM_ARG_ID_FOCUSNEXT} },
1685         { MODKEY | ShiftMask,   XK_Tab,         focus,          {.id = SWM_ARG_ID_FOCUSPREV} },
1686         { MODKEY | ShiftMask,   XK_x,           wkill,          {0} },
1687         { MODKEY,               XK_s,           screenshot,     {.id = SWM_ARG_ID_SS_ALL} },
1688         { MODKEY | ShiftMask,   XK_s,           screenshot,     {.id = SWM_ARG_ID_SS_WINDOW} },
1689         { MODKEY,               XK_t,           floating_toggle,{0} },
1690         { MODKEY | ShiftMask,   XK_v,           version,        {0} },
1691         { MODKEY | ShiftMask,   XK_Delete,      spawn,          {.argv = spawn_lock} },
1692 };
1693
1694 void
1695 resize_window(struct ws_win *win, int center)
1696 {
1697         unsigned int            mask;
1698         XWindowChanges          wc;
1699         struct swm_region       *r;
1700
1701         r = root_to_region(win->wa.root);
1702         bzero(&wc, sizeof wc);
1703         mask = CWBorderWidth | CWWidth | CWHeight;
1704         wc.border_width = 1;
1705         wc.width = win->g.w;
1706         wc.height = win->g.h;
1707         if (center == SWM_ARG_ID_CENTER) {
1708                 wc.x = (WIDTH(r) - win->g.w) / 2;
1709                 wc.y = (HEIGHT(r) - win->g.h) / 2;
1710                 mask |= CWX | CWY;
1711         }
1712
1713         DNPRINTF(SWM_D_STACK, "resize_window: win %lu x %d y %d w %d h %d\n",
1714             win->id, wc.x, wc.y, wc.width, wc.height);
1715
1716         XConfigureWindow(display, win->id, mask, &wc);
1717         config_win(win);
1718 }
1719
1720 void
1721 resize(struct ws_win *win, union arg *args)
1722 {
1723         XEvent                  ev;
1724         Time                    time = 0;
1725
1726         DNPRINTF(SWM_D_MOUSE, "resize: win %d floating %d trans %d\n",
1727             win->id, win->floating, win->transient);
1728
1729         if (!(win->transient != 0 || win->floating != 0))
1730                 return;
1731
1732         if (XGrabPointer(display, win->id, False, MOUSEMASK, GrabModeAsync,
1733             GrabModeAsync, None, None /* cursor */, CurrentTime) != GrabSuccess)
1734                 return;
1735         XWarpPointer(display, None, win->id, 0, 0, 0, 0, win->g.w, win->g.h);
1736         do {
1737                 XMaskEvent(display, MOUSEMASK | ExposureMask |
1738                     SubstructureRedirectMask, &ev);
1739                 switch(ev.type) {
1740                 case ConfigureRequest:
1741                 case Expose:
1742                 case MapRequest:
1743                         handler[ev.type](&ev);
1744                         break;
1745                 case MotionNotify:
1746                         if (ev.xmotion.x < 0)
1747                                 ev.xmotion.x = 0;
1748                         if (ev.xmotion.y < 0)
1749                                 ev.xmotion.y = 0;
1750                         win->g.w = ev.xmotion.x;
1751                         win->g.h = ev.xmotion.y;
1752
1753                         /* not free, don't sync more than 60 times / second */
1754                         if ((ev.xmotion.time - time) > (1000 / 60) ) {
1755                                 time = ev.xmotion.time;
1756                                 XSync(display, False);
1757                                 resize_window(win, args->id);
1758                         }
1759                         break;
1760                 }
1761         } while (ev.type != ButtonRelease);
1762         if (time) {
1763                 XSync(display, False);
1764                 resize_window(win, args->id);
1765         }
1766         XWarpPointer(display, None, win->id, 0, 0, 0, 0, win->g.w - 1,
1767             win->g.h - 1);
1768         XUngrabPointer(display, CurrentTime);
1769
1770         /* drain events */
1771         while (XCheckMaskEvent(display, EnterWindowMask, &ev));
1772 }
1773
1774 void
1775 move_window(struct ws_win *win)
1776 {
1777         unsigned int            mask;
1778         XWindowChanges          wc;
1779         struct swm_region       *r;
1780
1781         r = root_to_region(win->wa.root);
1782         bzero(&wc, sizeof wc);
1783         mask = CWX | CWY;
1784         wc.x = win->g.x;
1785         wc.y = win->g.y;
1786
1787         DNPRINTF(SWM_D_STACK, "move_window: win %lu x %d y %d w %d h %d\n",
1788             win->id, wc.x, wc.y, wc.width, wc.height);
1789
1790         XConfigureWindow(display, win->id, mask, &wc);
1791         config_win(win);
1792 }
1793
1794 void
1795 move(struct ws_win *win, union arg *args)
1796 {
1797         XEvent                  ev;
1798         Time                    time = 0;
1799         int                     restack = 0;
1800
1801         DNPRINTF(SWM_D_MOUSE, "move: win %d floating %d trans %d\n",
1802             win->id, win->floating, win->transient);
1803
1804         if (win->floating == 0) {
1805                 win->floating = 1;
1806                 win->manual = 1;
1807                 restack = 1;
1808         }
1809
1810         if (XGrabPointer(display, win->id, False, MOUSEMASK, GrabModeAsync,
1811             GrabModeAsync, None, None /* cursor */, CurrentTime) != GrabSuccess)
1812                 return;
1813         XWarpPointer(display, None, win->id, 0, 0, 0, 0, 0, 0);
1814         do {
1815                 XMaskEvent(display, MOUSEMASK | ExposureMask |
1816                     SubstructureRedirectMask, &ev);
1817                 switch(ev.type) {
1818                 case ConfigureRequest:
1819                 case Expose:
1820                 case MapRequest:
1821                         handler[ev.type](&ev);
1822                         break;
1823                 case MotionNotify:
1824                         win->g.x = ev.xmotion.x_root;
1825                         win->g.y = ev.xmotion.y_root;
1826
1827                         /* not free, don't sync more than 60 times / second */
1828                         if ((ev.xmotion.time - time) > (1000 / 60) ) {
1829                                 time = ev.xmotion.time;
1830                                 XSync(display, False);
1831                                 move_window(win);
1832                         }
1833                         break;
1834                 }
1835         } while (ev.type != ButtonRelease);
1836         if (time) {
1837                 XSync(display, False);
1838                 move_window(win);
1839         }
1840         XWarpPointer(display, None, win->id, 0, 0, 0, 0, 0, 0);
1841         XUngrabPointer(display, CurrentTime);
1842         if (restack)
1843                 stack();
1844
1845         /* drain events */
1846         while (XCheckMaskEvent(display, EnterWindowMask, &ev));
1847 }
1848
1849 /* mouse */
1850 enum { client_click, root_click };
1851 struct button {
1852         unsigned int            action;
1853         unsigned int            mask;
1854         unsigned int            button;
1855         void                    (*func)(struct ws_win *, union arg *);
1856         union arg               args;
1857 } buttons[] = {
1858           /* action             key             mouse button    func            args */
1859         { client_click,         MODKEY,         Button3,        resize,         {.id = SWM_ARG_ID_DONTCENTER} },
1860         { client_click,         MODKEY | ShiftMask, Button3,    resize,         {.id = SWM_ARG_ID_CENTER} },
1861         { client_click,         MODKEY,         Button1,        move,           {0} },
1862 };
1863
1864 void
1865 updatenumlockmask(void)
1866 {
1867         unsigned int            i, j;
1868         XModifierKeymap         *modmap;
1869
1870         DNPRINTF(SWM_D_MISC, "updatenumlockmask\n");
1871         numlockmask = 0;
1872         modmap = XGetModifierMapping(display);
1873         for (i = 0; i < 8; i++)
1874                 for (j = 0; j < modmap->max_keypermod; j++)
1875                         if (modmap->modifiermap[i * modmap->max_keypermod + j]
1876                           == XKeysymToKeycode(display, XK_Num_Lock))
1877                                 numlockmask = (1 << i);
1878
1879         XFreeModifiermap(modmap);
1880 }
1881
1882 void
1883 grabkeys(void)
1884 {
1885         unsigned int            i, j, k;
1886         KeyCode                 code;
1887         unsigned int            modifiers[] =
1888             { 0, LockMask, numlockmask, numlockmask | LockMask };
1889
1890         DNPRINTF(SWM_D_MISC, "grabkeys\n");
1891         updatenumlockmask();
1892
1893         for (k = 0; k < ScreenCount(display); k++) {
1894                 if (TAILQ_EMPTY(&screens[k].rl))
1895                         continue;
1896                 XUngrabKey(display, AnyKey, AnyModifier, screens[k].root);
1897                 for (i = 0; i < LENGTH(keys); i++) {
1898                         if ((code = XKeysymToKeycode(display, keys[i].keysym)))
1899                                 for (j = 0; j < LENGTH(modifiers); j++)
1900                                         XGrabKey(display, code,
1901                                             keys[i].mod | modifiers[j],
1902                                             screens[k].root, True,
1903                                             GrabModeAsync, GrabModeAsync);
1904                 }
1905         }
1906 }
1907
1908 void
1909 grabbuttons(struct ws_win *win, int focused)
1910 {
1911         unsigned int            i, j;
1912         unsigned int            modifiers[] =
1913             { 0, LockMask, numlockmask, numlockmask|LockMask };
1914
1915         updatenumlockmask();
1916         XUngrabButton(display, AnyButton, AnyModifier, win->id);
1917         if(focused) {
1918                 for (i = 0; i < LENGTH(buttons); i++)
1919                         if (buttons[i].action == client_click)
1920                                 for (j = 0; j < LENGTH(modifiers); j++)
1921                                         XGrabButton(display, buttons[i].button,
1922                                             buttons[i].mask | modifiers[j],
1923                                             win->id, False, BUTTONMASK,
1924                                             GrabModeAsync, GrabModeSync, None,
1925                                             None);
1926         } else
1927                 XGrabButton(display, AnyButton, AnyModifier, win->id, False,
1928                     BUTTONMASK, GrabModeAsync, GrabModeSync, None, None);
1929 }
1930
1931 void
1932 expose(XEvent *e)
1933 {
1934         DNPRINTF(SWM_D_EVENT, "expose: window: %lu\n", e->xexpose.window);
1935 }
1936
1937 void
1938 keypress(XEvent *e)
1939 {
1940         unsigned int            i;
1941         KeySym                  keysym;
1942         XKeyEvent               *ev = &e->xkey;
1943
1944         DNPRINTF(SWM_D_EVENT, "keypress: window: %lu\n", ev->window);
1945
1946         keysym = XKeycodeToKeysym(display, (KeyCode)ev->keycode, 0);
1947         for (i = 0; i < LENGTH(keys); i++)
1948                 if (keysym == keys[i].keysym
1949                    && CLEANMASK(keys[i].mod) == CLEANMASK(ev->state)
1950                    && keys[i].func)
1951                         keys[i].func(root_to_region(ev->root),
1952                             &(keys[i].args));
1953 }
1954
1955 void
1956 buttonpress(XEvent *e)
1957 {
1958         XButtonPressedEvent     *ev = &e->xbutton;
1959
1960         struct ws_win           *win;
1961         int                     i, action;
1962
1963         DNPRINTF(SWM_D_EVENT, "buttonpress: window: %lu\n", ev->window);
1964
1965         action = root_click;
1966         if ((win = find_window(ev->window)) == NULL)
1967                 return;
1968         else {
1969                 focus_win(win);
1970                 action = client_click;
1971         }
1972
1973         for (i = 0; i < LENGTH(buttons); i++)
1974                 if (action == buttons[i].action && buttons[i].func &&
1975                     buttons[i].button == ev->button &&
1976                     CLEANMASK(buttons[i].mask) == CLEANMASK(ev->state))
1977                         buttons[i].func(win, &buttons[i].args);
1978 }
1979
1980 void
1981 set_win_state(struct ws_win *win, long state)
1982 {
1983         long                    data[] = {state, None};
1984
1985         DNPRINTF(SWM_D_EVENT, "set_win_state: window: %lu\n", win->id);
1986
1987         XChangeProperty(display, win->id, astate, astate, 32, PropModeReplace,
1988             (unsigned char *)data, 2);
1989 }
1990
1991 struct ws_win *
1992 manage_window(Window id)
1993 {
1994         Window                  trans;
1995         struct workspace        *ws;
1996         struct ws_win           *win;
1997         int                     format, i, ws_idx;
1998         unsigned long           nitems, bytes;
1999         Atom                    ws_idx_atom = 0, type;
2000         unsigned char           ws_idx_str[SWM_PROPLEN], *prop = NULL;
2001         struct swm_region       *r;
2002         long                    mask;
2003         const char              *errstr;
2004
2005         if ((win = find_window(id)) != NULL)
2006                         return (win);   /* already being managed */
2007
2008         if ((win = calloc(1, sizeof(struct ws_win))) == NULL)
2009                 errx(1, "calloc: failed to allocate memory for new window");
2010
2011         /* Get all the window data in one shot */
2012         ws_idx_atom = XInternAtom(display, "_SWM_WS", False);
2013         if (ws_idx_atom)
2014                 XGetWindowProperty(display, id, ws_idx_atom, 0, SWM_PROPLEN,
2015                     False, XA_STRING, &type, &format, &nitems, &bytes, &prop);
2016         XGetWindowAttributes(display, id, &win->wa);
2017         XGetTransientForHint(display, id, &trans);
2018         XGetWMNormalHints(display, id, &win->sh, &mask); /* XXX function? */
2019         if (trans) {
2020                 win->transient = trans;
2021                 DNPRINTF(SWM_D_MISC, "manage_window: win %u transient %u\n",
2022                     (unsigned)win->id, win->transient);
2023         }
2024         
2025         /*
2026          * Figure out where to put the window. If it was previously assigned to
2027          * a workspace (either by spawn() or manually moving), and isn't
2028          * transient, * put it in the same workspace
2029          */
2030         r = root_to_region(win->wa.root);
2031         if (prop && win->transient == 0) {
2032                 DNPRINTF(SWM_D_PROP, "got property _SWM_WS=%s\n", prop);
2033                 ws_idx = strtonum(prop, 0, 9, &errstr);
2034                 if (errstr) {
2035                         DNPRINTF(SWM_D_EVENT, "window idx is %s: %s",
2036                             errstr, prop);
2037                 }
2038                 ws = &r->s->ws[ws_idx];
2039         } else
2040                 ws = r->ws;
2041
2042         /* set up the window layout */
2043         win->id = id;
2044         win->ws = ws;
2045         win->s = r->s;  /* this never changes */
2046         TAILQ_INSERT_TAIL(&ws->winlist, win, entry);
2047
2048         win->g.w = win->wa.width;
2049         win->g.h = win->wa.height;
2050         win->g.x = win->wa.x;
2051         win->g.y = win->wa.y;
2052
2053         /* Set window properties so we can remember this after reincarnation */
2054         if (ws_idx_atom && prop == NULL &&
2055             snprintf(ws_idx_str, SWM_PROPLEN, "%d", ws->idx) < SWM_PROPLEN) {
2056                 DNPRINTF(SWM_D_PROP, "setting property _SWM_WS to %s\n",
2057                     ws_idx_str);
2058                 XChangeProperty(display, win->id, ws_idx_atom, XA_STRING, 8,
2059                     PropModeReplace, ws_idx_str, SWM_PROPLEN);
2060         }
2061         XFree(prop);
2062
2063         /*
2064         fprintf(stderr, "manage window: %d x %d y %d w %d h %d\n", win->id, win->g.x, win->g.y, win->g.w, win->g.h);
2065         */
2066
2067         if (XGetClassHint(display, win->id, &win->ch)) {
2068                 DNPRINTF(SWM_D_CLASS, "class: %s name: %s\n",
2069                     win->ch.res_class, win->ch.res_name);
2070                 for (i = 0; quirks[i].class != NULL && quirks[i].name != NULL &&
2071                     quirks[i].quirk != 0; i++){
2072                         if (!strcmp(win->ch.res_class, quirks[i].class) &&
2073                             !strcmp(win->ch.res_name, quirks[i].name)) {
2074                                 DNPRINTF(SWM_D_CLASS, "found: %s name: %s\n",
2075                                     win->ch.res_class, win->ch.res_name);
2076                                 if (quirks[i].quirk & SWM_Q_FLOAT)
2077                                         win->floating = 1;
2078                         }
2079                 }
2080         }
2081
2082         XSelectInput(display, id, EnterWindowMask | FocusChangeMask |
2083             PropertyChangeMask | StructureNotifyMask);
2084
2085         set_win_state(win, NormalState);
2086
2087         /* make new win focused */
2088         focus_win(win);
2089
2090         return (win);
2091 }
2092
2093 void
2094 configurerequest(XEvent *e)
2095 {
2096         XConfigureRequestEvent  *ev = &e->xconfigurerequest;
2097         struct ws_win           *win;
2098         int                     new = 0;
2099         XWindowChanges          wc;
2100
2101         if ((win = find_window(ev->window)) == NULL)
2102                 new = 1;
2103
2104         if (new) {
2105                 DNPRINTF(SWM_D_EVENT, "configurerequest: new window: %lu\n",
2106                     ev->window);
2107                 /*
2108                 fprintf(stderr, "configurerequest: new window: %lu x %d y %d w %d h %d bw %d s %d sm %d\n",
2109                     ev->window, ev->x, ev->y, ev->width, ev->height, ev->border_width, ev->above, ev->detail);
2110                 */
2111                 bzero(&wc, sizeof wc);
2112                 wc.x = ev->x;
2113                 wc.y = ev->y;
2114                 wc.width = ev->width;
2115                 wc.height = ev->height;
2116                 wc.border_width = ev->border_width;
2117                 wc.sibling = ev->above;
2118                 wc.stack_mode = ev->detail;
2119                 XConfigureWindow(display, ev->window, ev->value_mask, &wc);
2120         } else {
2121                 /*
2122                 fprintf(stderr, "configurerequest: change window: %lu\n",
2123                     ev->window);
2124                 */
2125                 DNPRINTF(SWM_D_EVENT, "configurerequest: change window: %lu\n",
2126                     ev->window);
2127                 if (win->floating) {
2128                         if (ev->value_mask & CWX)
2129                                 win->g.x = ev->x;
2130                         if (ev->value_mask & CWY)
2131                                 win->g.y = ev->y;
2132                         if (ev->value_mask & CWWidth)
2133                                 win->g.w = ev->width;
2134                         if (ev->value_mask & CWHeight)
2135                                 win->g.h = ev->height;
2136                         if (win->ws->r != NULL) {
2137                                 /* this seems to be full screen */
2138                                 if (win->g.w >= WIDTH(win->ws->r)) {
2139                                         win->g.x = -1;
2140                                         win->g.w = WIDTH(win->ws->r);
2141                                         ev->value_mask |= CWX | CWWidth;
2142                                 }
2143                                 if (win->g.h >= HEIGHT(win->ws->r)) {
2144                                         /* kill border */
2145                                         win->g.y = -1;
2146                                         win->g.h = HEIGHT(win->ws->r);
2147                                         ev->value_mask |= CWY | CWHeight;
2148                                 }
2149                         }
2150                         if ((ev->value_mask & (CWX|CWY)) &&
2151                             !(ev->value_mask & (CWWidth|CWHeight)))
2152                                 config_win(win);
2153                         XMoveResizeWindow(display, win->id,
2154                             win->g.x, win->g.y, win->g.w, win->g.h);
2155                 } else
2156                         config_win(win);
2157         }
2158 }
2159
2160 void
2161 configurenotify(XEvent *e)
2162 {
2163         DNPRINTF(SWM_D_EVENT, "configurenotify: window: %lu\n",
2164             e->xconfigure.window);
2165 }
2166
2167 void
2168 destroynotify(XEvent *e)
2169 {
2170         struct ws_win           *win;
2171         XDestroyWindowEvent     *ev = &e->xdestroywindow;
2172         struct workspace        *ws;
2173
2174         DNPRINTF(SWM_D_EVENT, "destroynotify: window %lu\n", ev->window);
2175
2176         if ((win = find_window(ev->window)) != NULL) {
2177                 ws = win->ws;
2178                 /* find a window to focus */
2179                 if (ws->focus == win)
2180                         ws->focus = TAILQ_PREV(win, ws_win_list, entry);
2181                 if (ws->focus == NULL)
2182                         ws->focus = TAILQ_FIRST(&ws->winlist);
2183                 if (ws->focus == NULL || ws->focus == win) {
2184                         ws->focus = NULL;
2185                         unfocus_all();
2186                 } else
2187                         focus_win(ws->focus);
2188                 TAILQ_REMOVE(&ws->winlist, win, entry);
2189                 set_win_state(win, WithdrawnState);
2190                 if (win->ch.res_class)
2191                         XFree(win->ch.res_class);
2192                 if (win->ch.res_name)
2193                         XFree(win->ch.res_name);
2194                 free(win);
2195                 stack();
2196         }
2197 }
2198
2199 void
2200 enternotify(XEvent *e)
2201 {
2202         XCrossingEvent          *ev = &e->xcrossing;
2203         struct ws_win           *win;
2204
2205         DNPRINTF(SWM_D_EVENT, "enternotify: window: %lu\n", ev->window);
2206
2207         if (ignore_enter) {
2208                 /* eat event(r) to prevent autofocus */
2209                 ignore_enter--;
2210                 return;
2211         }
2212
2213         if ((win = find_window(ev->window)) != NULL)
2214                 focus_win(win);
2215 }
2216
2217 void
2218 focusin(XEvent *e)
2219 {
2220         DNPRINTF(SWM_D_EVENT, "focusin: window: %lu\n", e->xfocus.window);
2221 }
2222
2223 void
2224 mappingnotify(XEvent *e)
2225 {
2226         XMappingEvent           *ev = &e->xmapping;
2227
2228         DNPRINTF(SWM_D_EVENT, "mappingnotify: window: %lu\n", ev->window);
2229
2230         XRefreshKeyboardMapping(ev);
2231         if (ev->request == MappingKeyboard)
2232                 grabkeys();
2233 }
2234
2235 void
2236 maprequest(XEvent *e)
2237 {
2238         XMapRequestEvent        *ev = &e->xmaprequest;
2239         XWindowAttributes       wa;
2240
2241         DNPRINTF(SWM_D_EVENT, "maprequest: window: %lu\n",
2242             e->xmaprequest.window);
2243
2244         if (!XGetWindowAttributes(display, ev->window, &wa))
2245                 return;
2246         if (wa.override_redirect)
2247                 return;
2248         manage_window(e->xmaprequest.window);
2249         stack();
2250 }
2251
2252 void
2253 propertynotify(XEvent *e)
2254 {
2255         struct ws_win           *win;
2256         XPropertyEvent          *ev = &e->xproperty;
2257
2258         DNPRINTF(SWM_D_EVENT, "propertynotify: window: %lu\n",
2259             ev->window);
2260
2261         if (ev->state == PropertyDelete)
2262                 return; /* ignore */
2263         win = find_window(ev->window);
2264         if (win == NULL)
2265                 return;
2266
2267         switch (ev->atom) {
2268         case XA_WM_NORMAL_HINTS:
2269 #if 0
2270                 long            mask;
2271                 XGetWMNormalHints(display, win->id, &win->sh, &mask);
2272                 fprintf(stderr, "normal hints: flag 0x%x\n", win->sh.flags);
2273                 if (win->sh.flags & PMinSize) {
2274                         win->g.w = win->sh.min_width;
2275                         win->g.h = win->sh.min_height;
2276                         fprintf(stderr, "min %d %d\n", win->g.w, win->g.h);
2277                 }
2278                 XMoveResizeWindow(display, win->id,
2279                     win->g.x, win->g.y, win->g.w, win->g.h);
2280 #endif
2281                 break;
2282         default:
2283                 break;
2284         }
2285 }
2286
2287 void
2288 unmapnotify(XEvent *e)
2289 {
2290         DNPRINTF(SWM_D_EVENT, "unmapnotify: window: %lu\n", e->xunmap.window);
2291 }
2292
2293 void
2294 visibilitynotify(XEvent *e)
2295 {
2296         int                     i;
2297         struct swm_region       *r;
2298
2299         DNPRINTF(SWM_D_EVENT, "visibilitynotify: window: %lu\n",
2300             e->xvisibility.window);
2301         if (e->xvisibility.state == VisibilityUnobscured)
2302                 for (i = 0; i < ScreenCount(display); i++) 
2303                         TAILQ_FOREACH(r, &screens[i].rl, entry)
2304                                 if (e->xvisibility.window == r->bar_window)
2305                                         bar_update();
2306 }
2307
2308 int
2309 xerror_start(Display *d, XErrorEvent *ee)
2310 {
2311         other_wm = 1;
2312         return (-1);
2313 }
2314
2315 int
2316 xerror(Display *d, XErrorEvent *ee)
2317 {
2318         /* fprintf(stderr, "error: %p %p\n", display, ee); */
2319         return (-1);
2320 }
2321
2322 int
2323 active_wm(void)
2324 {
2325         other_wm = 0;
2326         xerrorxlib = XSetErrorHandler(xerror_start);
2327
2328         /* this causes an error if some other window manager is running */
2329         XSelectInput(display, DefaultRootWindow(display),
2330             SubstructureRedirectMask);
2331         XSync(display, False);
2332         if (other_wm)
2333                 return (1);
2334
2335         XSetErrorHandler(xerror);
2336         XSync(display, False);
2337         return (0);
2338 }
2339
2340 long
2341 getstate(Window w)
2342 {
2343         int                     format, status;
2344         long                    result = -1;
2345         unsigned char           *p = NULL;
2346         unsigned long           n, extra;
2347         Atom                    real;
2348
2349         astate = XInternAtom(display, "WM_STATE", False);
2350         status = XGetWindowProperty(display, w, astate, 0L, 2L, False, astate,
2351             &real, &format, &n, &extra, (unsigned char **)&p);
2352         if (status != Success)
2353                 return (-1);
2354         if (n != 0)
2355                 result = *p;
2356         XFree(p);
2357         return (result);
2358 }
2359
2360 void
2361 new_region(struct swm_screen *s, struct workspace *ws,
2362     int x, int y, int w, int h)
2363 {
2364         struct swm_region       *r;
2365
2366         DNPRINTF(SWM_D_MISC, "new region on screen %d: %dx%d (%d, %d)\n",
2367              s->idx, x, y, w, h);
2368
2369         if ((r = calloc(1, sizeof(struct swm_region))) == NULL)
2370                 errx(1, "calloc: failed to allocate memory for screen");
2371
2372         X(r) = x;
2373         Y(r) = y;
2374         WIDTH(r) = w;
2375         HEIGHT(r) = h;
2376         r->s = s;
2377         r->ws = ws;
2378         ws->r = r;
2379         TAILQ_INSERT_TAIL(&s->rl, r, entry);
2380         bar_setup(r);
2381 }
2382
2383 void
2384 setup_screens(void)
2385 {
2386 #ifdef SWM_XRR_HAS_CRTC
2387         XRRCrtcInfo             *ci;
2388         XRRScreenResources      *sr;
2389         int                     c;
2390 #endif /* SWM_XRR_HAS_CRTC */
2391         Window                  d1, d2, *wins = NULL;
2392         XWindowAttributes       wa;
2393         struct swm_region       *r;
2394         unsigned int            no;
2395         int                     errorbase, major, minor;
2396         int                     ncrtc = 0, w = 0;
2397         int                     i, j, k;
2398         struct workspace        *ws;
2399         int                     ws_idx_atom;
2400
2401
2402         if ((screens = calloc(ScreenCount(display),
2403              sizeof(struct swm_screen))) == NULL)
2404                 errx(1, "calloc: screens");
2405
2406         ws_idx_atom = XInternAtom(display, "_SWM_WS", False);
2407         
2408
2409         /* map physical screens */
2410         for (i = 0; i < ScreenCount(display); i++) {
2411                 DNPRINTF(SWM_D_WS, "setup_screens: init screen %d\n", i);
2412                 screens[i].idx = i;
2413                 TAILQ_INIT(&screens[i].rl);
2414                 screens[i].root = RootWindow(display, i);
2415
2416                 /* set default colors */
2417                 setscreencolor("red", i + 1, SWM_S_COLOR_FOCUS);
2418                 setscreencolor("rgb:88/88/88", i + 1, SWM_S_COLOR_UNFOCUS);
2419                 setscreencolor("rgb:00/80/80", i + 1, SWM_S_COLOR_BAR_BORDER);
2420                 setscreencolor("black", i + 1, SWM_S_COLOR_BAR);
2421                 setscreencolor("rgb:a0/a0/a0", i + 1, SWM_S_COLOR_BAR_FONT);
2422
2423                 /* init all workspaces */
2424                 for (j = 0; j < SWM_WS_MAX; j++) {
2425                         ws = &screens[i].ws[j];
2426                         ws->idx = j;
2427                         ws->restack = 1;
2428                         ws->focus = NULL;
2429                         ws->r = NULL;
2430                         TAILQ_INIT(&ws->winlist);
2431
2432                         for (k = 0; layouts[k].l_stack != NULL; k++)
2433                                 if (layouts[k].l_config != NULL)
2434                                         layouts[k].l_config(ws,
2435                                             SWM_ARG_ID_STACKINIT);
2436                         ws->cur_layout = &layouts[0];
2437                 }
2438
2439                 /* map virtual screens onto physical screens */
2440                 screens[i].xrandr_support = XRRQueryExtension(display,
2441                     &xrandr_eventbase, &errorbase);
2442                 if (screens[i].xrandr_support)
2443                         if (XRRQueryVersion(display, &major, &minor) &&
2444                             major < 1)
2445                                 screens[i].xrandr_support = 0;
2446
2447 #if 0   /* not ready for dynamic screen changes */
2448                 if (screens[i].xrandr_support)
2449                         XRRSelectInput(display,
2450                             screens[r->s].root,
2451                             RRScreenChangeNotifyMask);
2452 #endif
2453
2454                 /* grab existing windows (before we build the bars)*/
2455                 if (!XQueryTree(display, screens[i].root, &d1, &d2, &wins, &no))
2456                         continue;
2457
2458 #ifdef SWM_XRR_HAS_CRTC
2459                 sr = XRRGetScreenResources(display, screens[i].root);
2460                 if (sr == NULL)
2461                         new_region(&screens[i], &screens[i].ws[w],
2462                             0, 0, DisplayWidth(display, i),
2463                             DisplayHeight(display, i)); 
2464                 else 
2465                         ncrtc = sr->ncrtc;
2466
2467                 for (c = 0, ci = NULL; c < ncrtc; c++) {
2468                         ci = XRRGetCrtcInfo(display, sr, sr->crtcs[c]);
2469                         if (ci->noutput == 0)
2470                                 continue;
2471
2472                         if (ci != NULL && ci->mode == None)
2473                                 new_region(&screens[i], &screens[i].ws[w], 0, 0,
2474                                     DisplayWidth(display, i),
2475                                     DisplayHeight(display, i)); 
2476                         else
2477                                 new_region(&screens[i], &screens[i].ws[w],
2478                                     ci->x, ci->y, ci->width, ci->height);
2479                         w++;
2480                 }
2481                 if (ci)
2482                         XRRFreeCrtcInfo(ci);
2483                 XRRFreeScreenResources(sr);
2484 #else
2485                 new_region(&screens[i], &screens[i].ws[w], 0, 0,
2486                     DisplayWidth(display, i),
2487                     DisplayHeight(display, i)); 
2488 #endif /* SWM_XRR_HAS_CRTC */
2489
2490                 /* attach windows to a region */
2491                 /* normal windows */
2492                 if ((r = TAILQ_FIRST(&screens[i].rl)) == NULL)
2493                         errx(1, "no regions on screen %d", i);
2494
2495                 for (i = 0; i < no; i++) {
2496                         XGetWindowAttributes(display, wins[i], &wa);
2497                         if (!XGetWindowAttributes(display, wins[i], &wa) ||
2498                             wa.override_redirect ||
2499                             XGetTransientForHint(display, wins[i], &d1))
2500                                 continue;
2501
2502                         if (wa.map_state == IsViewable ||
2503                             getstate(wins[i]) == NormalState)
2504                                 manage_window(wins[i]);
2505                 }
2506                 /* transient windows */
2507                 for (i = 0; i < no; i++) {
2508                         if (!XGetWindowAttributes(display, wins[i], &wa))
2509                                 continue;
2510
2511                         if (XGetTransientForHint(display, wins[i], &d1) &&
2512                             (wa.map_state == IsViewable || getstate(wins[i]) ==
2513                             NormalState))
2514                                 manage_window(wins[i]);
2515                 }
2516                 if (wins) {
2517                         XFree(wins);
2518                         wins = NULL;
2519                 }
2520         }
2521 }
2522
2523 int
2524 main(int argc, char *argv[])
2525 {
2526         struct passwd           *pwd;
2527         char                    conf[PATH_MAX], *cfile = NULL;
2528         struct stat             sb;
2529         XEvent                  e;
2530         int                     xfd;
2531         fd_set                  rd;
2532
2533         start_argv = argv;
2534         fprintf(stderr, "Welcome to scrotwm V%s cvs tag: %s\n",
2535             SWM_VERSION, cvstag);
2536         if (!setlocale(LC_CTYPE, "") || !XSupportsLocale())
2537                 warnx("no locale support");
2538
2539         if (!(display = XOpenDisplay(0)))
2540                 errx(1, "can not open display");
2541
2542         if (active_wm())
2543                 errx(1, "other wm running");
2544
2545         astate = XInternAtom(display, "WM_STATE", False);
2546
2547         /* look for local and global conf file */
2548         pwd = getpwuid(getuid());
2549         if (pwd == NULL)
2550                 errx(1, "invalid user %d", getuid());
2551
2552         setup_screens();
2553
2554         snprintf(conf, sizeof conf, "%s/.%s", pwd->pw_dir, SWM_CONF_FILE);
2555         if (stat(conf, &sb) != -1) {
2556                 if (S_ISREG(sb.st_mode))
2557                         cfile = conf;
2558         } else {
2559                 /* try global conf file */
2560                 snprintf(conf, sizeof conf, "/etc/%s", SWM_CONF_FILE);
2561                 if (!stat(conf, &sb))
2562                         if (S_ISREG(sb.st_mode))
2563                                 cfile = conf;
2564         }
2565         if (cfile)
2566                 conf_load(cfile);
2567         bar_refresh();
2568
2569         /* ws[0].focus = TAILQ_FIRST(&ws[0].winlist); */
2570
2571         grabkeys();
2572         stack();
2573
2574         xfd = ConnectionNumber(display);
2575         while (running) {
2576                 FD_ZERO(&rd);
2577                 FD_SET(xfd, &rd);
2578                 if (select(xfd + 1, &rd, NULL, NULL, NULL) == -1)
2579                         if (errno != EINTR)
2580                                 errx(1, "select failed");
2581                 if (bar_alarm) {
2582                         bar_alarm = 0;
2583                         bar_update();
2584                 }
2585                 while(XPending(display)) {
2586                         XNextEvent(display, &e);
2587                         if (handler[e.type])
2588                                 handler[e.type](&e);
2589                 }
2590         }
2591
2592         XCloseDisplay(display);
2593
2594         return (0);
2595 }