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