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