JasonWoof Got questions, comments, patches, etc.? Contact Jason Woofenden
7e17a1b9c28813319a913df1ea4a53ba17e95319
[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 #define SWM_VERSION     "0.5"
52
53 #include <stdio.h>
54 #include <stdlib.h>
55 #include <err.h>
56 #include <locale.h>
57 #include <unistd.h>
58 #include <time.h>
59 #include <signal.h>
60 #include <string.h>
61 #include <util.h>
62 #include <pwd.h>
63
64 #include <sys/types.h>
65 #include <sys/stat.h>
66 #include <sys/wait.h>
67 #include <sys/queue.h>
68 #include <sys/param.h>
69
70 #include <X11/cursorfont.h>
71 #include <X11/keysym.h>
72 #include <X11/Xatom.h>
73 #include <X11/Xlib.h>
74 #include <X11/Xproto.h>
75 #include <X11/Xutil.h>
76 #include <X11/extensions/Xrandr.h>
77
78 #if RANDR_MAJOR < 1
79 #  error XRandR versions less than 1.0 are not supported
80 #endif
81
82 #if RANDR_MAJOR >= 1
83 #if RANDR_MINOR >= 2
84 #define SWM_XRR_HAS_CRTC
85 #endif
86 #endif
87
88 /* #define SWM_DEBUG */
89 #ifdef SWM_DEBUG
90 #define DPRINTF(x...)           do { if (swm_debug) fprintf(stderr, x); } while(0)
91 #define DNPRINTF(n,x...)        do { if (swm_debug & n) fprintf(stderr, x); } while(0)
92 #define SWM_D_MISC              0x0001
93 #define SWM_D_EVENT             0x0002
94 #define SWM_D_WS                0x0004
95 #define SWM_D_FOCUS             0x0008
96 #define SWM_D_MOVE              0x0010
97 #define SWM_D_STACK             0x0020
98
99 u_int32_t               swm_debug = 0
100                             | SWM_D_MISC
101                             | SWM_D_EVENT
102                             | SWM_D_WS
103                             | SWM_D_FOCUS
104                             | SWM_D_MOVE
105                             | SWM_D_STACK
106                             ;
107 #else
108 #define DPRINTF(x...)
109 #define DNPRINTF(n,x...)
110 #endif
111
112 #define LENGTH(x)               (sizeof x / sizeof x[0])
113 #define MODKEY                  Mod1Mask
114 #define CLEANMASK(mask)         (mask & ~(numlockmask | LockMask))
115
116 #define X(r)            (r)->g.x        
117 #define Y(r)            (r)->g.y
118 #define WIDTH(r)        (r)->g.w        
119 #define HEIGHT(r)       (r)->g.h
120
121 char                    **start_argv;
122 Atom                    astate;
123 int                     (*xerrorxlib)(Display *, XErrorEvent *);
124 int                     other_wm;
125 int                     running = 1;
126 int                     xrandr_eventbase;
127 int                     ignore_enter = 0;
128 unsigned int            numlockmask = 0;
129 Display                 *display;
130
131 /* dialog windows */
132 double                  dialog_ratio = .6;
133 /* status bar */
134 int                     bar_enabled = 1;
135 int                     bar_height = 0;
136 GC                      bar_gc;
137 XGCValues               bar_gcv;
138 XFontStruct             *bar_fs;
139 char                    bar_text[128];
140 char                    *bar_fonts[] = {
141                             "-*-terminus-*-*-*-*-*-*-*-*-*-*-*-*",
142                             "-*-times-medium-r-*-*-*-*-*-*-*-*-*-*",
143                             NULL
144 };
145
146 /* terminal + args */
147 char                            *spawn_term[] = { "xterm", NULL };
148 char                            *spawn_menu[] = { "dmenu_run", NULL };
149
150 /* layout manager data */
151 struct swm_geometry {
152         int                     x;
153         int                     y;
154         int                     w;
155         int                     h;
156 };
157
158 struct swm_screen;
159 struct workspace;
160
161 /* virtual "screens" */
162 struct swm_region {
163         TAILQ_ENTRY(swm_region) entry;
164         struct swm_geometry     g;
165         Window                  bar_window;
166         struct workspace        *ws;    /* current workspace on this region */
167         struct swm_screen       *s;     /* screen idx */
168 }; 
169 TAILQ_HEAD(swm_region_list, swm_region);
170
171
172 struct ws_win {
173         TAILQ_ENTRY(ws_win)     entry;
174         Window                  id;
175         struct swm_geometry     g;
176         int                     focus;
177         int                     floating;
178         int                     transient;
179         struct workspace        *ws;    /* always valid */
180         struct swm_screen       *s;     /* always valid, never changes */
181         XWindowAttributes       wa;
182 };
183 TAILQ_HEAD(ws_win_list, ws_win);
184
185 /* layout handlers */
186 void    stack(void);
187 void    vertical_init(struct workspace *);
188 void    vertical_resize(int);
189 void    vertical_stack(struct workspace *, struct swm_geometry *);
190 void    horizontal_init(struct workspace *);
191 void    horizontal_resize(int);
192 void    horizontal_stack(struct workspace *, struct swm_geometry *);
193 void    max_init(struct workspace *);
194 void    max_stack(struct workspace *, struct swm_geometry *);
195
196 struct layout {
197         void            (*l_init)(struct workspace *);  /* init/reset */
198         void            (*l_stack)(struct workspace *, struct swm_geometry *);
199         void            (*l_resize)(int);
200 } layouts[] =  {
201         /* init                 stack,                  resize */
202         { vertical_init,        vertical_stack,         vertical_resize},
203         { horizontal_init,      horizontal_stack,       horizontal_resize},
204         { NULL,                 max_stack,              NULL},
205         { NULL,                 NULL,                   NULL},
206 };
207
208 #define SWM_H_SLICE             (32)
209 #define SWM_V_SLICE             (32)
210
211
212 /* define work spaces */
213 struct workspace {
214         int                     idx;            /* workspace index */
215         int                     restack;        /* restack on switch */
216         struct layout           *cur_layout;    /* current layout handlers */
217         struct ws_win           *focus;         /* may be NULL */
218         struct swm_region       *r;             /* may be NULL */
219         struct ws_win_list      winlist;        /* list of windows in ws */
220
221         /* stacker state */
222         struct {
223                                 int horizontal_msize;
224                                 int vertical_msize;
225         } l_state;
226 };
227
228 /* physical screen mapping */
229 #define SWM_WS_MAX              (10)            /* XXX Too small? */
230 struct swm_screen {
231         int                     idx;            /* screen index */
232         struct swm_region_list  rl;     /* list of regions on this screen */
233         Window                  root;
234         int                     xrandr_support;
235         struct workspace        ws[SWM_WS_MAX];
236
237         /* colors */
238         unsigned long           bar_border;
239         unsigned long           bar_color;
240         unsigned long           bar_font_color;
241         unsigned long           color_focus;            /* XXX should this be per ws? */
242         unsigned long           color_unfocus;
243 };
244 struct swm_screen       *screens;
245 int                     num_screens;
246
247 struct ws_win           *cur_focus = NULL;
248
249 /* args to functions */
250 union arg {
251         int                     id;
252 #define SWM_ARG_ID_FOCUSNEXT    (0)
253 #define SWM_ARG_ID_FOCUSPREV    (1)
254 #define SWM_ARG_ID_FOCUSMAIN    (2)
255 #define SWM_ARG_ID_SWAPNEXT     (3)
256 #define SWM_ARG_ID_SWAPPREV     (4)
257 #define SWM_ARG_ID_SWAPMAIN     (5)
258 #define SWM_ARG_ID_MASTERSHRINK (6)
259 #define SWM_ARG_ID_MASTERGROW   (7)
260         char                    **argv;
261 };
262
263 unsigned long
264 name_to_color(char *colorname)
265 {
266         Colormap                cmap;
267         Status                  status;
268         XColor                  screen_def, exact_def;
269         unsigned long           result = 0;
270         char                    cname[32] = "#";
271
272         cmap = DefaultColormap(display, screens[0].idx);
273         status = XAllocNamedColor(display, cmap, colorname,
274             &screen_def, &exact_def);
275         if (!status) {
276                 strlcat(cname, colorname + 2, sizeof cname - 1);
277                 status = XAllocNamedColor(display, cmap, cname, &screen_def,
278                     &exact_def);
279         }
280         if (status)
281                 result = screen_def.pixel;
282         else
283                 fprintf(stderr, "color '%s' not found.\n", colorname);
284
285         return (result);
286 }
287
288 /* conf file stuff */
289 #define SWM_CONF_WS     "\n= \t"
290 #define SWM_CONF_FILE   "scrotwm.conf"
291 int
292 conf_load(char *filename)
293 {
294         FILE                    *config;
295         char                    *line, *cp, *var, *val;
296         size_t                  len, lineno = 0;
297         int                     i;
298
299         DNPRINTF(SWM_D_MISC, "conf_load: filename %s\n", filename);
300
301         if (filename == NULL)
302                 return (1);
303
304         if ((config = fopen(filename, "r")) == NULL)
305                 return (1);
306
307         for (;;) {
308                 if ((line = fparseln(config, &len, &lineno, NULL, 0)) == NULL)
309                         if (feof(config))
310                                 break;
311                 cp = line;
312                 cp += (long)strspn(cp, SWM_CONF_WS);
313                 if (cp[0] == '\0') {
314                         /* empty line */
315                         free(line);
316                         continue;
317                 }
318                 if ((var = strsep(&cp, SWM_CONF_WS)) == NULL || cp == NULL)
319                         break;
320                 cp += (long)strspn(cp, SWM_CONF_WS);
321                 if ((val = strsep(&cp, SWM_CONF_WS)) == NULL)
322                         break;
323
324                 DNPRINTF(SWM_D_MISC, "conf_load: %s=%s\n",var ,val);
325                 switch (var[0]) {
326                 case 'b':
327                         if (!strncmp(var, "bar_enabled", strlen("bar_enabled")))
328                                 bar_enabled = atoi(val);
329                         else if (!strncmp(var, "bar_border",
330                             strlen("bar_border")))
331                                 for (i = 0; i < ScreenCount(display); i++)
332                                         screens[i].bar_border = name_to_color(val);
333                         else if (!strncmp(var, "bar_color",
334                             strlen("bar_color")))
335                                 for (i = 0; i < ScreenCount(display); i++)
336                                         screens[i].bar_color = name_to_color(val);
337                         else if (!strncmp(var, "bar_font_color",
338                             strlen("bar_font_color")))
339                                 for (i = 0; i < ScreenCount(display); i++)
340                                         screens[i].bar_font_color = name_to_color(val);
341                         else if (!strncmp(var, "bar_font", strlen("bar_font")))
342                                 asprintf(&bar_fonts[0], "%s", val);
343                         else
344                                 goto bad;
345                         break;
346
347                 case 'c':
348                         if (!strncmp(var, "color_focus", strlen("color_focus")))
349                                 for (i = 0; i < ScreenCount(display); i++)
350                                         screens[i].color_focus = name_to_color(val);
351                         else if (!strncmp(var, "color_unfocus",
352                             strlen("color_unfocus")))
353                                 for (i = 0; i < ScreenCount(display); i++)
354                                         screens[i].color_unfocus = name_to_color(val);
355                         else
356                                 goto bad;
357                         break;
358
359                 case 'd':
360                         if (!strncmp(var, "dialog_ratio",
361                             strlen("dialog_ratio"))) {
362                                 dialog_ratio = atof(val);
363                                 if (dialog_ratio > 1.0 || dialog_ratio <= .3)
364                                         dialog_ratio = .6;
365                         } else
366                                 goto bad;
367                         break;
368
369                 case 's':
370                         if (!strncmp(var, "spawn_term", strlen("spawn_term")))
371                                 asprintf(&spawn_term[0], "%s", val); /* XXX args? */
372                         break;
373                 default:
374                         goto bad;
375                 }
376                 free(line);
377         }
378
379         fclose(config);
380         return (0);
381 bad:
382         errx(1, "invalid conf file entry: %s=%s", var, val);
383 }
384
385 void
386 bar_print(struct swm_region *r)
387 {
388         time_t                  tmt;
389         struct tm               tm;
390
391         if (bar_enabled == 0)
392                 return;
393
394         /* clear old text */
395         XSetForeground(display, bar_gc, r->s->bar_color);
396         XDrawString(display, r->bar_window,
397             bar_gc, 4, bar_fs->ascent, bar_text, strlen(bar_text));
398
399         /* draw new text */
400         time(&tmt);
401         localtime_r(&tmt, &tm);
402         strftime(bar_text, sizeof bar_text, "%a %b %d %R %Z %Y", &tm);
403         XSetForeground(display, bar_gc, r->s->bar_font_color);
404         XDrawString(display, r->bar_window, bar_gc, 4,
405             bar_fs->ascent, bar_text, strlen(bar_text));
406         XSync(display, False);
407
408         alarm(60);
409 }
410
411 void
412 bar_signal(int sig)
413 {
414         /* XXX yeah yeah byte me */
415         if (cur_focus)
416                 bar_print(cur_focus->ws->r);
417 }
418
419 void
420 bar_toggle(union arg *args)
421 {
422         struct swm_region *r;
423         int i, j;       
424
425         DNPRINTF(SWM_D_MISC, "bar_toggle\n");
426
427         if (bar_enabled) {
428                 for (i = 0; i < ScreenCount(display); i++)
429                         TAILQ_FOREACH(r, &screens[i].rl, entry)
430                                 XUnmapWindow(display, r->bar_window);
431         } else {
432                 for (i = 0; i < ScreenCount(display); i++)
433                         TAILQ_FOREACH(r, &screens[i].rl, entry)
434                                 XMapRaised(display, r->bar_window);
435         }
436         bar_enabled = !bar_enabled;
437         XSync(display, False);
438         for (i = 0; i < ScreenCount(display); i++)
439                 for (j = 0; j < SWM_WS_MAX; j++)
440                         screens[i].ws[j].restack = 1;
441
442         stack();
443         /* must be after stack */
444         for (i = 0; i < ScreenCount(display); i++)
445                 TAILQ_FOREACH(r, &screens[i].rl, entry)
446                         bar_print(r);
447 }
448
449 void
450 bar_setup(struct swm_region *r)
451 {
452         int                     i;
453
454         for (i = 0; bar_fonts[i] != NULL; i++) {
455                 bar_fs = XLoadQueryFont(display, bar_fonts[i]);
456                 if (bar_fs)
457                         break;
458         }
459         if (bar_fonts[i] == NULL)
460                         errx(1, "couldn't load font");
461         bar_height = bar_fs->ascent + bar_fs->descent + 3;
462
463         r->bar_window = XCreateSimpleWindow(display, 
464             r->s->root, X(r), Y(r), WIDTH(r), bar_height - 2,
465             1, r->s->bar_border, r->s->bar_color);
466         bar_gc = XCreateGC(display, r->bar_window, 0, &bar_gcv);
467         XSetFont(display, bar_gc, bar_fs->fid);
468         XSelectInput(display, r->bar_window, VisibilityChangeMask);
469         if (bar_enabled)
470                 XMapRaised(display, r->bar_window);
471         DNPRINTF(SWM_D_MISC, "bar_setup: bar_window %lu\n", r->bar_window);
472
473         if (signal(SIGALRM, bar_signal) == SIG_ERR)
474                 err(1, "could not install bar_signal");
475         bar_print(r);
476 }
477
478 void
479 config_win(struct ws_win *win)
480 {
481         XConfigureEvent         ce;
482
483         DNPRINTF(SWM_D_MISC, "config_win: win %lu x %d y %d w %d h %d\n",
484             win->id, win->g.x, win->g.y, win->g.w, win->g.h);
485         ce.type = ConfigureNotify;
486         ce.display = display;
487         ce.event = win->id;
488         ce.window = win->id;
489         ce.x = win->g.x;
490         ce.y = win->g.y;
491         ce.width = win->g.w;
492         ce.height = win->g.h;
493         ce.border_width = 1; /* XXX store this! */
494         ce.above = None;
495         ce.override_redirect = False;
496         XSendEvent(display, win->id, False, StructureNotifyMask, (XEvent *)&ce);
497 }
498
499 int
500 count_win(struct workspace *ws, int count_transient)
501 {
502         struct ws_win           *win;
503         int                     count = 0;
504
505         TAILQ_FOREACH(win, &ws->winlist, entry) {
506                 if (count_transient == 0 && win->floating)
507                         continue;
508                 if (count_transient == 0 && win->transient)
509                         continue;
510                 count++;
511         }
512         DNPRINTF(SWM_D_MISC, "count_win: %d\n", count);
513
514         return (count);
515 }
516
517 void
518 quit(union arg *args)
519 {
520         DNPRINTF(SWM_D_MISC, "quit\n");
521         running = 0;
522 }
523
524 void
525 restart(union arg *args)
526 {
527         DNPRINTF(SWM_D_MISC, "restart: %s\n", start_argv[0]);
528
529         XCloseDisplay(display);
530         execvp(start_argv[0], start_argv);
531         fprintf(stderr, "execvp failed\n");
532         perror(" failed");
533         quit(NULL);
534 }
535
536 struct swm_region *
537 cur_region(void)
538 {
539         if (cur_focus && cur_focus->ws->r) {
540                 return (cur_focus->ws->r);
541         } else {
542                 /* XXX get region from pointer */
543                 int i;
544                 struct swm_region *r;
545                 for (i = 0; i < ScreenCount(display); i++)
546                         if ((r = TAILQ_FIRST(&screens[i].rl)) != NULL)
547                                 return (r);
548         }
549 errx(1, "cur_region: no regions!?!?!\n");
550 }
551
552 struct workspace *
553 cur_ws(void)
554 {
555         return (cur_region()->ws);
556 }
557
558 struct ws_win *
559 find_window(Window id)
560 {
561         struct ws_win *win;
562         int i, j;
563
564         for (i = 0; i < ScreenCount(display); i++)
565                 for (j = 0; j < SWM_WS_MAX; j++)
566                         TAILQ_FOREACH(win, &screens[i].ws[j].winlist, entry)
567                                 if (id == win->id)
568                                         return (win);
569         return (NULL);
570 }
571
572 void
573 spawn(union arg *args)
574 {
575         DNPRINTF(SWM_D_MISC, "spawn: %s\n", args->argv[0]);
576         /*
577          * The double-fork construct avoids zombie processes and keeps the code
578          * clean from stupid signal handlers.
579          */
580         if (fork() == 0) {
581                 if (fork() == 0) {
582                         if (display)
583                                 close(ConnectionNumber(display));
584                         setsid();
585                         execvp(args->argv[0], args->argv);
586                         fprintf(stderr, "execvp failed\n");
587                         perror(" failed");
588                 }
589                 exit(0);
590         }
591         wait(0);
592 }
593
594 void
595 unfocus_win(struct ws_win *win)
596 {
597         DNPRINTF(SWM_D_FOCUS, "unfocus_win: id: %lu\n", win->id);
598         if (win->ws->r && win->focus)
599                 XSetWindowBorder(display, win->id,
600                     win->ws->r->s->color_unfocus);
601         win->focus = 0;
602         if (win->ws->focus == win)
603                 win->ws->focus = NULL;
604         if (cur_focus == win)
605                 cur_focus = NULL;
606 }
607
608
609 void
610 focus_win(struct ws_win *win)
611 {
612         DNPRINTF(SWM_D_FOCUS, "focus_win: id: %lu\n", win->id);
613
614         win->ws->focus = win;
615         if (win->ws->r != NULL) {
616                 if (cur_focus && cur_focus != win)
617                         unfocus_win(cur_focus);
618                 cur_focus = win;
619                 if (!win->focus)
620                         XSetWindowBorder(display, win->id,
621                             win->ws->r->s->color_focus);
622                 win->focus = 1;
623                 XSetInputFocus(display, win->id,
624                     RevertToPointerRoot, CurrentTime);
625         }
626 }
627
628 void
629 switchws(union arg *args)
630 {
631         int                     wsid = args->id;
632         struct swm_region       *this_r, *other_r;
633         struct ws_win           *win;
634         struct workspace        *new_ws, *old_ws;
635
636         DNPRINTF(SWM_D_WS, "switchws: %d\n", wsid + 1);
637
638         this_r = cur_region();
639         old_ws = this_r->ws;
640         new_ws = &this_r->s->ws[wsid];
641
642         if (new_ws == old_ws)
643                 return;
644
645         other_r = new_ws->r;
646         if (!other_r) {
647                 /* if the other region is hidden, switch windows */
648         
649                 /* map new window first to prevent ugly blinking */
650                 TAILQ_FOREACH(win, &new_ws->winlist, entry)
651                         XMapRaised(display, win->id);
652
653                 TAILQ_FOREACH(win, &old_ws->winlist, entry)
654                         XUnmapWindow(display, win->id);
655
656                 old_ws->r = NULL;
657         } else {
658                 other_r->ws = old_ws;
659                 old_ws->r = other_r;
660         }
661
662         this_r->ws = new_ws;
663         new_ws->r = this_r;
664
665         ignore_enter = 1;
666         /* set focus */
667         if (new_ws->restack) {
668                 stack();
669                 bar_print(this_r);
670         } else {        
671                 if (new_ws->focus)
672                         focus_win(new_ws->focus);
673                 XSync(display, False);
674         }
675 }
676
677 void
678 swapwin(union arg *args)
679 {
680         struct ws_win           *target;
681         struct ws_win_list      *wl;
682
683
684         DNPRINTF(SWM_D_MOVE, "swapwin: id %d\n", args->id);
685         if (cur_focus == NULL)
686                 return;
687
688         wl = &cur_focus->ws->winlist;
689
690         switch (args->id) {
691         case SWM_ARG_ID_SWAPPREV:
692                 target = TAILQ_PREV(cur_focus, ws_win_list, entry);
693                 TAILQ_REMOVE(wl, cur_focus, entry);
694                 if (target == NULL)
695                         TAILQ_INSERT_TAIL(wl, cur_focus, entry);
696                 else
697                         TAILQ_INSERT_BEFORE(target, cur_focus, entry);
698                 break;
699         case SWM_ARG_ID_SWAPNEXT: 
700                 target = TAILQ_NEXT(cur_focus, entry);
701                 TAILQ_REMOVE(wl, cur_focus, entry);
702                 if (target == NULL)
703                         TAILQ_INSERT_HEAD(wl, cur_focus, entry);
704                 else
705                         TAILQ_INSERT_AFTER(wl, target, cur_focus, entry);
706                 break;
707         case SWM_ARG_ID_SWAPMAIN:
708                 target = TAILQ_FIRST(wl);
709                 if (target == cur_focus)
710                         return;
711                 TAILQ_REMOVE(wl, target, entry);
712                 TAILQ_INSERT_BEFORE(cur_focus, target, entry);
713                 TAILQ_REMOVE(wl, cur_focus, entry);
714                 TAILQ_INSERT_HEAD(wl, cur_focus, entry);
715                 break;
716         default:
717                 DNPRINTF(SWM_D_MOVE, "invalid id: %d\n", args->id);
718                 return;
719         }
720
721         ignore_enter = 2;
722         stack();
723 }
724
725 void
726 focus(union arg *args)
727 {
728         struct ws_win           *winfocus, *winlostfocus;
729         struct ws_win_list      *wl;
730
731         DNPRINTF(SWM_D_FOCUS, "focus: id %d\n", args->id);
732         if (cur_focus == NULL)
733                 return;
734
735         wl = &cur_focus->ws->winlist;
736
737         winlostfocus = cur_focus;
738
739         switch (args->id) {
740         case SWM_ARG_ID_FOCUSPREV:
741                 winfocus = TAILQ_PREV(cur_focus, ws_win_list, entry);
742                 if (winfocus == NULL)
743                         winfocus = TAILQ_LAST(wl, ws_win_list);
744                 break;
745
746         case SWM_ARG_ID_FOCUSNEXT:
747                 winfocus = TAILQ_NEXT(cur_focus, entry);
748                 if (winfocus == NULL)
749                         winfocus = TAILQ_FIRST(wl);
750                 break;
751
752         case SWM_ARG_ID_FOCUSMAIN:
753                 winfocus = TAILQ_FIRST(wl);
754                 break;
755
756         default:
757                 return;
758         }
759
760         if (winfocus == winlostfocus)
761                 return;
762
763         focus_win(winfocus);
764         XSync(display, False);
765 }
766
767 void
768 cycle_layout(union arg *args)
769 {
770         struct workspace *ws;
771
772         ws = cur_ws();
773
774         DNPRINTF(SWM_D_EVENT, "cycle_layout: workspace: %d\n", ws->idx);
775
776         ws->cur_layout++;
777         if (ws->cur_layout->l_stack == NULL)
778                 ws->cur_layout = &layouts[0];
779         ignore_enter = 1;
780
781         stack();
782 }
783
784 void
785 resize_master(union arg *args)
786 {
787         struct workspace        *ws = cur_ws();
788
789         DNPRINTF(SWM_D_STACK, "resize_master for workspace %d (id %d\n",
790             args->id, ws->idx);
791
792         if (ws->cur_layout->l_resize != NULL)
793                 ws->cur_layout->l_resize(args->id);
794 }
795
796 void
797 stack_reset(union arg *args)
798 {
799         struct workspace        *ws = cur_ws();
800
801         DNPRINTF(SWM_D_STACK, "stack_reset: ws %d\n", ws->idx);
802
803         if (ws->cur_layout->l_init != NULL) {
804                 ws->cur_layout->l_init(ws);
805                 stack();
806         }
807 }
808
809 void
810 stack(void) {
811         struct swm_geometry g;
812         struct swm_region *r;
813         int i, j;
814
815         DNPRINTF(SWM_D_STACK, "stack\n");
816
817         for (i = 0; i < ScreenCount(display); i++) {
818                 j = 0;
819                 TAILQ_FOREACH(r, &screens[i].rl, entry) {
820                         DNPRINTF(SWM_D_STACK, "stacking workspace %d "
821                             "(screen %d, region %d)\n", r->ws->idx, i, j++);
822
823                         /* start with screen geometry, adjust for bar */
824                         g = r->g;
825                         g.w -= 2;
826                         g.h -= 2;
827                         if (bar_enabled) {
828                                 g.y += bar_height;
829                                 g.h -= bar_height;
830                         } 
831
832                         r->ws->restack = 0;
833                         r->ws->cur_layout->l_stack(r->ws, &g);
834                 }
835         }
836         XSync(display, False);
837 }
838
839 void
840 stack_floater(struct ws_win *win, struct swm_region *r)
841 {
842         unsigned int            mask;
843         XWindowChanges          wc;
844
845         bzero(&wc, sizeof wc);
846         mask = CWX | CWY | CWBorderWidth | CWWidth | CWHeight;
847         wc.border_width = 1;
848         if (win->transient) {
849                 win->g.w = (double)WIDTH(r) * dialog_ratio;
850                 win->g.h = (double)HEIGHT(r) * dialog_ratio;
851         }
852         wc.width = win->g.w;
853         wc.height = win->g.h;
854         wc.x = (WIDTH(r) - win->g.w) / 2;
855         wc.y = (HEIGHT(r) - win->g.h) / 2;
856
857         DNPRINTF(SWM_D_STACK, "stack_floater: win %lu x %d y %d w %d h %d\n",
858             win->id, wc.x, wc.y, wc.width, wc.height);
859
860         XConfigureWindow(display, win->id, mask, &wc);
861 }
862
863 int vertical_msize[SWM_WS_MAX];
864
865 void
866 vertical_init(struct workspace *ws)
867 {
868         DNPRINTF(SWM_D_MISC, "vertical_init: workspace: %d\n", ws->idx);
869
870         ws->l_state.vertical_msize = SWM_V_SLICE / 2;
871 }
872
873 void
874 vertical_resize(int id)
875 {
876         struct workspace *ws = cur_ws();
877
878         DNPRINTF(SWM_D_STACK, "vertical_resize: workspace: %d\n", ws->idx);
879
880         switch (id) {
881         case SWM_ARG_ID_MASTERSHRINK:
882                 ws->l_state.vertical_msize--;
883                 if (ws->l_state.vertical_msize < 1)
884                         ws->l_state.vertical_msize = 1;
885                 break;
886         case SWM_ARG_ID_MASTERGROW:
887                 ws->l_state.vertical_msize++;
888                 if (ws->l_state.vertical_msize > SWM_V_SLICE - 1)
889                         ws->l_state.vertical_msize = SWM_V_SLICE - 1;
890                 break;
891         default:
892                 return;
893         }
894         stack();
895 }
896
897 void
898 vertical_stack(struct workspace *ws, struct swm_geometry *g) {
899         XWindowChanges          wc;
900         struct swm_geometry     gg = *g;
901         struct ws_win           *win, *winfocus;
902         int                     i, hrh, winno, main_width;
903         unsigned int            mask;
904
905         DNPRINTF(SWM_D_STACK, "vertical_stack: workspace: %d\n", ws->idx);
906
907         if ((winno = count_win(ws, 0)) == 0)
908                 return;
909
910         if (ws->focus == NULL)
911                 ws->focus = TAILQ_FIRST(&ws->winlist);
912         winfocus = cur_focus ? cur_focus : ws->focus;
913
914         if (winno > 1) {
915                 main_width = (g->w / SWM_V_SLICE) *
916                    ws->l_state.vertical_msize;
917                 gg.w = main_width;
918         }
919
920         if (winno > 2)
921                 hrh = g->h / (winno - 1);
922         else
923                 hrh = 0;
924
925         i = 0;
926         TAILQ_FOREACH(win, &ws->winlist, entry) {
927                 if (i == 1) {
928                         gg.x += main_width + 2;
929                         gg.w = g->w - (main_width + 2);
930                 }
931                 if (i != 0 && hrh != 0) {
932                         /* correct the last window for lost pixels */
933                         if (win == TAILQ_LAST(&ws->winlist, ws_win_list)) {
934                                 gg.h = hrh + (g->h - (i * hrh));
935                                 gg.y += hrh;
936                         } else {
937                                 gg.h = hrh - 2;
938                                 /* leave first right hand window at y = 0 */
939                                 if (i > 1)
940                                         gg.y += gg.h + 2;
941                         }
942                 }
943
944                 if (win->transient != 0 || win->floating != 0)
945                         stack_floater(win, ws->r);
946                 else {
947                         bzero(&wc, sizeof wc);
948                         wc.border_width = 1;
949                         win->g.x = wc.x = gg.x;
950                         win->g.y = wc.y = gg.y;
951                         win->g.w = wc.width = gg.w;
952                         win->g.h = wc.height = gg.h;
953                         mask = CWX | CWY | CWWidth | CWHeight | CWBorderWidth;
954                         XConfigureWindow(display, win->id, mask, &wc);
955                 }
956
957                 XMapRaised(display, win->id);
958                 i++;
959         }
960
961         if (winfocus)
962                 focus_win(winfocus); /* has to be done outside of the loop */
963 }
964
965
966 void
967 horizontal_init(struct workspace *ws)
968 {
969         DNPRINTF(SWM_D_STACK, "horizontal_init: workspace: %d\n", ws->idx);
970
971         ws->l_state.horizontal_msize = SWM_H_SLICE / 2;
972 }
973
974 void
975 horizontal_resize(int id)
976 {
977         struct workspace *ws = cur_ws();
978         DNPRINTF(SWM_D_STACK, "horizontal_resize: workspace: %d\n", ws->idx);
979
980         switch (id) {
981         case SWM_ARG_ID_MASTERSHRINK:
982                 ws->l_state.horizontal_msize--;
983                 if (ws->l_state.horizontal_msize < 1)
984                         ws->l_state.horizontal_msize = 1;
985                 break;
986         case SWM_ARG_ID_MASTERGROW:
987                 ws->l_state.horizontal_msize++;
988                 if (ws->l_state.horizontal_msize > SWM_H_SLICE - 1)
989                         ws->l_state.horizontal_msize = SWM_H_SLICE - 1;
990                 break;
991         default:
992                 return;
993         }
994         stack();
995 }
996
997 void
998 horizontal_stack(struct workspace *ws, struct swm_geometry *g) {
999         XWindowChanges          wc;
1000         struct swm_geometry     gg = *g;
1001         struct ws_win           *win, *winfocus;
1002         int                     i, hrw, winno, main_height;
1003         unsigned int            mask;
1004
1005         DNPRINTF(SWM_D_STACK, "horizontal_stack: workspace: %d\n", ws->idx);
1006
1007         if ((winno = count_win(ws, 0)) == 0)
1008                 return;
1009
1010         if (ws->focus == NULL)
1011                 ws->focus = TAILQ_FIRST(&ws->winlist);
1012         winfocus = cur_focus ? cur_focus : ws->focus;
1013
1014         if (winno > 1) {
1015                 main_height = (g->h / SWM_H_SLICE) *
1016                     ws->l_state.horizontal_msize;
1017                 gg.h = main_height;
1018         }
1019
1020         if (winno > 2)
1021                 hrw = g->w / (winno - 1);
1022         else
1023                 hrw = 0;
1024
1025         i = 0;
1026         TAILQ_FOREACH(win, &ws->winlist, entry) {
1027                 if (i == 1) {
1028                         gg.y += main_height + 2;
1029                         gg.h = g->h - (main_height + 2);
1030                 }
1031                 if (i != 0 && hrw != 0) {
1032                         /* correct the last window for lost pixels */
1033                         if (win == TAILQ_LAST(&ws->winlist, ws_win_list)) {
1034                                 gg.w = hrw + (g->w - (i * hrw));
1035                                 gg.x += hrw;
1036                         } else {
1037                                 gg.w = hrw - 2;
1038                                 /* leave first bottom window at x = 0 */
1039                                 if (i > 1)
1040                                         gg.x += gg.w + 2;
1041                         }
1042                 }
1043
1044                 if (win->transient != 0 || win->floating != 0)
1045                         stack_floater(win, ws->r);
1046                 else {
1047                         bzero(&wc, sizeof wc);
1048                         wc.border_width = 1;
1049                         win->g.x = wc.x = gg.x;
1050                         win->g.y = wc.y = gg.y;
1051                         win->g.w = wc.width = gg.w;
1052                         win->g.h = wc.height = gg.h;
1053                         mask = CWX | CWY | CWWidth | CWHeight | CWBorderWidth;
1054                         XConfigureWindow(display, win->id, mask, &wc);
1055                 }
1056
1057                 XMapRaised(display, win->id);
1058                 i++;
1059         }
1060
1061         if (winfocus)
1062                 focus_win(winfocus); /* this has to be done outside of the loop */
1063 }
1064
1065 /* fullscreen view */
1066 void
1067 max_stack(struct workspace *ws, struct swm_geometry *g) {
1068         XWindowChanges          wc;
1069         struct swm_geometry     gg = *g;
1070         struct ws_win           *win, *winfocus;
1071         unsigned int            mask;
1072
1073         DNPRINTF(SWM_D_STACK, "max_stack: workspace: %d\n", ws->idx);
1074
1075         if (count_win(ws, 0) == 0)
1076                 return;
1077
1078         if (ws->focus == NULL)
1079                 ws->focus = TAILQ_FIRST(&ws->winlist);
1080         winfocus = cur_focus ? cur_focus : ws->focus;
1081
1082         TAILQ_FOREACH(win, &ws->winlist, entry) {
1083                 if (win->transient != 0 || win->floating != 0) {
1084                         if (win == ws->focus) {
1085                                 /* XXX maximize? */
1086                                 stack_floater(win, ws->r);
1087                                 XMapRaised(display, win->id);
1088                         } else
1089                                 XUnmapWindow(display, win->id);
1090                 } else {
1091                         bzero(&wc, sizeof wc);
1092                         wc.border_width = 1;
1093                         win->g.x = wc.x = gg.x;
1094                         win->g.y = wc.y = gg.y;
1095                         win->g.w = wc.width = gg.w;
1096                         win->g.h = wc.height = gg.h;
1097                         mask = CWX | CWY | CWWidth | CWHeight | CWBorderWidth;
1098                         XConfigureWindow(display, win->id, mask, &wc);
1099
1100                         if (win == ws->focus) {
1101                                 XMapRaised(display, win->id);
1102                         } else
1103                                 XUnmapWindow(display, win->id);
1104                 }
1105         }
1106
1107         if (winfocus)
1108                 focus_win(winfocus); /* has to be done outside of the loop */
1109 }
1110
1111 void
1112 send_to_ws(union arg *args)
1113 {
1114         int                     wsid = args->id;
1115         struct ws_win           *win = cur_focus;
1116         struct workspace        *ws, *nws;
1117
1118         DNPRINTF(SWM_D_MOVE, "send_to_ws: win: %lu\n", win->id);
1119
1120         ws = win->ws;
1121         nws = &win->s->ws[wsid];
1122
1123         XUnmapWindow(display, win->id);
1124
1125         /* find a window to focus */
1126         ws->focus = TAILQ_PREV(win, ws_win_list, entry);
1127         if (ws->focus == NULL)
1128                 ws->focus = TAILQ_FIRST(&ws->winlist);
1129         if (ws->focus == win)
1130                 ws->focus = NULL;
1131
1132         TAILQ_REMOVE(&ws->winlist, win, entry);
1133
1134         TAILQ_INSERT_TAIL(&nws->winlist, win, entry);
1135         win->ws = nws;
1136
1137         if (count_win(nws, 1) == 1)
1138                 nws->focus = win;
1139         ws->restack = 1;
1140         nws->restack = 1;
1141
1142         stack();
1143 }
1144
1145 /* key definitions */
1146 struct key {
1147         unsigned int            mod;
1148         KeySym                  keysym;
1149         void                    (*func)(union arg *);
1150         union arg               args;
1151 } keys[] = {
1152         /* modifier             key     function                argument */
1153         { MODKEY,               XK_space,       cycle_layout,   {0} }, 
1154         { MODKEY | ShiftMask,   XK_space,       stack_reset,    {0} }, 
1155         { MODKEY,               XK_h,           resize_master,  {.id = SWM_ARG_ID_MASTERSHRINK} },
1156         { MODKEY,               XK_l,           resize_master,  {.id = SWM_ARG_ID_MASTERGROW} },
1157         { MODKEY,               XK_Return,      swapwin,        {.id = SWM_ARG_ID_SWAPMAIN} },
1158         { MODKEY,               XK_j,           focus,          {.id = SWM_ARG_ID_FOCUSNEXT} },
1159         { MODKEY,               XK_k,           focus,          {.id = SWM_ARG_ID_FOCUSPREV} },
1160         { MODKEY | ShiftMask,   XK_j,           swapwin,        {.id = SWM_ARG_ID_SWAPNEXT} },
1161         { MODKEY | ShiftMask,   XK_k,           swapwin,        {.id = SWM_ARG_ID_SWAPPREV} },
1162         { MODKEY | ShiftMask,   XK_Return,      spawn,          {.argv = spawn_term} },
1163         { MODKEY,               XK_p,           spawn,          {.argv = spawn_menu} },
1164         { MODKEY | ShiftMask,   XK_q,           quit,           {0} },
1165         { MODKEY,               XK_q,           restart,        {0} },
1166         { MODKEY,               XK_m,           focus,          {.id = SWM_ARG_ID_FOCUSMAIN} },
1167         { MODKEY,               XK_1,           switchws,       {.id = 0} },
1168         { MODKEY,               XK_2,           switchws,       {.id = 1} },
1169         { MODKEY,               XK_3,           switchws,       {.id = 2} },
1170         { MODKEY,               XK_4,           switchws,       {.id = 3} },
1171         { MODKEY,               XK_5,           switchws,       {.id = 4} },
1172         { MODKEY,               XK_6,           switchws,       {.id = 5} },
1173         { MODKEY,               XK_7,           switchws,       {.id = 6} },
1174         { MODKEY,               XK_8,           switchws,       {.id = 7} },
1175         { MODKEY,               XK_9,           switchws,       {.id = 8} },
1176         { MODKEY,               XK_0,           switchws,       {.id = 9} },
1177         { MODKEY | ShiftMask,   XK_1,           send_to_ws,     {.id = 0} },
1178         { MODKEY | ShiftMask,   XK_2,           send_to_ws,     {.id = 1} },
1179         { MODKEY | ShiftMask,   XK_3,           send_to_ws,     {.id = 2} },
1180         { MODKEY | ShiftMask,   XK_4,           send_to_ws,     {.id = 3} },
1181         { MODKEY | ShiftMask,   XK_5,           send_to_ws,     {.id = 4} },
1182         { MODKEY | ShiftMask,   XK_6,           send_to_ws,     {.id = 5} },
1183         { MODKEY | ShiftMask,   XK_7,           send_to_ws,     {.id = 6} },
1184         { MODKEY | ShiftMask,   XK_8,           send_to_ws,     {.id = 7} },
1185         { MODKEY | ShiftMask,   XK_9,           send_to_ws,     {.id = 8} },
1186         { MODKEY | ShiftMask,   XK_0,           send_to_ws,     {.id = 9} },
1187         { MODKEY,               XK_b,           bar_toggle,     {0} },
1188         { MODKEY,               XK_Tab,         focus,          {.id = SWM_ARG_ID_FOCUSNEXT} },
1189         { MODKEY | ShiftMask,   XK_Tab,         focus,          {.id = SWM_ARG_ID_FOCUSPREV} },
1190 };
1191
1192 void
1193 updatenumlockmask(void)
1194 {
1195         unsigned int            i, j;
1196         XModifierKeymap         *modmap;
1197
1198         DNPRINTF(SWM_D_MISC, "updatenumlockmask\n");
1199         numlockmask = 0;
1200         modmap = XGetModifierMapping(display);
1201         for (i = 0; i < 8; i++)
1202                 for (j = 0; j < modmap->max_keypermod; j++)
1203                         if (modmap->modifiermap[i * modmap->max_keypermod + j]
1204                           == XKeysymToKeycode(display, XK_Num_Lock))
1205                                 numlockmask = (1 << i);
1206
1207         XFreeModifiermap(modmap);
1208 }
1209
1210 void
1211 grabkeys(void)
1212 {
1213         unsigned int            i, j, k;
1214         KeyCode                 code;
1215         unsigned int            modifiers[] =
1216             { 0, LockMask, numlockmask, numlockmask | LockMask };
1217
1218         DNPRINTF(SWM_D_MISC, "grabkeys\n");
1219         updatenumlockmask();
1220
1221         for (k = 0; k < ScreenCount(display); k++) {
1222                 if (TAILQ_EMPTY(&screens[k].rl))
1223                         continue;
1224                 XUngrabKey(display, AnyKey, AnyModifier, screens[k].root);
1225                 for(i = 0; i < LENGTH(keys); i++) {
1226                         if((code = XKeysymToKeycode(display, keys[i].keysym)))
1227                                 for(j = 0; j < LENGTH(modifiers); j++)
1228                                         XGrabKey(display, code,
1229                                             keys[i].mod | modifiers[j],
1230                                             screens[k].root, True,
1231                                             GrabModeAsync, GrabModeAsync);
1232                 }
1233         }
1234 }
1235 void
1236 expose(XEvent *e)
1237 {
1238         DNPRINTF(SWM_D_EVENT, "expose: window: %lu\n", e->xexpose.window);
1239 }
1240
1241 void
1242 keypress(XEvent *e)
1243 {
1244         unsigned int            i;
1245         KeySym                  keysym;
1246         XKeyEvent               *ev = &e->xkey;
1247
1248         DNPRINTF(SWM_D_EVENT, "keypress: window: %lu\n", ev->window);
1249
1250         keysym = XKeycodeToKeysym(display, (KeyCode)ev->keycode, 0);
1251         for (i = 0; i < LENGTH(keys); i++)
1252                 if (keysym == keys[i].keysym
1253                    && CLEANMASK(keys[i].mod) == CLEANMASK(ev->state)
1254                    && keys[i].func)
1255                         keys[i].func(&(keys[i].args));
1256 }
1257
1258 void
1259 buttonpress(XEvent *e)
1260 {
1261         XButtonPressedEvent     *ev = &e->xbutton;
1262 #ifdef SWM_CLICKTOFOCUS
1263         struct ws_win           *win;
1264         struct workspace        *ws = cur_ws();
1265 #endif
1266
1267         DNPRINTF(SWM_D_EVENT, "buttonpress: window: %lu\n", ev->window);
1268
1269         if (ev->window == ev->root)
1270                 return;
1271         if (ev->window == cur_focus->id)
1272                 return;
1273 #ifdef SWM_CLICKTOFOCUS
1274         TAILQ_FOREACH(win, &ws->winlist, entry)
1275                 if (win->id == ev->window) {
1276                         /* focus in the clicked window */
1277                         XSetWindowBorder(display, ev->window, 0xff0000);
1278                         XSetWindowBorder(display, ws->focus->id, 0x888888);
1279                         XSetInputFocus(display, ev->window, RevertToPointerRoot,
1280                             CurrentTime);
1281                         ws->focus = win;
1282                         XSync(display, False);
1283                         break;
1284         }
1285 #endif
1286 }
1287
1288 void
1289 set_win_state(struct ws_win *win, long state)
1290 {
1291         long                    data[] = {state, None};
1292
1293         DNPRINTF(SWM_D_EVENT, "set_win_state: window: %lu\n", win->id);
1294
1295         XChangeProperty(display, win->id, astate, astate, 32, PropModeReplace,
1296             (unsigned char *)data, 2);
1297 }
1298
1299 struct ws_win *
1300 manage_window(Window id, struct workspace *ws)
1301 {
1302         Window                  trans;
1303         struct ws_win           *win;
1304         XClassHint              ch;
1305
1306         TAILQ_FOREACH(win, &ws->winlist, entry) {
1307                 if (win->id == id)
1308                         return (win);   /* already being managed */
1309         }
1310
1311         if ((win = calloc(1, sizeof(struct ws_win))) == NULL)
1312                 errx(1, "calloc: failed to allocate memory for new window");
1313
1314         win->id = id;
1315         win->ws = ws;
1316         win->s = ws->r->s;      /* this never changes */
1317         TAILQ_INSERT_TAIL(&ws->winlist, win, entry);
1318
1319         /* make new win focused */
1320         focus_win(win);
1321
1322         XGetTransientForHint(display, win->id, &trans);
1323         if (trans) {
1324                 win->transient = trans;
1325                 DNPRINTF(SWM_D_MISC, "manage_window: win %u transient %u\n",
1326                     (unsigned)win->id, win->transient);
1327         }
1328         XGetWindowAttributes(display, win->id, &win->wa);
1329         win->g.w = win->wa.width;
1330         win->g.h = win->wa.height;
1331         win->g.x = win->wa.x;
1332         win->g.y = win->wa.y;
1333
1334         /* XXX make this a table */
1335         bzero(&ch, sizeof ch);
1336         if (XGetClassHint(display, win->id, &ch)) {
1337                 /*fprintf(stderr, "class: %s name: %s\n", ch.res_class, ch.res_name); */
1338                 if (!strcmp(ch.res_class, "MPlayer") && !strcmp(ch.res_name, "xv")) {
1339                         win->floating = 1;
1340                 }
1341                 if (ch.res_class)
1342                         XFree(ch.res_class);
1343                 if (ch.res_name)
1344                         XFree(ch.res_name);
1345         }
1346
1347         XSelectInput(display, id, EnterWindowMask | FocusChangeMask |
1348             PropertyChangeMask | StructureNotifyMask);
1349
1350         set_win_state(win, NormalState);
1351
1352         return (win);
1353 }
1354
1355 void
1356 configurerequest(XEvent *e)
1357 {
1358         XConfigureRequestEvent  *ev = &e->xconfigurerequest;
1359         struct ws_win           *win;
1360         int                     new = 1;
1361         XWindowChanges          wc;
1362
1363         if ((win = find_window(ev->window)) == NULL)
1364                 new = 1;
1365
1366         if (new) {
1367                 DNPRINTF(SWM_D_EVENT, "configurerequest: new window: %lu\n",
1368                     ev->window);
1369                 bzero(&wc, sizeof wc);
1370                 wc.x = ev->x;
1371                 wc.y = ev->y;
1372                 wc.width = ev->width;
1373                 wc.height = ev->height;
1374                 wc.border_width = ev->border_width;
1375                 wc.sibling = ev->above;
1376                 wc.stack_mode = ev->detail;
1377                 XConfigureWindow(display, ev->window, ev->value_mask, &wc);
1378         } else {
1379                 DNPRINTF(SWM_D_EVENT, "configurerequest: change window: %lu\n",
1380                     ev->window);
1381                 if (win->floating) {
1382                         if (ev->value_mask & CWX)
1383                                 win->g.x = ev->x;
1384                         if (ev->value_mask & CWY)
1385                                 win->g.y = ev->y;
1386                         if (ev->value_mask & CWWidth)
1387                                 win->g.w = ev->width;
1388                         if (ev->value_mask & CWHeight)
1389                                 win->g.h = ev->height;
1390                         if (win->ws->r != NULL) {
1391                                 /* this seems to be full screen */
1392                                 if (win->g.w > WIDTH(win->ws->r)) {
1393                                         /* kill border */
1394                                         win->g.x -= 1;
1395                                         win->g.w += 1;
1396                                 }
1397                                 if (win->g.h > HEIGHT(win->ws->r)) {
1398                                         /* kill border */
1399                                         win->g.y -= 1;
1400                                         win->g.h += 1;
1401                                 }
1402                         }
1403                         if ((ev->value_mask & (CWX|CWY)) &&
1404                             !(ev->value_mask & (CWWidth|CWHeight)))
1405                                 config_win(win);
1406                         XMoveResizeWindow(display, win->id,
1407                             win->g.x, win->g.y, win->g.w, win->g.h);
1408                 } else
1409                         config_win(win);
1410         }
1411 }
1412
1413 void
1414 configurenotify(XEvent *e)
1415 {
1416         DNPRINTF(SWM_D_EVENT, "configurenotify: window: %lu\n",
1417             e->xconfigure.window);
1418 }
1419
1420 void
1421 destroynotify(XEvent *e)
1422 {
1423         struct ws_win           *win;
1424         XDestroyWindowEvent     *ev = &e->xdestroywindow;
1425
1426         DNPRINTF(SWM_D_EVENT, "destroynotify: window %lu\n", ev->window);
1427
1428         if ((win = find_window(ev->window)) != NULL) {
1429                 struct workspace *ws = win->ws;
1430                 /* find a window to focus */
1431                 if (ws->focus == win)
1432                         ws->focus = TAILQ_PREV(win, ws_win_list, entry);
1433                 if (ws->focus == NULL)
1434                         ws->focus = TAILQ_FIRST(&ws->winlist);
1435                 if (ws->focus == win)
1436                         ws->focus = NULL;
1437                 if (cur_focus == win) {
1438                         if (ws->focus == NULL) 
1439                                 unfocus_win(win); /* XXX focus another ws? */
1440                         else
1441                                 focus_win(ws->focus);
1442                 }
1443
1444                 TAILQ_REMOVE(&ws->winlist, win, entry);
1445                 set_win_state(win, WithdrawnState);
1446                 free(win);
1447         }
1448
1449         stack();
1450 }
1451
1452 void
1453 enternotify(XEvent *e)
1454 {
1455         XCrossingEvent          *ev = &e->xcrossing;
1456         struct ws_win           *win;
1457         int                     i, j;
1458
1459         DNPRINTF(SWM_D_EVENT, "enternotify: window: %lu\n", ev->window);
1460
1461         if((ev->mode != NotifyNormal || ev->detail == NotifyInferior) &&
1462             ev->window != ev->root)
1463                 return;
1464         if (ignore_enter) {
1465                 /* eat event(r) to prevent autofocus */
1466                 ignore_enter--;
1467                 return;
1468         }
1469         /* XXX update cur_ws here?... brute force for now */
1470         for (i = 0; i < ScreenCount(display); i++) {
1471                 for (j = 0; j < SWM_WS_MAX; j++) {
1472                         TAILQ_FOREACH(win, &screens[i].ws[j].winlist , entry) {
1473                                 if (win->id == ev->window)
1474                                         focus_win(win);
1475                         }
1476                 }
1477         }
1478 }
1479
1480 void
1481 focusin(XEvent *e)
1482 {
1483         DNPRINTF(SWM_D_EVENT, "focusin: window: %lu\n", e->xfocus.window);
1484
1485         /* XXX this probably needs better handling now.
1486         if (ev->window == ev->root)
1487                 return;
1488         */
1489         /*
1490          * kill grab for now so that we can cut and paste , this screws up
1491          * click to focus
1492          */
1493         /*
1494         DNPRINTF(SWM_D_EVENT, "focusin: window: %lu grabbing\n", ev->window);
1495         XGrabButton(display, Button1, AnyModifier, ev->window, False,
1496             ButtonPress, GrabModeAsync, GrabModeSync, None, None);
1497         */
1498 }
1499
1500 void
1501 mappingnotify(XEvent *e)
1502 {
1503         XMappingEvent           *ev = &e->xmapping;
1504
1505         DNPRINTF(SWM_D_EVENT, "mappingnotify: window: %lu\n", ev->window);
1506
1507         XRefreshKeyboardMapping(ev);
1508         if (ev->request == MappingKeyboard)
1509                 grabkeys();
1510 }
1511
1512 void
1513 maprequest(XEvent *e)
1514 {
1515         XMapRequestEvent        *ev = &e->xmaprequest;
1516         XWindowAttributes       wa;
1517         struct workspace        *ws = cur_ws();
1518
1519         DNPRINTF(SWM_D_EVENT, "maprequest: window: %lu\n",
1520             e->xmaprequest.window);
1521
1522         if (!XGetWindowAttributes(display, ev->window, &wa))
1523                 return;
1524         if (wa.override_redirect)
1525                 return;
1526         manage_window(e->xmaprequest.window, ws); /* RRR */
1527         stack();
1528 }
1529
1530 void
1531 propertynotify(XEvent *e)
1532 {
1533         DNPRINTF(SWM_D_EVENT, "propertynotify: window: %lu\n",
1534             e->xproperty.window);
1535 }
1536
1537 void
1538 unmapnotify(XEvent *e)
1539 {
1540         DNPRINTF(SWM_D_EVENT, "unmapnotify: window: %lu\n", e->xunmap.window);
1541 }
1542
1543 void
1544 visibilitynotify(XEvent *e)
1545 {
1546         int i;
1547         struct swm_region *r;
1548         DNPRINTF(SWM_D_EVENT, "visibilitynotify: window: %lu\n", e->xvisibility.window);
1549         if (e->xvisibility.state == VisibilityUnobscured)
1550                 for (i = 0; i < ScreenCount(display); i++) 
1551                         TAILQ_FOREACH(r, &screens[i].rl, entry)
1552                                 if (e->xvisibility.window == r->bar_window)
1553                                         bar_print(r);
1554 }
1555
1556 void                    (*handler[LASTEvent])(XEvent *) = {
1557                                 [Expose] = expose,
1558                                 [KeyPress] = keypress,
1559                                 [ButtonPress] = buttonpress,
1560                                 [ConfigureRequest] = configurerequest,
1561                                 [ConfigureNotify] = configurenotify,
1562                                 [DestroyNotify] = destroynotify,
1563                                 [EnterNotify] = enternotify,
1564                                 [FocusIn] = focusin,
1565                                 [MappingNotify] = mappingnotify,
1566                                 [MapRequest] = maprequest,
1567                                 [PropertyNotify] = propertynotify,
1568                                 [UnmapNotify] = unmapnotify,
1569                                 [VisibilityNotify] = visibilitynotify,
1570 };
1571
1572 int
1573 xerror_start(Display *d, XErrorEvent *ee)
1574 {
1575         other_wm = 1;
1576         return (-1);
1577 }
1578
1579 int
1580 xerror(Display *d, XErrorEvent *ee)
1581 {
1582         /* fprintf(stderr, "error: %p %p\n", display, ee); */
1583         return (-1);
1584 }
1585
1586 int
1587 active_wm(void)
1588 {
1589         other_wm = 0;
1590         xerrorxlib = XSetErrorHandler(xerror_start);
1591
1592         /* this causes an error if some other window manager is running */
1593         XSelectInput(display, DefaultRootWindow(display),
1594             SubstructureRedirectMask);
1595         XSync(display, False);
1596         if (other_wm)
1597                 return (1);
1598
1599         XSetErrorHandler(xerror);
1600         XSync(display, False);
1601         return (0);
1602 }
1603
1604 long
1605 getstate(Window w)
1606 {
1607         int                     format, status;
1608         long                    result = -1;
1609         unsigned char           *p = NULL;
1610         unsigned long           n, extra;
1611         Atom                    real;
1612
1613         astate = XInternAtom(display, "WM_STATE", False);
1614         status = XGetWindowProperty(display, w, astate, 0L, 2L, False, astate,
1615             &real, &format, &n, &extra, (unsigned char **)&p);
1616         if (status != Success)
1617                 return (-1);
1618         if (n != 0)
1619                 result = *p;
1620         XFree(p);
1621         return (result);
1622 }
1623
1624 void
1625 new_region(struct swm_screen *s, struct workspace *ws,
1626     int x, int y, int w, int h)
1627 {
1628         struct swm_region *r;
1629
1630         DNPRINTF(SWM_D_MISC, "new region on screen %d: %dx%d (%d, %d)\n",
1631              s->idx, x, y, w, h);
1632
1633         if ((r = calloc(1, sizeof(struct swm_region))) == NULL)
1634                 errx(1, "calloc: failed to allocate memory for screen");
1635
1636         X(r) = x;
1637         Y(r) = y;
1638         WIDTH(r) = w;
1639         HEIGHT(r) = h;
1640         r->s = s;
1641         r->ws = ws;
1642         ws->r = r;
1643         TAILQ_INSERT_TAIL(&s->rl, r, entry);
1644         bar_setup(r);
1645 }
1646
1647 void
1648 setup_screens(void)
1649 {
1650         int i;
1651
1652
1653         if ((screens = calloc(ScreenCount(display),
1654              sizeof(struct swm_screen))) == NULL)
1655                 errx(1, "calloc: screens");
1656
1657         /* map physical screens */
1658         for (i = 0; i < ScreenCount(display); i++) {
1659 #ifdef SWM_XRR_HAS_CRTC
1660                 XRRCrtcInfo             *crtc_info;
1661                 XRRScreenResources      *res;
1662                 int                     c;
1663 #endif /* SWM_XRR_HAS_CRTC */
1664                 Window                  d1, d2, *wins = NULL;
1665                 XWindowAttributes       wa;
1666                 struct swm_region       *r;
1667                 int                     errorbase, major, minor;
1668                 int                     j, ncrtc, w = 0;
1669                 unsigned int            num;
1670
1671                 screens[i].idx = i;
1672                 TAILQ_INIT(&screens[i].rl);
1673                 screens[i].root = ScreenOfDisplay(display, i)->root;
1674                 screens[i].root = RootWindow(display, i);
1675
1676                 /* set default colors */
1677                 screens[i].color_focus = name_to_color("red");
1678                 screens[i].color_unfocus = name_to_color("rgb:88/88/88");
1679                 screens[i].bar_border = name_to_color("rgb:00/80/80");
1680                 screens[i].bar_color = name_to_color("black");
1681                 screens[i].bar_font_color = name_to_color("rgb:a0/a0/a0");
1682
1683                 /* init all workspaces */
1684                 for (j = 0; j < SWM_WS_MAX; j++) {
1685                         int                     k;
1686                         struct workspace        *ws = &screens[i].ws[j];
1687
1688                         ws->idx = j;
1689                         ws->restack = 1;
1690                         ws->focus = NULL;
1691                         ws->r = NULL;
1692                         TAILQ_INIT(&ws->winlist);
1693
1694                         for (k = 0; layouts[k].l_stack != NULL; k++) {
1695                                 if (layouts[k].l_init != NULL)
1696                                         layouts[k].l_init(ws);
1697                         }
1698                         ws->cur_layout = &layouts[0];
1699                 }
1700
1701                 /* map virtual screens onto physical screens */
1702                 screens[i].xrandr_support = XRRQueryExtension(display,
1703                     &xrandr_eventbase, &errorbase);
1704                 if (screens[i].xrandr_support)
1705                         if (XRRQueryVersion(display, &major, &minor) &&
1706                             major < 1)
1707                                 screens[i].xrandr_support = 0;
1708
1709 #if 0   /* not ready for dynamic screen changes */
1710                 if (screens[i].xrandr_support)
1711                         XRRSelectInput(display,
1712                             screens[r->s].root,
1713                             RRScreenChangeNotifyMask);
1714 #endif
1715
1716                 /* grab existing windows (before we build the bars)*/
1717                 if (!XQueryTree(display, screens[i].root,
1718                     &d1, &d2, &wins, &num)) {
1719                         if (wins)       /* not sure if this is necessary */
1720                                 XFree(wins);
1721                         continue;
1722                 }
1723
1724
1725 #ifdef SWM_XRR_HAS_CRTC
1726                 res = XRRGetScreenResources (display, screens[i].root);
1727                 if (res == NULL) {
1728                         ncrtc = 0;
1729                         new_region(&screens[i], &screens[i].ws[w],
1730                             0, 0, DisplayWidth(display, i),
1731                             DisplayHeight(display, i)); 
1732                 } else 
1733                         ncrtc = res->ncrtc;
1734
1735                 for (c = 0; c < ncrtc; c++) {
1736                         crtc_info = XRRGetCrtcInfo(display, res, res->crtcs[c]);
1737                         if (crtc_info != NULL && crtc_info->mode == None)
1738                                 new_region(&screens[i], &screens[i].ws[w],
1739                                     0, 0, DisplayWidth(display, i),
1740                                     DisplayHeight(display, i)); 
1741                         else
1742                                 new_region(&screens[i], &screens[i].ws[w],
1743                                     crtc_info->x, crtc_info->y,
1744                                     crtc_info->width, crtc_info->height);
1745                         w++;
1746                 }
1747 #else
1748                 new_region(&screens[i], &screens[i].ws[w],
1749                     0, 0, DisplayWidth(display, i),
1750                     DisplayHeight(display, i)); 
1751 #endif /* SWM_XRR_HAS_CRTC */
1752
1753                 /* attach windows to a region */
1754                 /* normal windows */
1755                 if ((r = TAILQ_FIRST(&screens[i].rl)) == NULL)
1756                         errx(1, "no regions on screen %d", i);
1757
1758                 for (i = 0; i < num; i++) {
1759                         XGetWindowAttributes(display, wins[i], &wa);
1760                         if (!XGetWindowAttributes(display, wins[i], &wa) ||
1761                             wa.override_redirect ||
1762                             XGetTransientForHint(display, wins[i], &d1))
1763                                 continue;
1764                         if (wa.map_state == IsViewable ||
1765                             getstate(wins[i]) == NormalState)
1766                                 manage_window(wins[i], r->ws);
1767                 }
1768                 /* transient windows */
1769                 for (i = 0; i < num; i++) {
1770                         if (!XGetWindowAttributes(display, wins[i], &wa))
1771                                 continue;
1772                         if (XGetTransientForHint(display, wins[i], &d1) &&
1773                             (wa.map_state == IsViewable || getstate(wins[i]) ==
1774                             NormalState))
1775                                 manage_window(wins[i], r->ws);
1776                 }
1777                 if (wins)
1778                         XFree(wins);
1779         }
1780 }
1781
1782 int
1783 main(int argc, char *argv[])
1784 {
1785         struct passwd           *pwd;
1786         char                    conf[PATH_MAX], *cfile = NULL;
1787         struct stat             sb;
1788         XEvent                  e;
1789
1790         start_argv = argv;
1791         fprintf(stderr, "Welcome to scrotwm V%s\n", SWM_VERSION);
1792         if (!setlocale(LC_CTYPE, "") || !XSupportsLocale())
1793                 warnx("no locale support");
1794
1795         if (!(display = XOpenDisplay(0)))
1796                 errx(1, "can not open display");
1797
1798         if (active_wm())
1799                 errx(1, "other wm running");
1800
1801         astate = XInternAtom(display, "WM_STATE", False);
1802
1803         /* look for local and global conf file */
1804         pwd = getpwuid(getuid());
1805         if (pwd == NULL)
1806                 errx(1, "invalid user %d", getuid());
1807
1808         snprintf(conf, sizeof conf, "%s/.%s", pwd->pw_dir, SWM_CONF_FILE);
1809         if (stat(conf, &sb) != -1) {
1810                 if (S_ISREG(sb.st_mode))
1811                         cfile = conf;
1812         } else {
1813                 /* try global conf file */
1814                 snprintf(conf, sizeof conf, "/etc/%s", SWM_CONF_FILE);
1815                 if (!stat(conf, &sb))
1816                         if (S_ISREG(sb.st_mode))
1817                                 cfile = conf;
1818         }
1819         if (cfile)
1820                 conf_load(cfile);
1821
1822         setup_screens();
1823
1824         //ws[0].focus = TAILQ_FIRST(&ws[0].winlist);
1825
1826         grabkeys();
1827         stack();
1828
1829         while (running) {
1830                 XNextEvent(display, &e);
1831                 if (handler[e.type])
1832                         handler[e.type](&e);
1833         }
1834
1835         XCloseDisplay(display);
1836
1837         return (0);
1838 }