JasonWoof Got questions, comments, patches, etc.? Contact Jason Woofenden
whitespace police. no actual changes.
[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  * Copyright (c) 2009 Darrin Chandler <dwchandler@stilyagin.com>
6  * Copyright (c) 2009 Pierre-Yves Ritschard <pyr@spootnik.org>
7  *
8  * Permission to use, copy, modify, and distribute this software for any
9  * purpose with or without fee is hereby granted, provided that the above
10  * copyright notice and this permission notice appear in all copies.
11  *
12  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19  */
20 /*
21  * Much code and ideas taken from dwm under the following license:
22  * MIT/X Consortium License
23  *
24  * 2006-2008 Anselm R Garbe <garbeam at gmail dot com>
25  * 2006-2007 Sander van Dijk <a dot h dot vandijk at gmail dot com>
26  * 2006-2007 Jukka Salmi <jukka at salmi dot ch>
27  * 2007 Premysl Hruby <dfenze at gmail dot com>
28  * 2007 Szabolcs Nagy <nszabolcs at gmail dot com>
29  * 2007 Christof Musik <christof at sendfax dot de>
30  * 2007-2008 Enno Gottox Boland <gottox at s01 dot de>
31  * 2007-2008 Peter Hartlich <sgkkr at hartlich dot com>
32  * 2008 Martin Hurton <martin dot hurton at gmail dot com>
33  *
34  * Permission is hereby granted, free of charge, to any person obtaining a
35  * copy of this software and associated documentation files (the "Software"),
36  * to deal in the Software without restriction, including without limitation
37  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
38  * and/or sell copies of the Software, and to permit persons to whom the
39  * Software is furnished to do so, subject to the following conditions:
40  *
41  * The above copyright notice and this permission notice shall be included in
42  * all copies or substantial portions of the Software.
43  *
44  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
45  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
46  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
47  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
48  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
49  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
50  * DEALINGS IN THE SOFTWARE.
51  */
52
53 static const char       *cvstag = "$scrotwm$";
54
55 #define SWM_VERSION     "0.9.5"
56
57 #include <stdio.h>
58 #include <stdlib.h>
59 #include <err.h>
60 #include <errno.h>
61 #include <fcntl.h>
62 #include <locale.h>
63 #include <unistd.h>
64 #include <time.h>
65 #include <signal.h>
66 #include <string.h>
67 #include <util.h>
68 #include <pwd.h>
69 #include <ctype.h>
70
71 #include <sys/types.h>
72 #include <sys/time.h>
73 #include <sys/stat.h>
74 #include <sys/wait.h>
75 #include <sys/queue.h>
76 #include <sys/param.h>
77 #include <sys/select.h>
78
79 #include <X11/cursorfont.h>
80 #include <X11/keysym.h>
81 #include <X11/Xatom.h>
82 #include <X11/Xlib.h>
83 #include <X11/Xproto.h>
84 #include <X11/Xutil.h>
85 #include <X11/extensions/Xrandr.h>
86
87 #if RANDR_MAJOR < 1
88 #  error XRandR versions less than 1.0 are not supported
89 #endif
90
91 #if RANDR_MAJOR >= 1
92 #if RANDR_MINOR >= 2
93 #define SWM_XRR_HAS_CRTC
94 #endif
95 #endif
96
97 /* #define SWM_DEBUG */
98 #ifdef SWM_DEBUG
99 #define DPRINTF(x...)           do { if (swm_debug) fprintf(stderr, x); } while(0)
100 #define DNPRINTF(n,x...)        do { if (swm_debug & n) fprintf(stderr, x); } while(0)
101 #define SWM_D_MISC              0x0001
102 #define SWM_D_EVENT             0x0002
103 #define SWM_D_WS                0x0004
104 #define SWM_D_FOCUS             0x0008
105 #define SWM_D_MOVE              0x0010
106 #define SWM_D_STACK             0x0020
107 #define SWM_D_MOUSE             0x0040
108 #define SWM_D_PROP              0x0080
109 #define SWM_D_CLASS             0x0100
110 #define SWM_D_KEY               0x0200
111 #define SWM_D_QUIRK             0x0400
112
113 u_int32_t               swm_debug = 0
114                             | SWM_D_MISC
115                             | SWM_D_EVENT
116                             | SWM_D_WS
117                             | SWM_D_FOCUS
118                             | SWM_D_MOVE
119                             | SWM_D_STACK
120                             | SWM_D_MOUSE
121                             | SWM_D_PROP
122                             | SWM_D_CLASS
123                             | SWM_D_KEY
124                             | SWM_D_QUIRK
125                             ;
126 #else
127 #define DPRINTF(x...)
128 #define DNPRINTF(n,x...)
129 #endif
130
131 #define LENGTH(x)               (sizeof x / sizeof x[0])
132 #define MODKEY                  Mod1Mask
133 #define CLEANMASK(mask)         (mask & ~(numlockmask | LockMask))
134 #define BUTTONMASK              (ButtonPressMask|ButtonReleaseMask)
135 #define MOUSEMASK               (BUTTONMASK|PointerMotionMask)
136 #define SWM_PROPLEN             (16)
137 #define SWM_FUNCNAME_LEN        (32)
138 #define SWM_KEYS_LEN            (255)
139 #define SWM_QUIRK_LEN           (64)
140 #define X(r)                    (r)->g.x
141 #define Y(r)                    (r)->g.y
142 #define WIDTH(r)                (r)->g.w
143 #define HEIGHT(r)               (r)->g.h
144 #define SWM_MAX_FONT_STEPS      (3)
145
146 #ifndef SWM_LIB
147 #define SWM_LIB                 "/usr/X11R6/lib/swmhack.so"
148 #endif
149
150 char                    **start_argv;
151 Atom                    astate;
152 Atom                    aprot;
153 Atom                    adelete;
154 int                     (*xerrorxlib)(Display *, XErrorEvent *);
155 int                     other_wm;
156 int                     running = 1;
157 int                     ss_enabled = 0;
158 int                     xrandr_support;
159 int                     xrandr_eventbase;
160 int                     ignore_enter = 0;
161 unsigned int            numlockmask = 0;
162 Display                 *display;
163
164 int                     cycle_empty = 0;
165 int                     cycle_visible = 0;
166 int                     term_width = 0;
167 int                     font_adjusted = 0;
168 unsigned int            mod_key = MODKEY;
169
170 /* dialog windows */
171 double                  dialog_ratio = .6;
172 /* status bar */
173 #define SWM_BAR_MAX     (256)
174 char                    *bar_argv[] = { NULL, NULL };
175 int                     bar_pipe[2];
176 char                    bar_ext[SWM_BAR_MAX];
177 char                    bar_vertext[SWM_BAR_MAX];
178 int                     bar_version = 0;
179 sig_atomic_t            bar_alarm = 0;
180 int                     bar_delay = 30;
181 int                     bar_enabled = 1;
182 int                     bar_extra = 1;
183 int                     bar_extra_running = 0;
184 int                     bar_verbose = 1;
185 int                     bar_height = 0;
186 int                     clock_enabled = 1;
187 int                     title_name_enabled = 0;
188 int                     title_class_enabled = 0;
189 pid_t                   bar_pid;
190 GC                      bar_gc;
191 XGCValues               bar_gcv;
192 int                     bar_fidx = 0;
193 XFontStruct             *bar_fs;
194 char                    *bar_fonts[] = {
195                             "-*-terminus-medium-*-*-*-*-*-*-*-*-*-*-*",
196                             "-*-times-medium-r-*-*-*-*-*-*-*-*-*-*",
197                             NULL
198 };
199
200 /* terminal + args */
201 char                    *spawn_term[] = { "xterm", NULL };
202 char                    *spawn_screenshot[] = { "screenshot.sh", NULL, NULL };
203 char                    *spawn_lock[] = { "xlock", NULL };
204 char                    *spawn_initscr[] = { "initscreen.sh", NULL };
205 char                    *spawn_menu[] = { "dmenu_run", "-fn", NULL, "-nb", NULL,
206                             "-nf", NULL, "-sb", NULL, "-sf", NULL, NULL };
207
208 #define SWM_MENU_FN     (2)
209 #define SWM_MENU_NB     (4)
210 #define SWM_MENU_NF     (6)
211 #define SWM_MENU_SB     (8)
212 #define SWM_MENU_SF     (10)
213
214 /* layout manager data */
215 struct swm_geometry {
216         int                     x;
217         int                     y;
218         int                     w;
219         int                     h;
220 };
221
222 struct swm_screen;
223 struct workspace;
224
225 /* virtual "screens" */
226 struct swm_region {
227         TAILQ_ENTRY(swm_region) entry;
228         struct swm_geometry     g;
229         struct workspace        *ws;    /* current workspace on this region */
230         struct swm_screen       *s;     /* screen idx */
231         Window                  bar_window;
232 };
233 TAILQ_HEAD(swm_region_list, swm_region);
234
235 struct ws_win {
236         TAILQ_ENTRY(ws_win)     entry;
237         Window                  id;
238         struct swm_geometry     g;
239         int                     got_focus;
240         int                     floating;
241         int                     transient;
242         int                     manual;
243         int                     font_size_boundary[SWM_MAX_FONT_STEPS];
244         int                     font_steps;
245         int                     last_inc;
246         int                     can_delete;
247         unsigned long           quirks;
248         struct workspace        *ws;    /* always valid */
249         struct swm_screen       *s;     /* always valid, never changes */
250         XWindowAttributes       wa;
251         XSizeHints              sh;
252         XClassHint              ch;
253 };
254 TAILQ_HEAD(ws_win_list, ws_win);
255
256 /* user/key callable function IDs */
257 enum keyfuncid {
258         kf_cycle_layout,
259         kf_stack_reset,
260         kf_master_shrink,
261         kf_master_grow,
262         kf_master_add,
263         kf_master_del,
264         kf_stack_inc,
265         kf_stack_dec,
266         kf_swap_main,
267         kf_focus_next,
268         kf_focus_prev,
269         kf_swap_next,
270         kf_swap_prev,
271         kf_spawn_term,
272         kf_spawn_menu,
273         kf_quit,
274         kf_restart,
275         kf_focus_main,
276         kf_ws_1,
277         kf_ws_2,
278         kf_ws_3,
279         kf_ws_4,
280         kf_ws_5,
281         kf_ws_6,
282         kf_ws_7,
283         kf_ws_8,
284         kf_ws_9,
285         kf_ws_10,
286         kf_ws_next,
287         kf_ws_prev,
288         kf_screen_next,
289         kf_screen_prev,
290         kf_mvws_1,
291         kf_mvws_2,
292         kf_mvws_3,
293         kf_mvws_4,
294         kf_mvws_5,
295         kf_mvws_6,
296         kf_mvws_7,
297         kf_mvws_8,
298         kf_mvws_9,
299         kf_mvws_10,
300         kf_bar_toggle,
301         kf_wind_kill,
302         kf_wind_del,
303         kf_screenshot_all,
304         kf_screenshot_wind,
305         kf_float_toggle,
306         kf_version,
307         kf_spawn_lock,
308         kf_spawn_initscr,
309         kf_invalid
310 };
311
312 /* layout handlers */
313 void    stack(void);
314 void    vertical_config(struct workspace *, int);
315 void    vertical_stack(struct workspace *, struct swm_geometry *);
316 void    horizontal_config(struct workspace *, int);
317 void    horizontal_stack(struct workspace *, struct swm_geometry *);
318 void    max_stack(struct workspace *, struct swm_geometry *);
319
320 void    grabbuttons(struct ws_win *, int);
321 void    new_region(struct swm_screen *, int, int, int, int);
322
323 struct layout {
324         void            (*l_stack)(struct workspace *, struct swm_geometry *);
325         void            (*l_config)(struct workspace *, int);
326 } layouts[] =  {
327         /* stack,               configure */
328         { vertical_stack,       vertical_config},
329         { horizontal_stack,     horizontal_config},
330         { max_stack,            NULL},
331         { NULL,                 NULL},
332 };
333
334 #define SWM_H_SLICE             (32)
335 #define SWM_V_SLICE             (32)
336
337 /* define work spaces */
338 struct workspace {
339         int                     idx;            /* workspace index */
340         int                     restack;        /* restack on switch */
341         struct layout           *cur_layout;    /* current layout handlers */
342         struct ws_win           *focus;         /* may be NULL */
343         struct ws_win           *focus_prev;    /* may be NULL */
344         struct swm_region       *r;             /* may be NULL */
345         struct ws_win_list      winlist;        /* list of windows in ws */
346
347         /* stacker state */
348         struct {
349                                 int horizontal_msize;
350                                 int horizontal_mwin;
351                                 int horizontal_stacks;
352                                 int vertical_msize;
353                                 int vertical_mwin;
354                                 int vertical_stacks;
355         } l_state;
356 };
357
358 enum    { SWM_S_COLOR_BAR, SWM_S_COLOR_BAR_BORDER, SWM_S_COLOR_BAR_FONT,
359           SWM_S_COLOR_FOCUS, SWM_S_COLOR_UNFOCUS, SWM_S_COLOR_MAX };
360
361 /* physical screen mapping */
362 #define SWM_WS_MAX              (10)            /* XXX Too small? */
363 struct swm_screen {
364         int                     idx;            /* screen index */
365         struct swm_region_list  rl;     /* list of regions on this screen */
366         struct swm_region_list  orl;    /* list of old regions */
367         Window                  root;
368         struct workspace        ws[SWM_WS_MAX];
369
370         /* colors */
371         struct {
372                 unsigned long   color;
373                 char            *name;
374         } c[SWM_S_COLOR_MAX];
375 };
376 struct swm_screen       *screens;
377 int                     num_screens;
378
379 struct ws_win           *cur_focus = NULL;
380
381 /* args to functions */
382 union arg {
383         int                     id;
384 #define SWM_ARG_ID_FOCUSNEXT    (0)
385 #define SWM_ARG_ID_FOCUSPREV    (1)
386 #define SWM_ARG_ID_FOCUSMAIN    (2)
387 #define SWM_ARG_ID_SWAPNEXT     (3)
388 #define SWM_ARG_ID_SWAPPREV     (4)
389 #define SWM_ARG_ID_SWAPMAIN     (5)
390 #define SWM_ARG_ID_MASTERSHRINK (6)
391 #define SWM_ARG_ID_MASTERGROW   (7)
392 #define SWM_ARG_ID_MASTERADD    (8)
393 #define SWM_ARG_ID_MASTERDEL    (9)
394 #define SWM_ARG_ID_STACKRESET   (10)
395 #define SWM_ARG_ID_STACKINIT    (11)
396 #define SWM_ARG_ID_CYCLEWS_UP   (12)
397 #define SWM_ARG_ID_CYCLEWS_DOWN (13)
398 #define SWM_ARG_ID_CYCLESC_UP   (14)
399 #define SWM_ARG_ID_CYCLESC_DOWN (15)
400 #define SWM_ARG_ID_STACKINC     (16)
401 #define SWM_ARG_ID_STACKDEC     (17)
402 #define SWM_ARG_ID_SS_ALL       (0)
403 #define SWM_ARG_ID_SS_WINDOW    (1)
404 #define SWM_ARG_ID_DONTCENTER   (0)
405 #define SWM_ARG_ID_CENTER       (1)
406 #define SWM_ARG_ID_KILLWINDOW   (0)
407 #define SWM_ARG_ID_DELETEWINDOW (1)
408         char                    **argv;
409 };
410
411 /* quirks */
412 struct quirk {
413         char                    *class;
414         char                    *name;
415         unsigned long           quirk;
416 #define SWM_Q_FLOAT             (1<<0)  /* float this window */
417 #define SWM_Q_TRANSSZ           (1<<1)  /* transiend window size too small */
418 #define SWM_Q_ANYWHERE          (1<<2)  /* don't position this window */
419 #define SWM_Q_XTERM_FONTADJ     (1<<3)  /* adjust xterm fonts when resizing */
420 #define SWM_Q_FULLSCREEN        (1<<4)  /* remove border */
421 };
422 int                             quirks_size = 0, quirks_length = 0;
423 struct quirk                    *quirks = NULL;
424
425 /* events */
426 void                    expose(XEvent *);
427 void                    keypress(XEvent *);
428 void                    buttonpress(XEvent *);
429 void                    configurerequest(XEvent *);
430 void                    configurenotify(XEvent *);
431 void                    destroynotify(XEvent *);
432 void                    enternotify(XEvent *);
433 void                    focusin(XEvent *);
434 void                    focusout(XEvent *);
435 void                    mappingnotify(XEvent *);
436 void                    maprequest(XEvent *);
437 void                    propertynotify(XEvent *);
438 void                    unmapnotify(XEvent *);
439 void                    visibilitynotify(XEvent *);
440
441 void                    (*handler[LASTEvent])(XEvent *) = {
442                                 [Expose] = expose,
443                                 [KeyPress] = keypress,
444                                 [ButtonPress] = buttonpress,
445                                 [ConfigureRequest] = configurerequest,
446                                 [ConfigureNotify] = configurenotify,
447                                 [DestroyNotify] = destroynotify,
448                                 [EnterNotify] = enternotify,
449                                 [FocusIn] = focusin,
450                                 [FocusOut] = focusout,
451                                 [MappingNotify] = mappingnotify,
452                                 [MapRequest] = maprequest,
453                                 [PropertyNotify] = propertynotify,
454                                 [UnmapNotify] = unmapnotify,
455                                 [VisibilityNotify] = visibilitynotify,
456 };
457
458 unsigned long
459 name_to_color(char *colorname)
460 {
461         Colormap                cmap;
462         Status                  status;
463         XColor                  screen_def, exact_def;
464         unsigned long           result = 0;
465         char                    cname[32] = "#";
466
467         cmap = DefaultColormap(display, screens[0].idx);
468         status = XAllocNamedColor(display, cmap, colorname,
469             &screen_def, &exact_def);
470         if (!status) {
471                 strlcat(cname, colorname + 2, sizeof cname - 1);
472                 status = XAllocNamedColor(display, cmap, cname, &screen_def,
473                     &exact_def);
474         }
475         if (status)
476                 result = screen_def.pixel;
477         else
478                 fprintf(stderr, "color '%s' not found.\n", colorname);
479
480         return (result);
481 }
482
483 void
484 setscreencolor(char *val, int i, int c)
485 {
486         if (i > 0 && i <= ScreenCount(display)) {
487                 screens[i - 1].c[c].color = name_to_color(val);
488                 if ((screens[i - 1].c[c].name = strdup(val)) == NULL)
489                         errx(1, "strdup");
490         } else if (i == -1) {
491                 for (i = 0; i < ScreenCount(display); i++)
492                         screens[i].c[c].color = name_to_color(val);
493                         if ((screens[i - 1].c[c].name = strdup(val)) == NULL)
494                                 errx(1, "strdup");
495         } else
496                 errx(1, "invalid screen index: %d out of bounds (maximum %d)\n",
497                     i, ScreenCount(display));
498 }
499
500 void
501 custom_region(char *val)
502 {
503         unsigned int                    sidx, x, y, w, h;
504
505         if (sscanf(val, "screen[%u]:%ux%u+%u+%u", &sidx, &w, &h, &x, &y) != 5)
506                 errx(1, "invalid custom region, "
507                     "should be 'screen[<n>]:<n>x<n>+<n>+<n>\n");
508         if (sidx < 1 || sidx > ScreenCount(display))
509                 errx(1, "invalid screen index: %d out of bounds (maximum %d)\n",
510                     sidx, ScreenCount(display));
511         sidx--;
512
513         if (w < 1 || h < 1)
514                 errx(1, "region %ux%u+%u+%u too small\n", w, h, x, y);
515
516         if (x  < 0 || x > DisplayWidth(display, sidx) ||
517             y < 0 || y > DisplayHeight(display, sidx) ||
518             w + x > DisplayWidth(display, sidx) ||
519             h + y > DisplayHeight(display, sidx))
520                 errx(1, "region %ux%u+%u+%u not within screen boundaries "
521                     "(%ux%u)\n", w, h, x, y,
522                     DisplayWidth(display, sidx), DisplayHeight(display, sidx));
523
524         new_region(&screens[sidx], x, y, w, h);
525 }
526
527 void
528 socket_setnonblock(int fd)
529 {
530         int                     flags;
531
532         if ((flags = fcntl(fd, F_GETFL, 0)) == -1)
533                 err(1, "fcntl F_GETFL");
534         flags |= O_NONBLOCK;
535         if ((flags = fcntl(fd, F_SETFL, flags)) == -1)
536                 err(1, "fcntl F_SETFL");
537 }
538
539 void
540 bar_print(struct swm_region *r, char *s)
541 {
542         XClearWindow(display, r->bar_window);
543         XSetForeground(display, bar_gc, r->s->c[SWM_S_COLOR_BAR_FONT].color);
544         XDrawString(display, r->bar_window, bar_gc, 4, bar_fs->ascent, s,
545             strlen(s));
546 }
547
548 void
549 bar_extra_stop(void)
550 {
551         if (bar_pipe[0]) {
552                 close(bar_pipe[0]);
553                 bzero(bar_pipe, sizeof bar_pipe);
554         }
555         if (bar_pid) {
556                 kill(bar_pid, SIGTERM);
557                 bar_pid = 0;
558         }
559         strlcpy(bar_ext, "", sizeof bar_ext);
560         bar_extra = 0;
561 }
562
563 void
564 bar_update(void)
565 {
566         time_t                  tmt;
567         struct tm               tm;
568         struct swm_region       *r;
569         int                     i, x, do_class, do_name;
570         size_t                  len;
571         char                    s[SWM_BAR_MAX];
572         char                    loc[SWM_BAR_MAX];
573         char                    *b;
574         XClassHint              *xch;
575         Status                   status;
576
577         if (bar_enabled == 0)
578                 return;
579         if (bar_extra && bar_extra_running) {
580                 /* ignore short reads; it'll correct itself */
581                 while ((b = fgetln(stdin, &len)) != NULL)
582                         if (b && b[len - 1] == '\n') {
583                                 b[len - 1] = '\0';
584                                 strlcpy(bar_ext, b, sizeof bar_ext);
585                         }
586                 if (b == NULL && errno != EAGAIN) {
587                         fprintf(stderr, "bar_extra failed: errno: %d %s\n",
588                             errno, strerror(errno));
589                         bar_extra_stop();
590                 }
591         } else
592                 strlcpy(bar_ext, "", sizeof bar_ext);
593
594         if (clock_enabled == 0)
595                 strlcpy(s, "", sizeof s);
596         else {
597                 time(&tmt);
598                 localtime_r(&tmt, &tm);
599                 strftime(s, sizeof s, "%a %b %d %R %Z %Y    ", &tm);
600         }
601         xch = NULL;
602         if ((title_name_enabled == 1 || title_class_enabled == 1) &&
603             cur_focus != NULL) {
604                 if ((xch = XAllocClassHint()) == NULL)
605                         goto out;
606                 status = XGetClassHint(display, cur_focus->id, xch);
607                 if (status == BadWindow || status == BadAlloc)
608                         goto out;
609                 do_class = (title_class_enabled && xch->res_class != NULL);
610                 do_name = (title_name_enabled && xch->res_name != NULL);
611                 if (do_class)
612                         strlcat(s, xch->res_class, sizeof s);
613                 if (do_class && do_name)
614                         strlcat(s, ":", sizeof s);
615                 if (do_name)
616                         strlcat(s, xch->res_name, sizeof s);
617         }
618 out:
619         if (xch)
620                 XFree(xch);
621         for (i = 0; i < ScreenCount(display); i++) {
622                 x = 1;
623                 TAILQ_FOREACH(r, &screens[i].rl, entry) {
624                         snprintf(loc, sizeof loc, "%d:%d    %s %s    %s",
625                             x++, r->ws->idx + 1, s, bar_ext, bar_vertext);
626                         bar_print(r, loc);
627                 }
628         }
629         XSync(display, False);
630         alarm(bar_delay);
631 }
632
633 void
634 bar_signal(int sig)
635 {
636         bar_alarm = 1;
637 }
638
639 void
640 bar_toggle(struct swm_region *r, union arg *args)
641 {
642         struct swm_region       *tmpr;
643         int                     i, j, sc = ScreenCount(display);
644
645         DNPRINTF(SWM_D_MISC, "bar_toggle\n");
646
647         if (bar_enabled)
648                 for (i = 0; i < sc; i++)
649                         TAILQ_FOREACH(tmpr, &screens[i].rl, entry)
650                                 XUnmapWindow(display, tmpr->bar_window);
651         else
652                 for (i = 0; i < sc; i++)
653                         TAILQ_FOREACH(tmpr, &screens[i].rl, entry)
654                                 XMapRaised(display, tmpr->bar_window);
655
656         bar_enabled = !bar_enabled;
657         for (i = 0; i < sc; i++)
658                 for (j = 0; j < SWM_WS_MAX; j++)
659                         screens[i].ws[j].restack = 1;
660
661         stack();
662         /* must be after stack */
663         bar_update();
664 }
665
666 void
667 bar_refresh(void)
668 {
669         XSetWindowAttributes    wa;
670         struct swm_region       *r;
671         int                     i;
672
673         /* do this here because the conf file is in memory */
674         if (bar_extra && bar_extra_running == 0 && bar_argv[0]) {
675                 /* launch external status app */
676                 bar_extra_running = 1;
677                 if (pipe(bar_pipe) == -1)
678                         err(1, "pipe error");
679                 socket_setnonblock(bar_pipe[0]);
680                 socket_setnonblock(bar_pipe[1]); /* XXX hmmm, really? */
681                 if (dup2(bar_pipe[0], 0) == -1)
682                         errx(1, "dup2");
683                 if (dup2(bar_pipe[1], 1) == -1)
684                         errx(1, "dup2");
685                 if (signal(SIGPIPE, SIG_IGN) == SIG_ERR)
686                         err(1, "could not disable SIGPIPE");
687                 switch (bar_pid = fork()) {
688                 case -1:
689                         err(1, "cannot fork");
690                         break;
691                 case 0: /* child */
692                         close(bar_pipe[0]);
693                         execvp(bar_argv[0], bar_argv);
694                         err(1, "%s external app failed", bar_argv[0]);
695                         break;
696                 default: /* parent */
697                         close(bar_pipe[1]);
698                         break;
699                 }
700         }
701
702         bzero(&wa, sizeof wa);
703         for (i = 0; i < ScreenCount(display); i++)
704                 TAILQ_FOREACH(r, &screens[i].rl, entry) {
705                         wa.border_pixel =
706                             screens[i].c[SWM_S_COLOR_BAR_BORDER].color;
707                         wa.background_pixel =
708                             screens[i].c[SWM_S_COLOR_BAR].color;
709                         XChangeWindowAttributes(display, r->bar_window,
710                             CWBackPixel | CWBorderPixel, &wa);
711                 }
712         bar_update();
713 }
714
715 void
716 bar_setup(struct swm_region *r)
717 {
718         int                     i;
719
720         for (i = 0; bar_fonts[i] != NULL; i++) {
721                 bar_fs = XLoadQueryFont(display, bar_fonts[i]);
722                 if (bar_fs) {
723                         bar_fidx = i;
724                         break;
725                 }
726         }
727         if (bar_fonts[i] == NULL)
728                         errx(1, "couldn't load font");
729         bar_height = bar_fs->ascent + bar_fs->descent + 3;
730
731         r->bar_window = XCreateSimpleWindow(display,
732             r->s->root, X(r), Y(r), WIDTH(r) - 2, bar_height - 2,
733             1, r->s->c[SWM_S_COLOR_BAR_BORDER].color,
734             r->s->c[SWM_S_COLOR_BAR].color);
735         bar_gc = XCreateGC(display, r->bar_window, 0, &bar_gcv);
736         XSetFont(display, bar_gc, bar_fs->fid);
737         XSelectInput(display, r->bar_window, VisibilityChangeMask);
738         if (bar_enabled)
739                 XMapRaised(display, r->bar_window);
740         DNPRINTF(SWM_D_MISC, "bar_setup: bar_window %lu\n", r->bar_window);
741
742         if (signal(SIGALRM, bar_signal) == SIG_ERR)
743                 err(1, "could not install bar_signal");
744         bar_refresh();
745 }
746
747 void
748 version(struct swm_region *r, union arg *args)
749 {
750         bar_version = !bar_version;
751         if (bar_version)
752                 snprintf(bar_vertext, sizeof bar_vertext, "Version: %s CVS: %s",
753                     SWM_VERSION, cvstag);
754         else
755                 strlcpy(bar_vertext, "", sizeof bar_vertext);
756         bar_update();
757 }
758
759 void
760 client_msg(struct ws_win *win, Atom a)
761 {
762         XClientMessageEvent     cm;
763
764         bzero(&cm, sizeof cm);
765         cm.type = ClientMessage;
766         cm.window = win->id;
767         cm.message_type = aprot;
768         cm.format = 32;
769         cm.data.l[0] = a;
770         cm.data.l[1] = CurrentTime;
771         XSendEvent(display, win->id, False, 0L, (XEvent *)&cm);
772 }
773
774 void
775 config_win(struct ws_win *win)
776 {
777         XConfigureEvent         ce;
778
779         DNPRINTF(SWM_D_MISC, "config_win: win %lu x %d y %d w %d h %d\n",
780             win->id, win->g.x, win->g.y, win->g.w, win->g.h);
781         ce.type = ConfigureNotify;
782         ce.display = display;
783         ce.event = win->id;
784         ce.window = win->id;
785         ce.x = win->g.x;
786         ce.y = win->g.y;
787         ce.width = win->g.w;
788         ce.height = win->g.h;
789         ce.border_width = 1; /* XXX store this! */
790         ce.above = None;
791         ce.override_redirect = False;
792         XSendEvent(display, win->id, False, StructureNotifyMask, (XEvent *)&ce);
793 }
794
795 int
796 count_win(struct workspace *ws, int count_transient)
797 {
798         struct ws_win           *win;
799         int                     count = 0;
800
801         TAILQ_FOREACH(win, &ws->winlist, entry) {
802                 if (count_transient == 0 && win->floating)
803                         continue;
804                 if (count_transient == 0 && win->transient)
805                         continue;
806                 count++;
807         }
808         DNPRINTF(SWM_D_MISC, "count_win: %d\n", count);
809
810         return (count);
811 }
812
813 void
814 quit(struct swm_region *r, union arg *args)
815 {
816         DNPRINTF(SWM_D_MISC, "quit\n");
817         running = 0;
818 }
819
820 void
821 unmap_all(void)
822 {
823         struct ws_win           *win;
824         int                     i, j;
825
826         for (i = 0; i < ScreenCount(display); i++)
827                 for (j = 0; j < SWM_WS_MAX; j++)
828                         TAILQ_FOREACH(win, &screens[i].ws[j].winlist, entry)
829                                 XUnmapWindow(display, win->id);
830 }
831
832 void
833 fake_keypress(struct ws_win *win, int keysym, int modifiers)
834 {
835         XKeyEvent event;
836
837         event.display = display;        /* Ignored, but what the hell */
838         event.window = win->id;
839         event.root = win->s->root;
840         event.subwindow = None;
841         event.time = CurrentTime;
842         event.x = win->g.x;
843         event.y = win->g.y;
844         event.x_root = 1;
845         event.y_root = 1;
846         event.same_screen = True;
847         event.keycode = XKeysymToKeycode(display, keysym);
848         event.state = modifiers;
849
850         event.type = KeyPress;
851         XSendEvent(event.display, event.window, True,
852             KeyPressMask, (XEvent *)&event);
853
854         event.type = KeyRelease;
855         XSendEvent(event.display, event.window, True,
856             KeyPressMask, (XEvent *)&event);
857
858 }
859
860 void
861 restart(struct swm_region *r, union arg *args)
862 {
863         DNPRINTF(SWM_D_MISC, "restart: %s\n", start_argv[0]);
864
865         /* disable alarm because the following code may not be interrupted */
866         alarm(0);
867         if (signal(SIGALRM, SIG_IGN) == SIG_ERR)
868                 errx(1, "can't disable alarm");
869
870         bar_extra_stop();
871         bar_extra = 1;
872         unmap_all();
873         XCloseDisplay(display);
874         execvp(start_argv[0], start_argv);
875         fprintf(stderr, "execvp failed\n");
876         perror(" failed");
877         quit(NULL, NULL);
878 }
879
880 struct swm_region *
881 root_to_region(Window root)
882 {
883         struct swm_region       *r = NULL;
884         Window                  rr, cr;
885         int                     i, x, y, wx, wy;
886         unsigned int            mask;
887
888         for (i = 0; i < ScreenCount(display); i++)
889                 if (screens[i].root == root)
890                         break;
891
892         if (XQueryPointer(display, screens[i].root,
893             &rr, &cr, &x, &y, &wx, &wy, &mask) != False) {
894                 /* choose a region based on pointer location */
895                 TAILQ_FOREACH(r, &screens[i].rl, entry)
896                         if (x >= X(r) && x <= X(r) + WIDTH(r) &&
897                             y >= Y(r) && y <= Y(r) + HEIGHT(r))
898                                 break;
899         }
900
901         if (r == NULL)
902                 r = TAILQ_FIRST(&screens[i].rl);
903
904         return (r);
905 }
906
907 struct ws_win *
908 find_window(Window id)
909 {
910         struct ws_win           *win;
911         int                     i, j;
912
913         for (i = 0; i < ScreenCount(display); i++)
914                 for (j = 0; j < SWM_WS_MAX; j++)
915                         TAILQ_FOREACH(win, &screens[i].ws[j].winlist, entry)
916                                 if (id == win->id)
917                                         return (win);
918         return (NULL);
919 }
920
921 void
922 spawn(struct swm_region *r, union arg *args)
923 {
924         char                    *ret;
925         int                     si;
926
927         DNPRINTF(SWM_D_MISC, "spawn: %s\n", args->argv[0]);
928         /*
929          * The double-fork construct avoids zombie processes and keeps the code
930          * clean from stupid signal handlers.
931          */
932         if (fork() == 0) {
933                 if (fork() == 0) {
934                         if (display)
935                                 close(ConnectionNumber(display));
936                         setenv("LD_PRELOAD", SWM_LIB, 1);
937                         if (asprintf(&ret, "%d", r->ws->idx)) {
938                                 setenv("_SWM_WS", ret, 1);
939                                 free(ret);
940                         }
941                         if (asprintf(&ret, "%d", getpid())) {
942                                 setenv("_SWM_PID", ret, 1);
943                                 free(ret);
944                         }
945                         setsid();
946                         /* kill stdin, mplayer, ssh-add etc. need that */
947                         si = open("/dev/null", O_RDONLY, 0);
948                         if (si == -1)
949                                 err(1, "open /dev/null");
950                         if (dup2(si, 0) == -1)
951                                 err(1, "dup2 /dev/null");
952                         execvp(args->argv[0], args->argv);
953                         fprintf(stderr, "execvp failed\n");
954                         perror(" failed");
955                 }
956                 exit(0);
957         }
958         wait(0);
959 }
960
961 void
962 spawnterm(struct swm_region *r, union arg *args)
963 {
964         DNPRINTF(SWM_D_MISC, "spawnterm\n");
965
966         if (term_width)
967                 setenv("_SWM_XTERM_FONTADJ", "", 1);
968         spawn(r, args);
969 }
970
971 void
972 spawnmenu(struct swm_region *r, union arg *args)
973 {
974         DNPRINTF(SWM_D_MISC, "spawnmenu\n");
975
976         spawn_menu[SWM_MENU_FN] = bar_fonts[bar_fidx];
977         spawn_menu[SWM_MENU_NB] = r->s->c[SWM_S_COLOR_BAR].name;
978         spawn_menu[SWM_MENU_NF] = r->s->c[SWM_S_COLOR_BAR_FONT].name;
979         spawn_menu[SWM_MENU_SB] = r->s->c[SWM_S_COLOR_BAR_BORDER].name;
980         spawn_menu[SWM_MENU_SF] = r->s->c[SWM_S_COLOR_BAR].name;
981
982         spawn(r, args);
983 }
984
985 void
986 unfocus_win(struct ws_win *win)
987 {
988         if (win == NULL)
989                 return;
990
991         if (win->ws->focus != win && win->ws->focus != NULL)
992                 win->ws->focus_prev = win->ws->focus;
993
994         if (win->ws->r == NULL)
995                 return;
996
997         grabbuttons(win, 0);
998         XSetWindowBorder(display, win->id,
999             win->ws->r->s->c[SWM_S_COLOR_UNFOCUS].color);
1000         win->got_focus = 0;
1001         if (win->ws->focus == win)
1002                 win->ws->focus = NULL;
1003         if (cur_focus == win)
1004                 cur_focus = NULL;
1005 }
1006
1007 void
1008 unfocus_all(void)
1009 {
1010         struct ws_win           *win;
1011         int                     i, j;
1012
1013         DNPRINTF(SWM_D_FOCUS, "unfocus_all:\n");
1014
1015         for (i = 0; i < ScreenCount(display); i++)
1016                 for (j = 0; j < SWM_WS_MAX; j++)
1017                         TAILQ_FOREACH(win, &screens[i].ws[j].winlist, entry)
1018                                 unfocus_win(win);
1019 }
1020
1021 void
1022 focus_win(struct ws_win *win)
1023 {
1024         DNPRINTF(SWM_D_FOCUS, "focus_win: id: %lu\n", win ? win->id : 0);
1025
1026         if (win == NULL)
1027                 return;
1028
1029         if (cur_focus)
1030                 unfocus_win(cur_focus);
1031         if (win->ws->focus) {
1032                 /* probably shouldn't happen due to the previous unfocus_win */
1033                 DNPRINTF(SWM_D_FOCUS, "unfocusing win->ws->focus: %lu\n",
1034                     win->ws->focus->id);
1035                 unfocus_win(win->ws->focus);
1036         }
1037         win->ws->focus = win;
1038         if (win->ws->r != NULL) {
1039                 cur_focus = win;
1040                 if (!win->got_focus) {
1041                         XSetWindowBorder(display, win->id,
1042                             win->ws->r->s->c[SWM_S_COLOR_FOCUS].color);
1043                         grabbuttons(win, 1);
1044                 }
1045                 win->got_focus = 1;
1046                 XSetInputFocus(display, win->id,
1047                     RevertToPointerRoot, CurrentTime);
1048         }
1049 }
1050
1051 void
1052 switchws(struct swm_region *r, union arg *args)
1053 {
1054         int                     wsid = args->id;
1055         struct swm_region       *this_r, *other_r;
1056         struct ws_win           *win;
1057         struct workspace        *new_ws, *old_ws;
1058
1059         this_r = r;
1060         old_ws = this_r->ws;
1061         new_ws = &this_r->s->ws[wsid];
1062
1063         DNPRINTF(SWM_D_WS, "switchws screen[%d]:%dx%d+%d+%d: "
1064             "%d -> %d\n", r->s->idx, WIDTH(r), HEIGHT(r), X(r), Y(r),
1065             old_ws->idx, wsid);
1066
1067         if (new_ws == old_ws)
1068                 return;
1069
1070         other_r = new_ws->r;
1071         if (!other_r) {
1072                 /* if the other workspace is hidden, switch windows */
1073                 /* map new window first to prevent ugly blinking */
1074                 old_ws->r = NULL;
1075                 old_ws->restack = 1;
1076
1077                 TAILQ_FOREACH(win, &new_ws->winlist, entry)
1078                         XMapRaised(display, win->id);
1079
1080                 TAILQ_FOREACH(win, &old_ws->winlist, entry)
1081                         XUnmapWindow(display, win->id);
1082         } else {
1083                 other_r->ws = old_ws;
1084                 old_ws->r = other_r;
1085         }
1086         this_r->ws = new_ws;
1087         new_ws->r = this_r;
1088
1089         ignore_enter = 1;
1090         /* set focus */
1091         if (new_ws->focus == NULL)
1092                 new_ws->focus = TAILQ_FIRST(&new_ws->winlist);
1093         if (new_ws->focus)
1094                 focus_win(new_ws->focus);
1095         stack();
1096         bar_update();
1097 }
1098
1099 void
1100 cyclews(struct swm_region *r, union arg *args)
1101 {
1102         union                   arg a;
1103         struct swm_screen       *s = r->s;
1104
1105         DNPRINTF(SWM_D_WS, "cyclews id %d "
1106             "in screen[%d]:%dx%d+%d+%d ws %d\n", args->id,
1107             r->s->idx, WIDTH(r), HEIGHT(r), X(r), Y(r), r->ws->idx);
1108
1109         a.id = r->ws->idx;
1110         do {
1111                 switch (args->id) {
1112                 case SWM_ARG_ID_CYCLEWS_UP:
1113                         if (a.id < SWM_WS_MAX - 1)
1114                                 a.id++;
1115                         else
1116                                 a.id = 0;
1117                         break;
1118                 case SWM_ARG_ID_CYCLEWS_DOWN:
1119                         if (a.id > 0)
1120                                 a.id--;
1121                         else
1122                                 a.id = SWM_WS_MAX - 1;
1123                         break;
1124                 default:
1125                         return;
1126                 };
1127
1128                 if (cycle_empty == 0 && TAILQ_EMPTY(&s->ws[a.id].winlist))
1129                         continue;
1130                 if (cycle_visible == 0 && s->ws[a.id].r != NULL)
1131                         continue;
1132
1133                 switchws(r, &a);
1134         } while (a.id != r->ws->idx);
1135 }
1136
1137 void
1138 cyclescr(struct swm_region *r, union arg *args)
1139 {
1140         struct swm_region       *rr;
1141         int                     i;
1142
1143         i = r->s->idx;
1144         switch (args->id) {
1145         case SWM_ARG_ID_CYCLESC_UP:
1146                 rr = TAILQ_NEXT(r, entry);
1147                 if (rr == NULL)
1148                         rr = TAILQ_FIRST(&screens[i].rl);
1149                 break;
1150         case SWM_ARG_ID_CYCLESC_DOWN:
1151                 rr = TAILQ_PREV(r, swm_region_list, entry);
1152                 if (rr == NULL)
1153                         rr = TAILQ_LAST(&screens[i].rl, swm_region_list);
1154                 break;
1155         default:
1156                 return;
1157         };
1158         unfocus_all();
1159         XSetInputFocus(display, PointerRoot, RevertToPointerRoot, CurrentTime);
1160         XWarpPointer(display, None, rr->s[i].root, 0, 0, 0, 0, rr->g.x,
1161             rr->g.y + bar_enabled ? bar_height : 0);
1162 }
1163
1164 void
1165 swapwin(struct swm_region *r, union arg *args)
1166 {
1167         struct ws_win           *target, *source;
1168         struct ws_win_list      *wl;
1169
1170
1171         DNPRINTF(SWM_D_WS, "swapwin id %d "
1172             "in screen %d region %dx%d+%d+%d ws %d\n", args->id,
1173             r->s->idx, WIDTH(r), HEIGHT(r), X(r), Y(r), r->ws->idx);
1174         if (cur_focus == NULL)
1175                 return;
1176
1177         source = cur_focus;
1178         wl = &source->ws->winlist;
1179
1180         switch (args->id) {
1181         case SWM_ARG_ID_SWAPPREV:
1182                 target = TAILQ_PREV(source, ws_win_list, entry);
1183                 TAILQ_REMOVE(wl, cur_focus, entry);
1184                 if (target == NULL)
1185                         TAILQ_INSERT_TAIL(wl, source, entry);
1186                 else
1187                         TAILQ_INSERT_BEFORE(target, source, entry);
1188                 break;
1189         case SWM_ARG_ID_SWAPNEXT:
1190                 target = TAILQ_NEXT(source, entry);
1191                 TAILQ_REMOVE(wl, source, entry);
1192                 if (target == NULL)
1193                         TAILQ_INSERT_HEAD(wl, source, entry);
1194                 else
1195                         TAILQ_INSERT_AFTER(wl, target, source, entry);
1196                 break;
1197         case SWM_ARG_ID_SWAPMAIN:
1198                 target = TAILQ_FIRST(wl);
1199                 if (target == source) {
1200                         if (source->ws->focus_prev != NULL &&
1201                             source->ws->focus_prev != target)
1202
1203                                 source = source->ws->focus_prev;
1204                         else
1205                                 return;
1206                 }
1207                 source->ws->focus_prev = target;
1208                 TAILQ_REMOVE(wl, target, entry);
1209                 TAILQ_INSERT_BEFORE(source, target, entry);
1210                 TAILQ_REMOVE(wl, source, entry);
1211                 TAILQ_INSERT_HEAD(wl, source, entry);
1212                 break;
1213         default:
1214                 DNPRINTF(SWM_D_MOVE, "invalid id: %d\n", args->id);
1215                 return;
1216         }
1217
1218         ignore_enter = 1;
1219         stack();
1220 }
1221
1222 void
1223 focus(struct swm_region *r, union arg *args)
1224 {
1225         struct ws_win           *winfocus, *winlostfocus;
1226         struct ws_win_list      *wl;
1227
1228         DNPRINTF(SWM_D_FOCUS, "focus: id %d\n", args->id);
1229         if (cur_focus == NULL)
1230                 return;
1231
1232         wl = &cur_focus->ws->winlist;
1233
1234         winlostfocus = cur_focus;
1235
1236         switch (args->id) {
1237         case SWM_ARG_ID_FOCUSPREV:
1238                 winfocus = TAILQ_PREV(cur_focus, ws_win_list, entry);
1239                 if (winfocus == NULL)
1240                         winfocus = TAILQ_LAST(wl, ws_win_list);
1241                 break;
1242
1243         case SWM_ARG_ID_FOCUSNEXT:
1244                 winfocus = TAILQ_NEXT(cur_focus, entry);
1245                 if (winfocus == NULL)
1246                         winfocus = TAILQ_FIRST(wl);
1247                 break;
1248
1249         case SWM_ARG_ID_FOCUSMAIN:
1250                 winfocus = TAILQ_FIRST(wl);
1251                 if (winfocus == cur_focus)
1252                         winfocus = cur_focus->ws->focus_prev;
1253                 if (winfocus == NULL)
1254                         return;
1255                 break;
1256
1257         default:
1258                 return;
1259         }
1260
1261         if (winfocus == winlostfocus || winfocus == NULL)
1262                 return;
1263
1264         XMapRaised(display, winfocus->id);
1265         focus_win(winfocus);
1266         XSync(display, False);
1267 }
1268
1269 void
1270 cycle_layout(struct swm_region *r, union arg *args)
1271 {
1272         struct workspace        *ws = r->ws;
1273
1274         DNPRINTF(SWM_D_EVENT, "cycle_layout: workspace: %d\n", ws->idx);
1275
1276         ws->cur_layout++;
1277         if (ws->cur_layout->l_stack == NULL)
1278                 ws->cur_layout = &layouts[0];
1279         ignore_enter = 1;
1280         stack();
1281 }
1282
1283 void
1284 stack_config(struct swm_region *r, union arg *args)
1285 {
1286         struct workspace        *ws = r->ws;
1287
1288         DNPRINTF(SWM_D_STACK, "stack_config for workspace %d (id %d\n",
1289             args->id, ws->idx);
1290
1291         if (ws->cur_layout->l_config != NULL)
1292                 ws->cur_layout->l_config(ws, args->id);
1293
1294         if (args->id != SWM_ARG_ID_STACKINIT);
1295                 stack();
1296 }
1297
1298 void
1299 stack(void) {
1300         struct swm_geometry     g;
1301         struct swm_region       *r;
1302         int                     i, j;
1303
1304         DNPRINTF(SWM_D_STACK, "stack\n");
1305
1306         for (i = 0; i < ScreenCount(display); i++) {
1307                 j = 0;
1308                 TAILQ_FOREACH(r, &screens[i].rl, entry) {
1309                         DNPRINTF(SWM_D_STACK, "stacking workspace %d "
1310                             "(screen %d, region %d)\n", r->ws->idx, i, j++);
1311
1312                         /* start with screen geometry, adjust for bar */
1313                         g = r->g;
1314                         g.w -= 2;
1315                         g.h -= 2;
1316                         if (bar_enabled) {
1317                                 g.y += bar_height;
1318                                 g.h -= bar_height;
1319                         }
1320
1321                         r->ws->restack = 0;
1322                         r->ws->cur_layout->l_stack(r->ws, &g);
1323                 }
1324         }
1325         if (font_adjusted)
1326                 font_adjusted--;
1327         XSync(display, False);
1328 }
1329
1330 void
1331 stack_floater(struct ws_win *win, struct swm_region *r)
1332 {
1333         unsigned int            mask;
1334         XWindowChanges          wc;
1335
1336         bzero(&wc, sizeof wc);
1337         mask = CWX | CWY | CWBorderWidth | CWWidth | CWHeight;
1338         if ((win->quirks & SWM_Q_FULLSCREEN) && (win->g.w == WIDTH(r)) &&
1339             (win->g.h == HEIGHT(r)))
1340                 wc.border_width = 0;
1341         else
1342                 wc.border_width = 1;
1343         if (win->transient && (win->quirks & SWM_Q_TRANSSZ)) {
1344                 win->g.w = (double)WIDTH(r) * dialog_ratio;
1345                 win->g.h = (double)HEIGHT(r) * dialog_ratio;
1346         }
1347         wc.width = win->g.w;
1348         wc.height = win->g.h;
1349         if (win->manual) {
1350                 wc.x = win->g.x;
1351                 wc.y = win->g.y;
1352         } else {
1353                 wc.x = (WIDTH(r) - win->g.w) / 2;
1354                 wc.y = (HEIGHT(r) - win->g.h) / 2;
1355         }
1356
1357         DNPRINTF(SWM_D_STACK, "stack_floater: win %lu x %d y %d w %d h %d\n",
1358             win->id, wc.x, wc.y, wc.width, wc.height);
1359
1360         XConfigureWindow(display, win->id, mask, &wc);
1361 }
1362
1363 /*
1364  * Send keystrokes to terminal to decrease/increase the font size as the
1365  * window size changes.
1366  */
1367 void
1368 adjust_font(struct ws_win *win)
1369 {
1370         if (!(win->quirks & SWM_Q_XTERM_FONTADJ) ||
1371             win->floating || win->transient)
1372                 return;
1373
1374         if (win->sh.width_inc && win->last_inc != win->sh.width_inc &&
1375             win->g.w / win->sh.width_inc < term_width &&
1376             win->font_steps < SWM_MAX_FONT_STEPS) {
1377                 win->font_size_boundary[win->font_steps] =
1378                     (win->sh.width_inc * term_width) + win->sh.base_width;
1379                 win->font_steps++;
1380                 font_adjusted++;
1381                 win->last_inc = win->sh.width_inc;
1382                 fake_keypress(win, XK_KP_Subtract, ShiftMask);
1383         } else if (win->font_steps && win->last_inc != win->sh.width_inc &&
1384             win->g.w > win->font_size_boundary[win->font_steps - 1]) {
1385                 win->font_steps--;
1386                 font_adjusted++;
1387                 win->last_inc = win->sh.width_inc;
1388                 fake_keypress(win, XK_KP_Add, ShiftMask);
1389         }
1390 }
1391
1392 #define SWAPXY(g)       do {                            \
1393         int tmp;                                        \
1394         tmp = (g)->y; (g)->y = (g)->x; (g)->x = tmp;    \
1395         tmp = (g)->h; (g)->h = (g)->w; (g)->w = tmp;    \
1396 } while (0)
1397 void
1398 stack_master(struct workspace *ws, struct swm_geometry *g, int rot, int flip)
1399 {
1400         XWindowChanges          wc;
1401         struct swm_geometry     win_g, r_g = *g;
1402         struct ws_win           *win, *winfocus;
1403         int                     i, j, s, stacks;
1404         int                     w_inc = 1, h_inc, w_base = 1, h_base;
1405         int                     hrh, extra = 0, h_slice, last_h = 0;
1406         int                     split, colno, winno, mwin, msize, mscale;
1407         int                     remain, missing, v_slice;
1408         unsigned int            mask;
1409
1410         DNPRINTF(SWM_D_STACK, "stack_master: workspace: %d\n rot=%s flip=%s",
1411             ws->idx, rot ? "yes" : "no", flip ? "yes" : "no");
1412
1413         if ((winno = count_win(ws, 0)) == 0)
1414                 return;
1415
1416         if (ws->focus == NULL)
1417                 ws->focus = TAILQ_FIRST(&ws->winlist);
1418         winfocus = cur_focus ? cur_focus : ws->focus;
1419
1420         TAILQ_FOREACH(win, &ws->winlist, entry)
1421                 if (win->transient == 0 && win->floating == 0)
1422                         break;
1423
1424         if (win == NULL)
1425                 goto notiles;
1426
1427         if (rot) {
1428                 w_inc = win->sh.width_inc;
1429                 w_base = win->sh.base_width;
1430                 mwin = ws->l_state.horizontal_mwin;
1431                 mscale = ws->l_state.horizontal_msize;
1432                 stacks = ws->l_state.horizontal_stacks;
1433                 SWAPXY(&r_g);
1434         } else {
1435                 w_inc = win->sh.height_inc;
1436                 w_base = win->sh.base_height;
1437                 mwin = ws->l_state.vertical_mwin;
1438                 mscale = ws->l_state.vertical_msize;
1439                 stacks = ws->l_state.vertical_stacks;
1440         }
1441         win_g = r_g;
1442
1443         if (stacks > winno - mwin)
1444                 stacks = winno - mwin;
1445         if (stacks < 1)
1446                 stacks = 1;
1447
1448         h_slice = r_g.h / SWM_H_SLICE;
1449         if (mwin && winno > mwin) {
1450                 v_slice = r_g.w / SWM_V_SLICE;
1451
1452                 split = mwin;
1453                 colno = split;
1454                 win_g.w = v_slice * mscale;
1455
1456                 if (w_inc > 1 && w_inc < v_slice) {
1457                         /* adjust for window's requested size increment */
1458                         remain = (win_g.w - w_base) % w_inc;
1459                         missing = w_inc - remain;
1460                         win_g.w -= remain;
1461                         extra += remain;
1462                 }
1463
1464                 msize = win_g.w;
1465                 if (flip)
1466                         win_g.x += r_g.w - msize;
1467         } else {
1468                 msize = -2;
1469                 colno = split = winno / stacks;
1470                 win_g.w = ((r_g.w - (stacks * 2) + 2) / stacks);
1471         }
1472         hrh = r_g.h / colno;
1473         extra = r_g.h - (colno * hrh);
1474         win_g.h = hrh - 2;
1475
1476         /*  stack all the tiled windows */
1477         i = j = 0, s = stacks;
1478         TAILQ_FOREACH(win, &ws->winlist, entry) {
1479                 if (win->transient != 0 || win->floating != 0)
1480                         continue;
1481
1482                 if (split && i == split) {
1483                         colno = (winno - mwin) / stacks;
1484                         if (s <= (winno - mwin) % stacks)
1485                                 colno++;
1486                         split = split + colno;
1487                         hrh = (r_g.h / colno);
1488                         extra = r_g.h - (colno * hrh);
1489                         if (flip)
1490                                 win_g.x = r_g.x;
1491                         else
1492                                 win_g.x += win_g.w + 2;
1493                         win_g.w = (r_g.w - msize - (stacks * 2)) / stacks;
1494                         if (s == 1)
1495                                 win_g.w += (r_g.w - msize - (stacks * 2)) %
1496                                     stacks;
1497                         s--;
1498                         j = 0;
1499                 }
1500                 win_g.h = hrh - 2;
1501                 if (rot) {
1502                         h_inc = win->sh.width_inc;
1503                         h_base = win->sh.base_width;
1504                 } else {
1505                         h_inc = win->sh.height_inc;
1506                         h_base = win->sh.base_height;
1507                 }
1508                 if (j == colno - 1) {
1509                         win_g.h = hrh + extra;
1510                 } else if (h_inc > 1 && h_inc < h_slice) {
1511                         /* adjust for window's requested size increment */
1512                         remain = (win_g.h - h_base) % h_inc;
1513                         missing = h_inc - remain;
1514
1515                         if (missing <= extra || j == 0) {
1516                                 extra -= missing;
1517                                 win_g.h += missing;
1518                         } else {
1519                                 win_g.h -= remain;
1520                                 extra += remain;
1521                         }
1522                 }
1523
1524                 if (j == 0)
1525                         win_g.y = r_g.y;
1526                 else
1527                         win_g.y += last_h + 2;
1528
1529                 bzero(&wc, sizeof wc);
1530                 wc.border_width = 1;
1531                 if (rot) {
1532                         win->g.x = wc.x = win_g.y;
1533                         win->g.y = wc.y = win_g.x;
1534                         win->g.w = wc.width = win_g.h;
1535                         win->g.h = wc.height = win_g.w;
1536                 } else {
1537                         win->g.x = wc.x = win_g.x;
1538                         win->g.y = wc.y = win_g.y;
1539                         win->g.w = wc.width = win_g.w;
1540                         win->g.h = wc.height = win_g.h;
1541                 }
1542                 adjust_font(win);
1543                 mask = CWX | CWY | CWWidth | CWHeight | CWBorderWidth;
1544                 XConfigureWindow(display, win->id, mask, &wc);
1545                 XMapRaised(display, win->id);
1546
1547                 last_h = win_g.h;
1548                 i++;
1549                 j++;
1550         }
1551
1552  notiles:
1553         /* now, stack all the floaters and transients */
1554         TAILQ_FOREACH(win, &ws->winlist, entry) {
1555                 if (win->transient == 0 && win->floating == 0)
1556                         continue;
1557
1558                 stack_floater(win, ws->r);
1559                 XMapRaised(display, win->id);
1560         }
1561
1562         if (winfocus)
1563                 focus_win(winfocus); /* has to be done outside of the loop */
1564 }
1565
1566 void
1567 vertical_config(struct workspace *ws, int id)
1568 {
1569         DNPRINTF(SWM_D_STACK, "vertical_resize: workspace: %d\n", ws->idx);
1570
1571         switch (id) {
1572         case SWM_ARG_ID_STACKRESET:
1573         case SWM_ARG_ID_STACKINIT:
1574                 ws->l_state.vertical_msize = SWM_V_SLICE / 2;
1575                 ws->l_state.vertical_mwin = 1;
1576                 ws->l_state.vertical_stacks = 1;
1577                 break;
1578         case SWM_ARG_ID_MASTERSHRINK:
1579                 if (ws->l_state.vertical_msize > 1)
1580                         ws->l_state.vertical_msize--;
1581                 break;
1582         case SWM_ARG_ID_MASTERGROW:
1583                 if (ws->l_state.vertical_msize < SWM_V_SLICE - 1)
1584                         ws->l_state.vertical_msize++;
1585                 break;
1586         case SWM_ARG_ID_MASTERADD:
1587                 ws->l_state.vertical_mwin++;
1588                 break;
1589         case SWM_ARG_ID_MASTERDEL:
1590                 if (ws->l_state.vertical_mwin > 0)
1591                         ws->l_state.vertical_mwin--;
1592                 break;
1593         case SWM_ARG_ID_STACKINC:
1594                 ws->l_state.vertical_stacks++;
1595                 break;
1596         case SWM_ARG_ID_STACKDEC:
1597                 if (ws->l_state.vertical_stacks > 1)
1598                         ws->l_state.vertical_stacks--;
1599                 break;
1600         default:
1601                 return;
1602         }
1603 }
1604
1605 void
1606 vertical_stack(struct workspace *ws, struct swm_geometry *g)
1607 {
1608         DNPRINTF(SWM_D_STACK, "vertical_stack: workspace: %d\n", ws->idx);
1609
1610         stack_master(ws, g, 0, 0);
1611 }
1612
1613 void
1614 horizontal_config(struct workspace *ws, int id)
1615 {
1616         DNPRINTF(SWM_D_STACK, "horizontal_config: workspace: %d\n", ws->idx);
1617
1618         switch (id) {
1619         case SWM_ARG_ID_STACKRESET:
1620         case SWM_ARG_ID_STACKINIT:
1621                 ws->l_state.horizontal_mwin = 1;
1622                 ws->l_state.horizontal_msize = SWM_H_SLICE / 2;
1623                 ws->l_state.horizontal_stacks = 1;
1624                 break;
1625         case SWM_ARG_ID_MASTERSHRINK:
1626                 if (ws->l_state.horizontal_msize > 1)
1627                         ws->l_state.horizontal_msize--;
1628                 break;
1629         case SWM_ARG_ID_MASTERGROW:
1630                 if (ws->l_state.horizontal_msize < SWM_H_SLICE - 1)
1631                         ws->l_state.horizontal_msize++;
1632                 break;
1633         case SWM_ARG_ID_MASTERADD:
1634                 ws->l_state.horizontal_mwin++;
1635                 break;
1636         case SWM_ARG_ID_MASTERDEL:
1637                 if (ws->l_state.horizontal_mwin > 0)
1638                         ws->l_state.horizontal_mwin--;
1639                 break;
1640         case SWM_ARG_ID_STACKINC:
1641                 ws->l_state.horizontal_stacks++;
1642                 break;
1643         case SWM_ARG_ID_STACKDEC:
1644                 if (ws->l_state.horizontal_stacks > 1)
1645                         ws->l_state.horizontal_stacks--;
1646                 break;
1647         default:
1648                 return;
1649         }
1650 }
1651
1652 void
1653 horizontal_stack(struct workspace *ws, struct swm_geometry *g)
1654 {
1655         DNPRINTF(SWM_D_STACK, "vertical_stack: workspace: %d\n", ws->idx);
1656
1657         stack_master(ws, g, 1, 0);
1658 }
1659
1660 /* fullscreen view */
1661 void
1662 max_stack(struct workspace *ws, struct swm_geometry *g) {
1663         XWindowChanges          wc;
1664         struct swm_geometry     gg = *g;
1665         struct ws_win           *win, *winfocus;
1666         unsigned int            mask;
1667
1668         DNPRINTF(SWM_D_STACK, "max_stack: workspace: %d\n", ws->idx);
1669
1670         if (count_win(ws, 0) == 0)
1671                 return;
1672
1673         if (ws->focus == NULL)
1674                 ws->focus = TAILQ_FIRST(&ws->winlist);
1675         winfocus = cur_focus ? cur_focus : ws->focus;
1676
1677         TAILQ_FOREACH(win, &ws->winlist, entry) {
1678                 if (win->transient != 0 || win->floating != 0) {
1679                         if (win == ws->focus) {
1680                                 /* XXX maximize? */
1681                                 stack_floater(win, ws->r);
1682                                 XMapRaised(display, win->id);
1683                         } else
1684                                 XUnmapWindow(display, win->id);
1685                 } else {
1686                         bzero(&wc, sizeof wc);
1687                         wc.border_width = 1;
1688                         win->g.x = wc.x = gg.x;
1689                         win->g.y = wc.y = gg.y;
1690                         win->g.w = wc.width = gg.w;
1691                         win->g.h = wc.height = gg.h;
1692                         mask = CWX | CWY | CWWidth | CWHeight | CWBorderWidth;
1693                         XConfigureWindow(display, win->id, mask, &wc);
1694
1695                         if (win == ws->focus) {
1696                                 XMapRaised(display, win->id);
1697                         } else
1698                                 XUnmapWindow(display, win->id);
1699                 }
1700         }
1701
1702         if (winfocus)
1703                 focus_win(winfocus); /* has to be done outside of the loop */
1704 }
1705
1706 void
1707 send_to_ws(struct swm_region *r, union arg *args)
1708 {
1709         int                     wsid = args->id;
1710         struct ws_win           *win = cur_focus;
1711         struct workspace        *ws, *nws;
1712         Atom                    ws_idx_atom = 0;
1713         unsigned char           ws_idx_str[SWM_PROPLEN];
1714
1715         if (win == NULL)
1716                 return;
1717
1718         DNPRINTF(SWM_D_MOVE, "send_to_ws: win: %lu\n", win->id);
1719
1720         ws = win->ws;
1721         nws = &win->s->ws[wsid];
1722
1723         XUnmapWindow(display, win->id);
1724
1725         /* find a window to focus */
1726         ws->focus = TAILQ_PREV(win, ws_win_list, entry);
1727         if (ws->focus == NULL)
1728                 ws->focus = TAILQ_FIRST(&ws->winlist);
1729         if (ws->focus == win)
1730                 ws->focus = NULL;
1731
1732         TAILQ_REMOVE(&ws->winlist, win, entry);
1733
1734         TAILQ_INSERT_TAIL(&nws->winlist, win, entry);
1735         win->ws = nws;
1736
1737         /* Try to update the window's workspace property */
1738         ws_idx_atom = XInternAtom(display, "_SWM_WS", False);
1739         if (ws_idx_atom &&
1740             snprintf(ws_idx_str, SWM_PROPLEN, "%d", nws->idx) < SWM_PROPLEN) {
1741                 DNPRINTF(SWM_D_PROP, "setting property _SWM_WS to %s\n",
1742                     ws_idx_str);
1743                 XChangeProperty(display, win->id, ws_idx_atom, XA_STRING, 8,
1744                     PropModeReplace, ws_idx_str, SWM_PROPLEN);
1745         }
1746
1747         if (count_win(nws, 1) == 1)
1748                 nws->focus = win;
1749         ws->restack = 1;
1750         nws->restack = 1;
1751
1752         stack();
1753 }
1754
1755 void
1756 wkill(struct swm_region *r, union arg *args)
1757 {
1758         DNPRINTF(SWM_D_MISC, "wkill %d\n", args->id);
1759
1760         if(r->ws->focus == NULL)
1761                 return;
1762
1763         if (args->id == SWM_ARG_ID_KILLWINDOW)
1764                 XKillClient(display, r->ws->focus->id);
1765         else
1766                 if (r->ws->focus->can_delete)
1767                         client_msg(r->ws->focus, adelete);
1768 }
1769
1770 void
1771 screenshot(struct swm_region *r, union arg *args)
1772 {
1773         union arg                       a;
1774
1775         DNPRINTF(SWM_D_MISC, "screenshot\n");
1776
1777         if (ss_enabled == 0)
1778                 return;
1779
1780         switch (args->id) {
1781         case SWM_ARG_ID_SS_ALL:
1782                 spawn_screenshot[1] = "full";
1783                 break;
1784         case SWM_ARG_ID_SS_WINDOW:
1785                 spawn_screenshot[1] = "window";
1786                 break;
1787         default:
1788                 return;
1789         }
1790         a.argv = spawn_screenshot;
1791         spawn(r, &a);
1792 }
1793
1794 void
1795 floating_toggle(struct swm_region *r, union arg *args)
1796 {
1797         struct ws_win   *win = cur_focus;
1798
1799         if (win == NULL)
1800                 return;
1801
1802         win->floating = !win->floating;
1803         win->manual = 0;
1804         stack();
1805         focus_win(win);
1806 }
1807
1808 void
1809 resize_window(struct ws_win *win, int center)
1810 {
1811         unsigned int            mask;
1812         XWindowChanges          wc;
1813         struct swm_region       *r;
1814
1815         r = root_to_region(win->wa.root);
1816         bzero(&wc, sizeof wc);
1817         mask = CWBorderWidth | CWWidth | CWHeight;
1818         wc.border_width = 1;
1819         wc.width = win->g.w;
1820         wc.height = win->g.h;
1821         if (center == SWM_ARG_ID_CENTER) {
1822                 wc.x = (WIDTH(r) - win->g.w) / 2;
1823                 wc.y = (HEIGHT(r) - win->g.h) / 2;
1824                 mask |= CWX | CWY;
1825         }
1826
1827         DNPRINTF(SWM_D_STACK, "resize_window: win %lu x %d y %d w %d h %d\n",
1828             win->id, wc.x, wc.y, wc.width, wc.height);
1829
1830         XConfigureWindow(display, win->id, mask, &wc);
1831         config_win(win);
1832 }
1833
1834 void
1835 resize(struct ws_win *win, union arg *args)
1836 {
1837         XEvent                  ev;
1838         Time                    time = 0;
1839
1840         DNPRINTF(SWM_D_MOUSE, "resize: win %lu floating %d trans %d\n",
1841             win->id, win->floating, win->transient);
1842
1843         if (!(win->transient != 0 || win->floating != 0))
1844                 return;
1845
1846         if (XGrabPointer(display, win->id, False, MOUSEMASK, GrabModeAsync,
1847             GrabModeAsync, None, None /* cursor */, CurrentTime) != GrabSuccess)
1848                 return;
1849         XWarpPointer(display, None, win->id, 0, 0, 0, 0, win->g.w, win->g.h);
1850         do {
1851                 XMaskEvent(display, MOUSEMASK | ExposureMask |
1852                     SubstructureRedirectMask, &ev);
1853                 switch(ev.type) {
1854                 case ConfigureRequest:
1855                 case Expose:
1856                 case MapRequest:
1857                         handler[ev.type](&ev);
1858                         break;
1859                 case MotionNotify:
1860                         if (ev.xmotion.x <= 1)
1861                                 ev.xmotion.x = 1;
1862                         if (ev.xmotion.y <= 1)
1863                                 ev.xmotion.y = 1;
1864                         win->g.w = ev.xmotion.x;
1865                         win->g.h = ev.xmotion.y;
1866
1867                         /* not free, don't sync more than 60 times / second */
1868                         if ((ev.xmotion.time - time) > (1000 / 60) ) {
1869                                 time = ev.xmotion.time;
1870                                 XSync(display, False);
1871                                 resize_window(win, args->id);
1872                         }
1873                         break;
1874                 }
1875         } while (ev.type != ButtonRelease);
1876         if (time) {
1877                 XSync(display, False);
1878                 resize_window(win, args->id);
1879         }
1880         XWarpPointer(display, None, win->id, 0, 0, 0, 0, win->g.w - 1,
1881             win->g.h - 1);
1882         XUngrabPointer(display, CurrentTime);
1883
1884         /* drain events */
1885         while (XCheckMaskEvent(display, EnterWindowMask, &ev));
1886 }
1887
1888 void
1889 move_window(struct ws_win *win)
1890 {
1891         unsigned int            mask;
1892         XWindowChanges          wc;
1893         struct swm_region       *r;
1894
1895         r = root_to_region(win->wa.root);
1896         bzero(&wc, sizeof wc);
1897         mask = CWX | CWY;
1898         wc.x = win->g.x;
1899         wc.y = win->g.y;
1900
1901         DNPRINTF(SWM_D_STACK, "move_window: win %lu x %d y %d w %d h %d\n",
1902             win->id, wc.x, wc.y, wc.width, wc.height);
1903
1904         XConfigureWindow(display, win->id, mask, &wc);
1905         config_win(win);
1906 }
1907
1908 void
1909 move(struct ws_win *win, union arg *args)
1910 {
1911         XEvent                  ev;
1912         Time                    time = 0;
1913         int                     restack = 0;
1914
1915         DNPRINTF(SWM_D_MOUSE, "move: win %lu floating %d trans %d\n",
1916             win->id, win->floating, win->transient);
1917
1918         if (win->floating == 0) {
1919                 win->floating = 1;
1920                 win->manual = 1;
1921                 restack = 1;
1922         }
1923
1924         if (XGrabPointer(display, win->id, False, MOUSEMASK, GrabModeAsync,
1925             GrabModeAsync, None, None /* cursor */, CurrentTime) != GrabSuccess)
1926                 return;
1927         XWarpPointer(display, None, win->id, 0, 0, 0, 0, 0, 0);
1928         do {
1929                 XMaskEvent(display, MOUSEMASK | ExposureMask |
1930                     SubstructureRedirectMask, &ev);
1931                 switch(ev.type) {
1932                 case ConfigureRequest:
1933                 case Expose:
1934                 case MapRequest:
1935                         handler[ev.type](&ev);
1936                         break;
1937                 case MotionNotify:
1938                         win->g.x = ev.xmotion.x_root;
1939                         win->g.y = ev.xmotion.y_root;
1940
1941                         /* not free, don't sync more than 60 times / second */
1942                         if ((ev.xmotion.time - time) > (1000 / 60) ) {
1943                                 time = ev.xmotion.time;
1944                                 XSync(display, False);
1945                                 move_window(win);
1946                         }
1947                         break;
1948                 }
1949         } while (ev.type != ButtonRelease);
1950         if (time) {
1951                 XSync(display, False);
1952                 move_window(win);
1953         }
1954         XWarpPointer(display, None, win->id, 0, 0, 0, 0, 0, 0);
1955         XUngrabPointer(display, CurrentTime);
1956         if (restack)
1957                 stack();
1958
1959         /* drain events */
1960         while (XCheckMaskEvent(display, EnterWindowMask, &ev));
1961 }
1962
1963 /* key definitions */
1964 struct keyfunc {
1965         char                    name[SWM_FUNCNAME_LEN];
1966         void                    (*func)(struct swm_region *r, union arg *);
1967         union arg               args;
1968 } keyfuncs[kf_invalid] = {
1969         /* name                 function        argument */
1970         { "cycle_layout",       cycle_layout,   {0} },
1971         { "stack_reset",        stack_config,   {.id = SWM_ARG_ID_STACKRESET} },
1972         { "master_shrink",      stack_config,   {.id = SWM_ARG_ID_MASTERSHRINK} },
1973         { "master_grow",        stack_config,   {.id = SWM_ARG_ID_MASTERGROW} },
1974         { "master_add",         stack_config,   {.id = SWM_ARG_ID_MASTERADD} },
1975         { "master_del",         stack_config,   {.id = SWM_ARG_ID_MASTERDEL} },
1976         { "stack_inc",          stack_config,   {.id = SWM_ARG_ID_STACKINC} },
1977         { "stack_dec",          stack_config,   {.id = SWM_ARG_ID_STACKDEC} },
1978         { "swap_main",          swapwin,        {.id = SWM_ARG_ID_SWAPMAIN} },
1979         { "focus_next",         focus,          {.id = SWM_ARG_ID_FOCUSNEXT} },
1980         { "focus_prev",         focus,          {.id = SWM_ARG_ID_FOCUSPREV} },
1981         { "swap_next",          swapwin,        {.id = SWM_ARG_ID_SWAPNEXT} },
1982         { "swap_prev",          swapwin,        {.id = SWM_ARG_ID_SWAPPREV} },
1983         { "spawn_term",         spawnterm,      {.argv = spawn_term} },
1984         { "spawn_menu",         spawnmenu,      {.argv = spawn_menu} },
1985         { "quit",               quit,           {0} },
1986         { "restart",            restart,        {0} },
1987         { "focus_main",         focus,          {.id = SWM_ARG_ID_FOCUSMAIN} },
1988         { "ws_1",               switchws,       {.id = 0} },
1989         { "ws_2",               switchws,       {.id = 1} },
1990         { "ws_3",               switchws,       {.id = 2} },
1991         { "ws_4",               switchws,       {.id = 3} },
1992         { "ws_5",               switchws,       {.id = 4} },
1993         { "ws_6",               switchws,       {.id = 5} },
1994         { "ws_7",               switchws,       {.id = 6} },
1995         { "ws_8",               switchws,       {.id = 7} },
1996         { "ws_9",               switchws,       {.id = 8} },
1997         { "ws_10",              switchws,       {.id = 9} },
1998         { "ws_next",            cyclews,        {.id = SWM_ARG_ID_CYCLEWS_UP} },
1999         { "ws_prev",            cyclews,        {.id = SWM_ARG_ID_CYCLEWS_DOWN} },
2000         { "screen_next",        cyclescr,       {.id = SWM_ARG_ID_CYCLESC_UP} },
2001         { "screen_prev",        cyclescr,       {.id = SWM_ARG_ID_CYCLESC_DOWN} },
2002         { "mvws_1",             send_to_ws,     {.id = 0} },
2003         { "mvws_2",             send_to_ws,     {.id = 1} },
2004         { "mvws_3",             send_to_ws,     {.id = 2} },
2005         { "mvws_4",             send_to_ws,     {.id = 3} },
2006         { "mvws_5",             send_to_ws,     {.id = 4} },
2007         { "mvws_6",             send_to_ws,     {.id = 5} },
2008         { "mvws_7",             send_to_ws,     {.id = 6} },
2009         { "mvws_8",             send_to_ws,     {.id = 7} },
2010         { "mvws_9",             send_to_ws,     {.id = 8} },
2011         { "mvws_10",            send_to_ws,     {.id = 9} },
2012         { "bar_toggle",         bar_toggle,     {0} },
2013         { "wind_kill",          wkill,          {.id = SWM_ARG_ID_KILLWINDOW} },
2014         { "wind_del",           wkill,          {.id = SWM_ARG_ID_DELETEWINDOW} },
2015         { "screenshot_all",     screenshot,     {.id = SWM_ARG_ID_SS_ALL} },
2016         { "screenshot_wind",    screenshot,     {.id = SWM_ARG_ID_SS_WINDOW} },
2017         { "float_toggle",       floating_toggle,{0} },
2018         { "version",            version,        {0} },
2019         { "spawn_lock",         spawn,          {.argv = spawn_lock} },
2020         { "spawn_initscr",      spawn,          {.argv = spawn_initscr} },
2021 };
2022 struct key {
2023         unsigned int            mod;
2024         KeySym                  keysym;
2025         enum keyfuncid          funcid;
2026 };
2027 int                             keys_size = 0, keys_length = 0;
2028 struct key                      *keys = NULL;
2029
2030 /* mouse */
2031 enum { client_click, root_click };
2032 struct button {
2033         unsigned int            action;
2034         unsigned int            mask;
2035         unsigned int            button;
2036         void                    (*func)(struct ws_win *, union arg *);
2037         union arg               args;
2038 } buttons[] = {
2039           /* action     key             mouse button    func    args */
2040         { client_click, MODKEY,         Button3,        resize, {.id = SWM_ARG_ID_DONTCENTER} },
2041         { client_click, MODKEY | ShiftMask, Button3,    resize, {.id = SWM_ARG_ID_CENTER} },
2042         { client_click, MODKEY,         Button1,        move,   {0} },
2043 };
2044
2045 void
2046 update_modkey(unsigned int mod)
2047 {
2048         int                     i;
2049
2050         mod_key = mod;
2051         for (i = 0; i < keys_length; i++)
2052                 if (keys[i].mod & ShiftMask)
2053                         keys[i].mod = mod | ShiftMask;
2054                 else
2055                         keys[i].mod = mod;
2056
2057         for (i = 0; i < LENGTH(buttons); i++)
2058                 if (buttons[i].mask & ShiftMask)
2059                         buttons[i].mask = mod | ShiftMask;
2060                 else
2061                         buttons[i].mask = mod;
2062 }
2063
2064 #define SWM_MODNAME_SIZE        32
2065 #define SWM_KEY_WS              "\n+ \t"
2066 int
2067 parsekeys(char *keystr, unsigned int currmod, unsigned int *mod, KeySym *ks)
2068 {
2069         char                    *cp, *name;
2070         KeySym                  uks;
2071         if (mod == NULL || ks == NULL)
2072                 return (0);
2073         cp = keystr;
2074         *mod = 0;
2075         while ((name = strsep(&cp, SWM_KEY_WS)) != NULL) {
2076                 if (cp)
2077                         cp += (long)strspn(cp, SWM_KEY_WS);
2078                 if (strncasecmp(name, "MOD", SWM_MODNAME_SIZE) == 0)
2079                         *mod |= currmod;
2080                 else if (!strncasecmp(name, "Mod1", SWM_MODNAME_SIZE))
2081                         *mod |= Mod1Mask;
2082                 else if (!strncasecmp(name, "Mod2", SWM_MODNAME_SIZE))
2083                         *mod += Mod2Mask;
2084                 else if (!strncmp(name, "Mod3", SWM_MODNAME_SIZE))
2085                         *mod |= Mod3Mask;
2086                 else if (!strncmp(name, "Mod4", SWM_MODNAME_SIZE))
2087                         *mod |= Mod4Mask;
2088                 else if (strncasecmp(name, "SHIFT", SWM_MODNAME_SIZE) == 0)
2089                         *mod |= ShiftMask;
2090                 else if (strncasecmp(name, "CONTROL", SWM_MODNAME_SIZE) == 0)
2091                         *mod |= ControlMask;
2092                 else {
2093                         *ks = XStringToKeysym(name);
2094                         XConvertCase(*ks, ks, &uks);
2095                         if (ks == NoSymbol) {
2096                                 DNPRINTF(SWM_D_KEY,
2097                                     "parsekeys: invalid key %s\n",
2098                                     name);
2099                                 return (0);
2100                         }
2101                 }
2102         }
2103         return (1);
2104 }
2105 void
2106 setkeybinding(unsigned int mod, KeySym ks, enum keyfuncid kfid)
2107 {
2108         int                     i, j;
2109         /* find existing */
2110         for (i = 0; i < keys_length; i++) {
2111                 if (keys[i].mod == mod && keys[i].keysym == ks) {
2112                         if (kfid == kf_invalid) {
2113                                 /* found: delete */
2114                                 DNPRINTF(SWM_D_KEY,
2115                                     "setkeybinding: delete #%d %s\n",
2116                                     i, keyfuncs[keys[i].funcid].name);
2117                                 j = keys_length - 1;
2118                                 if (i < j)
2119                                         keys[i] = keys[j];
2120                                 keys_length--;
2121                                 return;
2122                         } else {
2123                                 /* found: replace */
2124                                 DNPRINTF(SWM_D_KEY,
2125                                     "setkeybinding: replace #%d %s\n",
2126                                     i, keyfuncs[keys[i].funcid].name);
2127                                 keys[i].mod = mod;
2128                                 keys[i].keysym = ks;
2129                                 keys[i].funcid = kfid;
2130                                 return;
2131                         }
2132                 }
2133         }
2134         if (kfid == kf_invalid) {
2135                 fprintf(stderr,
2136                     "error: setkeybinding: cannot find mod/key combination");
2137                 return;
2138         }
2139         /* not found: add */
2140         if (keys_size == 0 || keys == NULL) {
2141                 keys_size = 4;
2142                 DNPRINTF(SWM_D_KEY, "setkeybinding: init list %d\n", keys_size);
2143                 keys = malloc((size_t)keys_size * sizeof(struct key));
2144                 if (!keys) {
2145                         fprintf(stderr, "malloc failed\n");
2146                         perror(" failed");
2147                         quit(NULL, NULL);
2148                 }
2149         } else if (keys_length == keys_size) {
2150                 keys_size *= 2;
2151                 DNPRINTF(SWM_D_KEY, "setkeybinding: grow list %d\n", keys_size);
2152                 keys = realloc(keys, (size_t)keys_size * sizeof(struct key));
2153                 if (!keys) {
2154                         fprintf(stderr, "realloc failed\n");
2155                         perror(" failed");
2156                         quit(NULL, NULL);
2157                 }
2158         }
2159         if (keys_length < keys_size) {
2160                 DNPRINTF(SWM_D_KEY, "setkeybinding: add %d\n", keys_length);
2161                 j = keys_length++;
2162                 keys[j].mod = mod;
2163                 keys[j].keysym = ks;
2164                 keys[j].funcid = kfid;
2165         } else {
2166                 fprintf(stderr, "keys array problem?\n");
2167                 if (!keys) {
2168                         fprintf(stderr, "keys array problem\n");
2169                         quit(NULL, NULL);
2170                 }
2171         }
2172 }
2173 int
2174 setconfbinding(char *selector, char *value, int flags)
2175 {
2176         enum keyfuncid          kfid;
2177         unsigned int            mod;
2178         KeySym                  ks;
2179         for (kfid = 0; kfid < kf_invalid; (kfid)++) {
2180                 if (strncasecmp(selector, keyfuncs[kfid].name,
2181                     SWM_FUNCNAME_LEN) == 0) {
2182                         if (parsekeys(value, mod_key, &mod, &ks))
2183                                 setkeybinding(mod, ks, kfid);
2184                         else
2185                                 return (0);
2186                 }
2187
2188         }
2189         return (1);
2190 }
2191 void
2192 setup_keys(void)
2193 {
2194         setkeybinding(MODKEY,           XK_space,       kf_cycle_layout);
2195         setkeybinding(MODKEY|ShiftMask, XK_space,       kf_stack_reset);
2196         setkeybinding(MODKEY,           XK_h,           kf_master_shrink);
2197         setkeybinding(MODKEY,           XK_l,           kf_master_grow);
2198         setkeybinding(MODKEY,           XK_comma,       kf_master_add);
2199         setkeybinding(MODKEY,           XK_period,      kf_master_del);
2200         setkeybinding(MODKEY|ShiftMask, XK_comma,       kf_stack_inc);
2201         setkeybinding(MODKEY|ShiftMask, XK_period,      kf_stack_dec);
2202         setkeybinding(MODKEY,           XK_Return,      kf_swap_main);
2203         setkeybinding(MODKEY,           XK_j,           kf_focus_next);
2204         setkeybinding(MODKEY,           XK_k,           kf_focus_prev);
2205         setkeybinding(MODKEY|ShiftMask, XK_j,           kf_swap_next);
2206         setkeybinding(MODKEY|ShiftMask, XK_k,           kf_swap_prev);
2207         setkeybinding(MODKEY|ShiftMask, XK_Return,      kf_spawn_term);
2208         setkeybinding(MODKEY,           XK_p,           kf_spawn_menu);
2209         setkeybinding(MODKEY|ShiftMask, XK_q,           kf_quit);
2210         setkeybinding(MODKEY,           XK_q,           kf_restart);
2211         setkeybinding(MODKEY,           XK_m,           kf_focus_main);
2212         setkeybinding(MODKEY,           XK_1,           kf_ws_1);
2213         setkeybinding(MODKEY,           XK_2,           kf_ws_2);
2214         setkeybinding(MODKEY,           XK_3,           kf_ws_3);
2215         setkeybinding(MODKEY,           XK_4,           kf_ws_4);
2216         setkeybinding(MODKEY,           XK_5,           kf_ws_5);
2217         setkeybinding(MODKEY,           XK_6,           kf_ws_6);
2218         setkeybinding(MODKEY,           XK_7,           kf_ws_7);
2219         setkeybinding(MODKEY,           XK_8,           kf_ws_8);
2220         setkeybinding(MODKEY,           XK_9,           kf_ws_9);
2221         setkeybinding(MODKEY,           XK_0,           kf_ws_10);
2222         setkeybinding(MODKEY,           XK_Right,       kf_ws_next);
2223         setkeybinding(MODKEY,           XK_Left,        kf_ws_prev);
2224         setkeybinding(MODKEY|ShiftMask, XK_Right,       kf_screen_next);
2225         setkeybinding(MODKEY|ShiftMask, XK_Left,        kf_screen_prev);
2226         setkeybinding(MODKEY|ShiftMask, XK_1,           kf_mvws_1);
2227         setkeybinding(MODKEY|ShiftMask, XK_2,           kf_mvws_2);
2228         setkeybinding(MODKEY|ShiftMask, XK_3,           kf_mvws_3);
2229         setkeybinding(MODKEY|ShiftMask, XK_4,           kf_mvws_4);
2230         setkeybinding(MODKEY|ShiftMask, XK_5,           kf_mvws_5);
2231         setkeybinding(MODKEY|ShiftMask, XK_6,           kf_mvws_6);
2232         setkeybinding(MODKEY|ShiftMask, XK_7,           kf_mvws_7);
2233         setkeybinding(MODKEY|ShiftMask, XK_8,           kf_mvws_8);
2234         setkeybinding(MODKEY|ShiftMask, XK_9,           kf_mvws_9);
2235         setkeybinding(MODKEY|ShiftMask, XK_0,           kf_mvws_10);
2236         setkeybinding(MODKEY,           XK_b,           kf_bar_toggle);
2237         setkeybinding(MODKEY,           XK_Tab,         kf_focus_next);
2238         setkeybinding(MODKEY|ShiftMask, XK_Tab,         kf_focus_prev);
2239         setkeybinding(MODKEY|ShiftMask, XK_x,           kf_wind_kill);
2240         setkeybinding(MODKEY,           XK_x,           kf_wind_del);
2241         setkeybinding(MODKEY,           XK_s,           kf_screenshot_all);
2242         setkeybinding(MODKEY|ShiftMask, XK_s,           kf_screenshot_wind);
2243         setkeybinding(MODKEY,           XK_t,           kf_float_toggle);
2244         setkeybinding(MODKEY|ShiftMask, XK_v,           kf_version);
2245         setkeybinding(MODKEY|ShiftMask, XK_Delete,      kf_spawn_lock);
2246         setkeybinding(MODKEY|ShiftMask, XK_i,           kf_spawn_initscr);
2247 }
2248 void
2249 updatenumlockmask(void)
2250 {
2251         unsigned int            i, j;
2252         XModifierKeymap         *modmap;
2253
2254         DNPRINTF(SWM_D_MISC, "updatenumlockmask\n");
2255         numlockmask = 0;
2256         modmap = XGetModifierMapping(display);
2257         for (i = 0; i < 8; i++)
2258                 for (j = 0; j < modmap->max_keypermod; j++)
2259                         if (modmap->modifiermap[i * modmap->max_keypermod + j]
2260                           == XKeysymToKeycode(display, XK_Num_Lock))
2261                                 numlockmask = (1 << i);
2262
2263         XFreeModifiermap(modmap);
2264 }
2265
2266 void
2267 grabkeys(void)
2268 {
2269         unsigned int            i, j, k;
2270         KeyCode                 code;
2271         unsigned int            modifiers[] =
2272             { 0, LockMask, numlockmask, numlockmask | LockMask };
2273
2274         DNPRINTF(SWM_D_MISC, "grabkeys\n");
2275         updatenumlockmask();
2276
2277         for (k = 0; k < ScreenCount(display); k++) {
2278                 if (TAILQ_EMPTY(&screens[k].rl))
2279                         continue;
2280                 XUngrabKey(display, AnyKey, AnyModifier, screens[k].root);
2281                 for (i = 0; i < keys_length; i++) {
2282                         if ((code = XKeysymToKeycode(display, keys[i].keysym)))
2283                                 for (j = 0; j < LENGTH(modifiers); j++)
2284                                         XGrabKey(display, code,
2285                                             keys[i].mod | modifiers[j],
2286                                             screens[k].root, True,
2287                                             GrabModeAsync, GrabModeAsync);
2288                 }
2289         }
2290 }
2291
2292 void
2293 grabbuttons(struct ws_win *win, int focused)
2294 {
2295         unsigned int            i, j;
2296         unsigned int            modifiers[] =
2297             { 0, LockMask, numlockmask, numlockmask|LockMask };
2298
2299         updatenumlockmask();
2300         XUngrabButton(display, AnyButton, AnyModifier, win->id);
2301         if(focused) {
2302                 for (i = 0; i < LENGTH(buttons); i++)
2303                         if (buttons[i].action == client_click)
2304                                 for (j = 0; j < LENGTH(modifiers); j++)
2305                                         XGrabButton(display, buttons[i].button,
2306                                             buttons[i].mask | modifiers[j],
2307                                             win->id, False, BUTTONMASK,
2308                                             GrabModeAsync, GrabModeSync, None,
2309                                             None);
2310         } else
2311                 XGrabButton(display, AnyButton, AnyModifier, win->id, False,
2312                     BUTTONMASK, GrabModeAsync, GrabModeSync, None, None);
2313 }
2314
2315 void
2316 expose(XEvent *e)
2317 {
2318         DNPRINTF(SWM_D_EVENT, "expose: window: %lu\n", e->xexpose.window);
2319 }
2320
2321 void
2322 keypress(XEvent *e)
2323 {
2324         unsigned int            i;
2325         KeySym                  keysym;
2326         XKeyEvent               *ev = &e->xkey;
2327
2328         DNPRINTF(SWM_D_EVENT, "keypress: window: %lu\n", ev->window);
2329
2330         keysym = XKeycodeToKeysym(display, (KeyCode)ev->keycode, 0);
2331         for (i = 0; i < keys_length; i++)
2332                 if (keysym == keys[i].keysym
2333                    && CLEANMASK(keys[i].mod) == CLEANMASK(ev->state)
2334                    && keyfuncs[keys[i].funcid].func)
2335                         keyfuncs[keys[i].funcid].func(
2336                             root_to_region(ev->root),
2337                             &(keyfuncs[keys[i].funcid].args)
2338                             );
2339 }
2340
2341 void
2342 buttonpress(XEvent *e)
2343 {
2344         XButtonPressedEvent     *ev = &e->xbutton;
2345
2346         struct ws_win           *win;
2347         int                     i, action;
2348
2349         DNPRINTF(SWM_D_EVENT, "buttonpress: window: %lu\n", ev->window);
2350
2351         action = root_click;
2352         if ((win = find_window(ev->window)) == NULL)
2353                 return;
2354         else {
2355                 focus_win(win);
2356                 action = client_click;
2357         }
2358
2359         for (i = 0; i < LENGTH(buttons); i++)
2360                 if (action == buttons[i].action && buttons[i].func &&
2361                     buttons[i].button == ev->button &&
2362                     CLEANMASK(buttons[i].mask) == CLEANMASK(ev->state))
2363                         buttons[i].func(win, &buttons[i].args);
2364 }
2365
2366 void
2367 set_win_state(struct ws_win *win, long state)
2368 {
2369         long                    data[] = {state, None};
2370
2371         DNPRINTF(SWM_D_EVENT, "set_win_state: window: %lu\n", win->id);
2372
2373         XChangeProperty(display, win->id, astate, astate, 32, PropModeReplace,
2374             (unsigned char *)data, 2);
2375 }
2376
2377 const char *quirkname[] = {
2378         "NONE",         /* config string for "no value" */
2379         "FLOAT",
2380         "TRANSSZ",
2381         "ANYWHERE",
2382         "XTERM_FONTADJ",
2383         "FULLSCREEN",
2384 };
2385
2386 /* SWM_Q_WS: retain '|' for back compat for now (2009-08-11) */
2387 #define SWM_Q_WS                "\n|+ \t"
2388 int
2389 parsequirks(char *qstr, unsigned long *quirk)
2390 {
2391         char                    *cp, *name;
2392         int                     i;
2393         if (quirk == NULL)
2394                 return (0);
2395         cp = qstr;
2396         *quirk = 0;
2397         while ((name = strsep(&cp, SWM_Q_WS)) != NULL) {
2398                 if (cp)
2399                         cp += (long)strspn(cp, SWM_Q_WS);
2400                 for (i = 0; i < LENGTH(quirkname); i++) {
2401                         if (!strncasecmp(name, quirkname[i], SWM_QUIRK_LEN)) {
2402                                 DNPRINTF(SWM_D_QUIRK, "parsequirks: %s\n", name);
2403                                 if (i == 0) {
2404                                         *quirk = 0;
2405                                         return (1);
2406                                 }
2407                                 *quirk |= 1 << (i-1);
2408                                 break;
2409                         }
2410                 }
2411                 if (i >= LENGTH(quirkname)) {
2412                         DNPRINTF(SWM_D_QUIRK,
2413                             "parsequirks: invalid quirk [%s]\n", name);
2414                         return (0);
2415                 }
2416         }
2417         return (1);
2418 }
2419 void
2420 setquirk(const char *class, const char *name, const int quirk)
2421 {
2422         int                     i, j;
2423         /* find existing */
2424         for (i = 0; i < quirks_length; i++) {
2425                 if (!strcmp(quirks[i].class, class) &&
2426                     !strcmp(quirks[i].name, name)) {
2427                         if (!quirk) {
2428                                 /* found: delete */
2429                                 DNPRINTF(SWM_D_QUIRK,
2430                                     "setquirk: delete #%d %s:%s\n",
2431                                     i, quirks[i].class, quirks[i].name);
2432                                 free(quirks[i].class);
2433                                 free(quirks[i].name);
2434                                 j = quirks_length - 1;
2435                                 if (i < j)
2436                                         quirks[i] = quirks[j];
2437                                 quirks_length--;
2438                                 return;
2439                         } else {
2440                                 /* found: replace */
2441                                 DNPRINTF(SWM_D_QUIRK,
2442                                     "setquirk: replace #%d %s:%s\n",
2443                                     i, quirks[i].class, quirks[i].name);
2444                                 free(quirks[i].class);
2445                                 free(quirks[i].name);
2446                                 quirks[i].class = strdup(class);
2447                                 quirks[i].name = strdup(name);
2448                                 quirks[i].quirk = quirk;
2449                                 return;
2450                         }
2451                 }
2452         }
2453         if (!quirk) {
2454                 fprintf(stderr,
2455                     "error: setquirk: cannot find class/name combination");
2456                 return;
2457         }
2458         /* not found: add */
2459         if (quirks_size == 0 || quirks == NULL) {
2460                 quirks_size = 4;
2461                 DNPRINTF(SWM_D_QUIRK, "setquirk: init list %d\n", quirks_size);
2462                 quirks = malloc((size_t)quirks_size * sizeof(struct quirk));
2463                 if (!quirks) {
2464                         fprintf(stderr, "setquirk: malloc failed\n");
2465                         perror(" failed");
2466                         quit(NULL, NULL);
2467                 }
2468         } else if (quirks_length == quirks_size) {
2469                 quirks_size *= 2;
2470                 DNPRINTF(SWM_D_QUIRK, "setquirk: grow list %d\n", quirks_size);
2471                 quirks = realloc(quirks, (size_t)quirks_size * sizeof(struct quirk));
2472                 if (!quirks) {
2473                         fprintf(stderr, "setquirk: realloc failed\n");
2474                         perror(" failed");
2475                         quit(NULL, NULL);
2476                 }
2477         }
2478         if (quirks_length < quirks_size) {
2479                 DNPRINTF(SWM_D_QUIRK, "setquirk: add %d\n", quirks_length);
2480                 j = quirks_length++;
2481                 quirks[j].class = strdup(class);
2482                 quirks[j].name = strdup(name);
2483                 quirks[j].quirk = quirk;
2484         } else {
2485                 fprintf(stderr, "quirks array problem?\n");
2486                 if (!quirks) {
2487                         fprintf(stderr, "quirks array problem!\n");
2488                         quit(NULL, NULL);
2489                 }
2490         }
2491 }
2492 int
2493 setconfquirk(char *selector, char *value, int flags)
2494 {
2495         char                    *cp, *class, *name;
2496         int                     retval;
2497         unsigned long           quirks;
2498         if (selector == NULL)
2499                 return (0);
2500         if ((cp = strchr(selector, ':')) == NULL)
2501                 return (0);
2502         *cp = '\0';
2503         class = selector;
2504         name = cp + 1;
2505         if ((retval = parsequirks(value, &quirks)))
2506                 setquirk(class, name, quirks);
2507         return (retval);
2508 }
2509
2510 void
2511 setup_quirks(void)
2512 {
2513         setquirk("MPlayer",             "xv",           SWM_Q_FLOAT | SWM_Q_FULLSCREEN);
2514         setquirk("OpenOffice.org 2.4",  "VCLSalFrame",  SWM_Q_FLOAT);
2515         setquirk("OpenOffice.org 3.0",  "VCLSalFrame",  SWM_Q_FLOAT);
2516         setquirk("Firefox-bin",         "firefox-bin",  SWM_Q_TRANSSZ);
2517         setquirk("Firefox",             "Dialog",       SWM_Q_FLOAT);
2518         setquirk("Gimp",                "gimp",         SWM_Q_FLOAT | SWM_Q_ANYWHERE);
2519         setquirk("XTerm",               "xterm",        SWM_Q_XTERM_FONTADJ);
2520         setquirk("xine",                "Xine Window",  SWM_Q_FLOAT | SWM_Q_ANYWHERE);
2521         setquirk("Xitk",                "Xitk Combo",   SWM_Q_FLOAT | SWM_Q_ANYWHERE);
2522         setquirk("xine",                "xine Panel",   SWM_Q_FLOAT | SWM_Q_ANYWHERE);
2523         setquirk("Xitk",                "Xine Window",  SWM_Q_FLOAT | SWM_Q_ANYWHERE);
2524         setquirk("xine",                "xine Video Fullscreen Window", SWM_Q_FULLSCREEN | SWM_Q_FLOAT);
2525         setquirk("pcb",                 "pcb",          SWM_Q_FLOAT);
2526 }
2527
2528 /* conf file stuff */
2529 #define SWM_CONF_FILE   "scrotwm.conf"
2530
2531 enum    { SWM_S_BAR_DELAY, SWM_S_BAR_ENABLED, SWM_S_CLOCK_ENABLED,
2532           SWM_S_CYCLE_EMPTY, SWM_S_CYCLE_VISIBLE, SWM_S_SS_ENABLED,
2533           SWM_S_TERM_WIDTH, SWM_S_TITLE_CLASS_ENABLED, SWM_S_TITLE_NAME_ENABLED,
2534           SWM_S_BAR_FONT, SWM_S_BAR_ACTION, SWM_S_SPAWN_TERM, SWM_S_SS_APP,
2535           SWM_S_DIALOG_RATIO };
2536
2537 int
2538 setconfvalue(char *selector, char *value, int flags)
2539 {
2540         switch (flags) {
2541         case SWM_S_BAR_DELAY:
2542                 bar_delay = atoi(value);
2543                 break;
2544         case SWM_S_BAR_ENABLED:
2545                 bar_enabled = atoi(value);
2546                 break;
2547         case SWM_S_CLOCK_ENABLED:
2548                 clock_enabled = atoi(value);
2549                 break;
2550         case SWM_S_CYCLE_EMPTY:
2551                 cycle_empty = atoi(value);
2552                 break;
2553         case SWM_S_CYCLE_VISIBLE:
2554                 cycle_visible = atoi(value);
2555                 break;
2556         case SWM_S_SS_ENABLED:
2557                 ss_enabled = atoi(value);
2558                 break;
2559         case SWM_S_TERM_WIDTH:
2560                 term_width = atoi(value);
2561                 break;
2562         case SWM_S_TITLE_CLASS_ENABLED:
2563                 title_class_enabled = atoi(value);
2564                 break;
2565         case SWM_S_TITLE_NAME_ENABLED:
2566                 title_name_enabled = atoi(value);
2567                 break;
2568         case SWM_S_BAR_FONT:
2569                 bar_fonts[0] = strdup(value);
2570                 break;
2571         case SWM_S_BAR_ACTION:
2572                 bar_argv[0] = strdup(value);
2573                 break;
2574         case SWM_S_SPAWN_TERM:
2575                 spawn_term[0] = strdup(value);
2576                 break;
2577         case SWM_S_SS_APP:
2578                 spawn_screenshot[0] = strdup(value);
2579                 break;
2580         case SWM_S_DIALOG_RATIO:
2581                 dialog_ratio = atof(value);
2582                 if (dialog_ratio > 1.0 || dialog_ratio <= .3)
2583                         dialog_ratio = .6;
2584                 break;
2585         default:
2586                 return (0);
2587         }
2588         return (1);
2589 }
2590
2591 int
2592 setconfmodkey(char *selector, char *value, int flags)
2593 {
2594         if (!strncasecmp(value, "Mod1", strlen("Mod1")))
2595                 update_modkey(Mod1Mask);
2596         else if (!strncasecmp(value, "Mod2", strlen("Mod2")))
2597                 update_modkey(Mod2Mask);
2598         else if (!strncasecmp(value, "Mod3", strlen("Mod3")))
2599                 update_modkey(Mod3Mask);
2600         else if (!strncasecmp(value, "Mod4", strlen("Mod4")))
2601                 update_modkey(Mod4Mask);
2602         else
2603                 return (0);
2604         return (1);
2605 }
2606
2607 int
2608 setconfcolor(char *selector, char *value, int flags)
2609 {
2610         setscreencolor(value, ((selector == NULL)?-1:atoi(selector)), flags);
2611         return (1);
2612 }
2613
2614 int
2615 setconfregion(char *selector, char *value, int flags)
2616 {
2617         custom_region(value);
2618         return (1);
2619 }
2620
2621 /* config options */
2622 struct config_option {
2623         char                    *optname;
2624         int (*func)(char*, char*, int);
2625         int funcflags;
2626 };
2627 struct config_option configopt[] = {
2628         { "bar_enabled",                setconfvalue,   SWM_S_BAR_ENABLED },
2629         { "bar_border",                 setconfcolor,   SWM_S_COLOR_BAR_BORDER },
2630         { "bar_color",                  setconfcolor,   SWM_S_COLOR_BAR },
2631         { "bar_font_color",             setconfcolor,   SWM_S_COLOR_BAR_FONT },
2632         { "bar_font",                   setconfvalue,   SWM_S_BAR_FONT },
2633         { "bar_action",                 setconfvalue,   SWM_S_BAR_ACTION },
2634         { "bar_delay",                  setconfvalue,   SWM_S_BAR_DELAY },
2635         { "bind",                       setconfbinding, 0 },
2636         { "clock_enabled",              setconfvalue,   SWM_S_CLOCK_ENABLED },
2637         { "color_focus",                setconfcolor,   SWM_S_COLOR_FOCUS },
2638         { "color_unfocus",              setconfcolor,   SWM_S_COLOR_UNFOCUS },
2639         { "cycle_empty",                setconfvalue,   SWM_S_CYCLE_EMPTY },
2640         { "cycle_visible",              setconfvalue,   SWM_S_CYCLE_VISIBLE },
2641         { "dialog_ratio",               setconfvalue,   SWM_S_DIALOG_RATIO },
2642         { "modkey",                     setconfmodkey,  0 },
2643         { "quirk",                      setconfquirk,   0 },
2644         { "region",                     setconfregion,  0 },
2645         { "spawn_term",                 setconfvalue,   SWM_S_SPAWN_TERM },
2646         { "screenshot_enabled",         setconfvalue,   SWM_S_SS_ENABLED },
2647         { "screenshot_app",             setconfvalue,   SWM_S_SS_APP },
2648         { "term_width",                 setconfvalue,   SWM_S_TERM_WIDTH },
2649         { "title_class_enabled",        setconfvalue,   SWM_S_TITLE_CLASS_ENABLED },
2650         { "title_name_enabled",         setconfvalue,   SWM_S_TITLE_NAME_ENABLED }
2651 };
2652
2653
2654 int
2655 conf_load(char *filename)
2656 {
2657         FILE                    *config;
2658         char                    *line, *cp, *optsub, *optval;
2659         size_t                  linelen, lineno = 0;
2660         int                     wordlen, i, optind;
2661         struct config_option    *opt;
2662         if (filename == NULL)
2663                 return (0);
2664         if ((config = fopen(filename, "r")) == NULL)
2665                 return (0);
2666         while (!feof(config)) {
2667                 if ((line = fparseln(config, &linelen, &lineno, NULL, 0))
2668                     == NULL) {
2669                         if (ferror(config))
2670                                 err(1, "%s", filename);
2671                         else
2672                                 continue;
2673                 }
2674                 cp = line;
2675                 cp += strspn(cp, " \t\n"); /* eat whitespace */
2676                 if (cp[0] == '\0') {
2677                         /* empty line */
2678                         free(line);
2679                         continue;
2680                 }
2681                 /* get config option */
2682                 wordlen = strcspn(cp, "=[ \t\n");
2683                 if (!wordlen) {
2684                         warnx("%s: line %zd: no option found",
2685                             filename, lineno);
2686                         return (0);
2687                 }
2688                 optind = -1;
2689                 for (i = 0; i < LENGTH(configopt); i++) {
2690                         opt = &configopt[i];
2691                         if (!strncasecmp(cp, opt->optname, wordlen) &&
2692                             strlen(opt->optname) == wordlen) {
2693                                 optind = i;
2694                                 break;
2695                         }
2696                 }
2697                 if (optind == -1) {
2698                         warnx("%s: line %zd: unknown option %.*s",
2699                             filename, lineno, wordlen, cp);
2700                         return (0);
2701                 }
2702                 cp += wordlen;
2703                 cp += strspn(cp, " \t\n"); /* eat whitespace */
2704                 /* get [selector] if any */
2705                 optsub = NULL;
2706                 if (*cp == '[') {
2707                         cp++;
2708                         wordlen = strcspn(cp, "]");
2709                         if (!wordlen) {
2710                                 warnx("%s: line %zd: syntax error",
2711                                     filename, lineno);
2712                                 return (0);
2713                         }
2714                         asprintf(&optsub, "%.*s", wordlen, cp);
2715                         cp += wordlen;
2716                         cp += strspn(cp, "] \t\n"); /* eat trailing */
2717                 }
2718                 cp += strspn(cp, "= \t\n"); /* eat trailing */
2719                 /* get RHS value */
2720                 optval = strdup(cp);
2721                 /* call function to deal with it all */
2722                 if (!configopt[optind].func(optsub, optval,
2723                     configopt[optind].funcflags))
2724                         errx(1, "%s: line %zd: invalid data for %s",
2725                             filename, lineno, configopt[optind].optname);
2726                 free(optval);
2727                 free(optsub);
2728                 free(line);
2729         }
2730         return (1);
2731 }
2732
2733 struct ws_win *
2734 manage_window(Window id)
2735 {
2736         Window                  trans;
2737         struct workspace        *ws;
2738         struct ws_win           *win;
2739         int                     format, i, ws_idx, n;
2740         unsigned long           nitems, bytes;
2741         Atom                    ws_idx_atom = 0, type;
2742         Atom                    *prot = NULL, *pp;
2743         unsigned char           ws_idx_str[SWM_PROPLEN], *prop = NULL;
2744         struct swm_region       *r;
2745         long                    mask;
2746         const char              *errstr;
2747         XWindowChanges          wc;
2748
2749         if ((win = find_window(id)) != NULL)
2750                         return (win);   /* already being managed */
2751
2752         if ((win = calloc(1, sizeof(struct ws_win))) == NULL)
2753                 errx(1, "calloc: failed to allocate memory for new window");
2754
2755         /* Get all the window data in one shot */
2756         ws_idx_atom = XInternAtom(display, "_SWM_WS", False);
2757         if (ws_idx_atom)
2758                 XGetWindowProperty(display, id, ws_idx_atom, 0, SWM_PROPLEN,
2759                     False, XA_STRING, &type, &format, &nitems, &bytes, &prop);
2760         XGetWindowAttributes(display, id, &win->wa);
2761         XGetTransientForHint(display, id, &trans);
2762         XGetWMNormalHints(display, id, &win->sh, &mask); /* XXX function? */
2763         if (trans) {
2764                 win->transient = trans;
2765                 DNPRINTF(SWM_D_MISC, "manage_window: win %u transient %u\n",
2766                     (unsigned)win->id, win->transient);
2767         }
2768         /* get supported protocols */
2769         if (XGetWMProtocols(display, id, &prot, &n)) {
2770                 for (i = 0, pp = prot; i < n; i++, pp++)
2771                         if (*pp == adelete)
2772                                 win->can_delete = 1;
2773                 if (prot)
2774                         XFree(prot);
2775         }
2776
2777         /*
2778          * Figure out where to put the window. If it was previously assigned to
2779          * a workspace (either by spawn() or manually moving), and isn't
2780          * transient, * put it in the same workspace
2781          */
2782         r = root_to_region(win->wa.root);
2783         if (prop && win->transient == 0) {
2784                 DNPRINTF(SWM_D_PROP, "got property _SWM_WS=%s\n", prop);
2785                 ws_idx = strtonum(prop, 0, 9, &errstr);
2786                 if (errstr) {
2787                         DNPRINTF(SWM_D_EVENT, "window idx is %s: %s",
2788                             errstr, prop);
2789                 }
2790                 ws = &r->s->ws[ws_idx];
2791         } else
2792                 ws = r->ws;
2793
2794         /* set up the window layout */
2795         win->id = id;
2796         win->ws = ws;
2797         win->s = r->s;  /* this never changes */
2798         TAILQ_INSERT_TAIL(&ws->winlist, win, entry);
2799
2800         win->g.w = win->wa.width;
2801         win->g.h = win->wa.height;
2802         win->g.x = win->wa.x;
2803         win->g.y = win->wa.y;
2804
2805         /* Set window properties so we can remember this after reincarnation */
2806         if (ws_idx_atom && prop == NULL &&
2807             snprintf(ws_idx_str, SWM_PROPLEN, "%d", ws->idx) < SWM_PROPLEN) {
2808                 DNPRINTF(SWM_D_PROP, "setting property _SWM_WS to %s\n",
2809                     ws_idx_str);
2810                 XChangeProperty(display, win->id, ws_idx_atom, XA_STRING, 8,
2811                     PropModeReplace, ws_idx_str, SWM_PROPLEN);
2812         }
2813         XFree(prop);
2814
2815         if (XGetClassHint(display, win->id, &win->ch)) {
2816                 DNPRINTF(SWM_D_CLASS, "class: %s name: %s\n",
2817                     win->ch.res_class, win->ch.res_name);
2818                 for (i = 0; i < quirks_length; i++){
2819                         if (!strcmp(win->ch.res_class, quirks[i].class) &&
2820                             !strcmp(win->ch.res_name, quirks[i].name)) {
2821                                 DNPRINTF(SWM_D_CLASS, "found: %s name: %s\n",
2822                                     win->ch.res_class, win->ch.res_name);
2823                                 if (quirks[i].quirk & SWM_Q_FLOAT)
2824                                         win->floating = 1;
2825                                 win->quirks = quirks[i].quirk;
2826                         }
2827                 }
2828         }
2829
2830         /* alter window position if quirky */
2831         if (win->quirks & SWM_Q_ANYWHERE) {
2832                 win->manual = 1; /* don't center the quirky windows */
2833                 bzero(&wc, sizeof wc);
2834                 mask = 0;
2835                 if (win->g.y < bar_height) {
2836                         win->g.y = wc.y = bar_height;
2837                         mask |= CWY;
2838                 }
2839                 if (win->g.w + win->g.x > WIDTH(r)) {
2840                         win->g.x = wc.x = WIDTH(win->ws->r) - win->g.w - 2;
2841                         mask |= CWX;
2842                 }
2843                 wc.border_width = 1;
2844                 mask |= CWBorderWidth;
2845                 XConfigureWindow(display, win->id, mask, &wc);
2846         }
2847
2848         /* Reset font sizes (the bruteforce way; no default keybinding). */
2849         if (win->quirks & SWM_Q_XTERM_FONTADJ) {
2850                 for (i = 0; i < SWM_MAX_FONT_STEPS; i++)
2851                         fake_keypress(win, XK_KP_Subtract, ShiftMask);
2852                 for (i = 0; i < SWM_MAX_FONT_STEPS; i++)
2853                         fake_keypress(win, XK_KP_Add, ShiftMask);
2854         }
2855
2856         XSelectInput(display, id, EnterWindowMask | FocusChangeMask |
2857             PropertyChangeMask | StructureNotifyMask);
2858
2859         set_win_state(win, NormalState);
2860
2861         /* floaters need to be mapped if they are in the current workspace */
2862         if (win->floating && (ws->idx == r->ws->idx))
2863                 XMapRaised(display, win->id);
2864
2865         /* make new win focused */
2866         focus_win(win);
2867
2868         return (win);
2869 }
2870
2871 void
2872 unmanage_window(struct ws_win *win)
2873 {
2874         struct workspace        *ws;
2875
2876         if (win == NULL)
2877                 return;
2878
2879         DNPRINTF(SWM_D_MISC, "unmanage_window:  %lu\n", win->id);
2880
2881         /* don't unmanage if we are switching workspaces */
2882         ws = win->ws;
2883         if (ws->restack)
2884                 return;
2885
2886         /* find a window to focus */
2887         if (ws->focus == win)
2888                 ws->focus = TAILQ_PREV(win, ws_win_list, entry);
2889         if (ws->focus == NULL)
2890                 ws->focus = TAILQ_FIRST(&ws->winlist);
2891         if (ws->focus == NULL || ws->focus == win) {
2892                 ws->focus = NULL;
2893                 unfocus_win(win);
2894         } else
2895                 focus_win(ws->focus);
2896         if (ws->focus_prev == win)
2897                 ws->focus_prev = NULL;
2898
2899         TAILQ_REMOVE(&win->ws->winlist, win, entry);
2900         set_win_state(win, WithdrawnState);
2901         if (win->ch.res_class)
2902                 XFree(win->ch.res_class);
2903         if (win->ch.res_name)
2904                 XFree(win->ch.res_name);
2905         free(win);
2906 }
2907
2908 void
2909 configurerequest(XEvent *e)
2910 {
2911         XConfigureRequestEvent  *ev = &e->xconfigurerequest;
2912         struct ws_win           *win;
2913         int                     new = 0;
2914         XWindowChanges          wc;
2915
2916         if ((win = find_window(ev->window)) == NULL)
2917                 new = 1;
2918
2919         if (new) {
2920                 DNPRINTF(SWM_D_EVENT, "configurerequest: new window: %lu\n",
2921                     ev->window);
2922                 bzero(&wc, sizeof wc);
2923                 wc.x = ev->x;
2924                 wc.y = ev->y;
2925                 wc.width = ev->width;
2926                 wc.height = ev->height;
2927                 wc.border_width = ev->border_width;
2928                 wc.sibling = ev->above;
2929                 wc.stack_mode = ev->detail;
2930                 XConfigureWindow(display, ev->window, ev->value_mask, &wc);
2931         } else {
2932                 DNPRINTF(SWM_D_EVENT, "configurerequest: change window: %lu\n",
2933                     ev->window);
2934                 if (win->floating) {
2935                         if (ev->value_mask & CWX)
2936                                 win->g.x = ev->x;
2937                         if (ev->value_mask & CWY)
2938                                 win->g.y = ev->y;
2939                         if (ev->value_mask & CWWidth)
2940                                 win->g.w = ev->width;
2941                         if (ev->value_mask & CWHeight)
2942                                 win->g.h = ev->height;
2943                         if (win->ws->r != NULL) {
2944                                 /* this seems to be full screen */
2945                                 if (win->g.w >= WIDTH(win->ws->r)) {
2946                                         win->g.x = 0;
2947                                         win->g.w = WIDTH(win->ws->r);
2948                                         ev->value_mask |= CWX | CWWidth;
2949                                 }
2950                                 if (win->g.h >= HEIGHT(win->ws->r)) {
2951                                         /* kill border */
2952                                         win->g.y = 0;
2953                                         win->g.h = HEIGHT(win->ws->r);
2954                                         ev->value_mask |= CWY | CWHeight;
2955                                 }
2956                         }
2957                         if ((ev->value_mask & (CWX | CWY)) &&
2958                             !(ev->value_mask & (CWWidth | CWHeight)))
2959                                 config_win(win);
2960                         XMoveResizeWindow(display, win->id,
2961                             win->g.x, win->g.y, win->g.w, win->g.h);
2962                 } else
2963                         config_win(win);
2964         }
2965 }
2966
2967 void
2968 configurenotify(XEvent *e)
2969 {
2970         struct ws_win           *win;
2971         long                    mask;
2972
2973         DNPRINTF(SWM_D_EVENT, "configurenotify: window: %lu\n",
2974             e->xconfigure.window);
2975
2976         XMapWindow(display, e->xconfigure.window);
2977         win = find_window(e->xconfigure.window);
2978         if (win) {
2979                 XGetWMNormalHints(display, win->id, &win->sh, &mask);
2980                 adjust_font(win);
2981                 XMapWindow(display, win->id);
2982                 if (font_adjusted)
2983                         stack();
2984         }
2985 }
2986
2987 void
2988 destroynotify(XEvent *e)
2989 {
2990         struct ws_win           *win;
2991         XDestroyWindowEvent     *ev = &e->xdestroywindow;
2992
2993         DNPRINTF(SWM_D_EVENT, "destroynotify: window %lu\n", ev->window);
2994
2995         if ((win = find_window(ev->window)) != NULL) {
2996                 unmanage_window(win);
2997                 stack();
2998         }
2999 }
3000
3001 void
3002 enternotify(XEvent *e)
3003 {
3004         XCrossingEvent          *ev = &e->xcrossing;
3005         struct ws_win           *win;
3006
3007         DNPRINTF(SWM_D_EVENT, "enternotify: window: %lu\n", ev->window);
3008
3009         if (ignore_enter) {
3010                 /* eat event(r) to prevent autofocus */
3011                 ignore_enter--;
3012                 return;
3013         }
3014
3015         if ((win = find_window(ev->window)) != NULL)
3016                 focus_win(win);
3017 }
3018
3019 void
3020 focusin(XEvent *e)
3021 {
3022         DNPRINTF(SWM_D_EVENT, "focusin: window: %lu\n", e->xfocus.window);
3023 }
3024
3025 void
3026 focusout(XEvent *e)
3027 {
3028         DNPRINTF(SWM_D_EVENT, "focusout: window: %lu\n", e->xfocus.window);
3029
3030         if (cur_focus && cur_focus->ws->r &&
3031             cur_focus->id == e->xfocus.window) {
3032                 struct swm_screen       *s = cur_focus->ws->r->s;
3033                 Window                  rr, cr;
3034                 int                     x, y, wx, wy;
3035                 unsigned int            mask;
3036
3037                 /* Try to detect synergy hiding the cursor.  */
3038                 if (XQueryPointer(display, cur_focus->id,
3039                     &rr, &cr, &x, &y, &wx, &wy, &mask) != False &&
3040                     cr == 0 && !mask &&
3041                     x == DisplayWidth(display, s->idx)/2 &&
3042                     y == DisplayHeight(display, s->idx)/2) {
3043                         unfocus_win(cur_focus);
3044                 }
3045         }
3046 }
3047
3048 void
3049 mappingnotify(XEvent *e)
3050 {
3051         XMappingEvent           *ev = &e->xmapping;
3052
3053         DNPRINTF(SWM_D_EVENT, "mappingnotify: window: %lu\n", ev->window);
3054
3055         XRefreshKeyboardMapping(ev);
3056         if (ev->request == MappingKeyboard)
3057                 grabkeys();
3058 }
3059
3060 void
3061 maprequest(XEvent *e)
3062 {
3063         XMapRequestEvent        *ev = &e->xmaprequest;
3064         XWindowAttributes       wa;
3065
3066         DNPRINTF(SWM_D_EVENT, "maprequest: window: %lu\n",
3067             e->xmaprequest.window);
3068
3069         if (!XGetWindowAttributes(display, ev->window, &wa))
3070                 return;
3071         if (wa.override_redirect)
3072                 return;
3073         manage_window(e->xmaprequest.window);
3074
3075         stack();
3076 }
3077
3078 void
3079 propertynotify(XEvent *e)
3080 {
3081         struct ws_win           *win;
3082         XPropertyEvent          *ev = &e->xproperty;
3083
3084         DNPRINTF(SWM_D_EVENT, "propertynotify: window: %lu\n",
3085             ev->window);
3086
3087         if (ev->state == PropertyDelete)
3088                 return; /* ignore */
3089         win = find_window(ev->window);
3090         if (win == NULL)
3091                 return;
3092
3093         switch (ev->atom) {
3094         case XA_WM_NORMAL_HINTS:
3095 #if 0
3096                 long            mask;
3097                 XGetWMNormalHints(display, win->id, &win->sh, &mask);
3098                 fprintf(stderr, "normal hints: flag 0x%x\n", win->sh.flags);
3099                 if (win->sh.flags & PMinSize) {
3100                         win->g.w = win->sh.min_width;
3101                         win->g.h = win->sh.min_height;
3102                         fprintf(stderr, "min %d %d\n", win->g.w, win->g.h);
3103                 }
3104                 XMoveResizeWindow(display, win->id,
3105                     win->g.x, win->g.y, win->g.w, win->g.h);
3106 #endif
3107                 break;
3108         default:
3109                 break;
3110         }
3111 }
3112
3113 void
3114 unmapnotify(XEvent *e)
3115 {
3116         XDestroyWindowEvent     *ev = &e->xdestroywindow;
3117         struct ws_win           *win;
3118
3119         DNPRINTF(SWM_D_EVENT, "unmapnotify: window: %lu\n", e->xunmap.window);
3120
3121         if ((win = find_window(ev->window)) != NULL)
3122                 if (win->transient)
3123                         unmanage_window(win);
3124 }
3125
3126 void
3127 visibilitynotify(XEvent *e)
3128 {
3129         int                     i;
3130         struct swm_region       *r;
3131
3132         DNPRINTF(SWM_D_EVENT, "visibilitynotify: window: %lu\n",
3133             e->xvisibility.window);
3134         if (e->xvisibility.state == VisibilityUnobscured)
3135                 for (i = 0; i < ScreenCount(display); i++)
3136                         TAILQ_FOREACH(r, &screens[i].rl, entry)
3137                                 if (e->xvisibility.window == r->bar_window)
3138                                         bar_update();
3139 }
3140
3141 int
3142 xerror_start(Display *d, XErrorEvent *ee)
3143 {
3144         other_wm = 1;
3145         return (-1);
3146 }
3147
3148 int
3149 xerror(Display *d, XErrorEvent *ee)
3150 {
3151         /* fprintf(stderr, "error: %p %p\n", display, ee); */
3152         return (-1);
3153 }
3154
3155 int
3156 active_wm(void)
3157 {
3158         other_wm = 0;
3159         xerrorxlib = XSetErrorHandler(xerror_start);
3160
3161         /* this causes an error if some other window manager is running */
3162         XSelectInput(display, DefaultRootWindow(display),
3163             SubstructureRedirectMask);
3164         XSync(display, False);
3165         if (other_wm)
3166                 return (1);
3167
3168         XSetErrorHandler(xerror);
3169         XSync(display, False);
3170         return (0);
3171 }
3172
3173 long
3174 getstate(Window w)
3175 {
3176         int                     format, status;
3177         long                    result = -1;
3178         unsigned char           *p = NULL;
3179         unsigned long           n, extra;
3180         Atom                    real;
3181
3182         status = XGetWindowProperty(display, w, astate, 0L, 2L, False, astate,
3183             &real, &format, &n, &extra, (unsigned char **)&p);
3184         if (status != Success)
3185                 return (-1);
3186         if (n != 0)
3187                 result = *((long *)p);
3188         XFree(p);
3189         return (result);
3190 }
3191
3192 void
3193 new_region(struct swm_screen *s, int x, int y, int w, int h)
3194 {
3195         struct swm_region       *r, *n;
3196         struct workspace        *ws = NULL;
3197         int                     i;
3198
3199         DNPRINTF(SWM_D_MISC, "new region: screen[%d]:%dx%d+%d+%d\n",
3200              s->idx, w, h, x, y);
3201
3202         /* remove any conflicting regions */
3203         n = TAILQ_FIRST(&s->rl);
3204         while (n) {
3205                 r = n;
3206                 n = TAILQ_NEXT(r, entry);
3207                 if (X(r) < (x + w) &&
3208                     (X(r) + WIDTH(r)) > x &&
3209                     Y(r) < (y + h) &&
3210                     (Y(r) + HEIGHT(r)) > y) {
3211                         XDestroyWindow(display, r->bar_window);
3212                         TAILQ_REMOVE(&s->rl, r, entry);
3213                         TAILQ_INSERT_TAIL(&s->orl, r, entry);
3214                 }
3215         }
3216
3217         /* search old regions for one to reuse */
3218
3219         /* size + location match */
3220         TAILQ_FOREACH(r, &s->orl, entry)
3221                 if (X(r) == x && Y(r) == y &&
3222                     HEIGHT(r) == h && WIDTH(r) == w)
3223                         break;
3224
3225         /* size match */
3226         TAILQ_FOREACH(r, &s->orl, entry)
3227                 if (HEIGHT(r) == h && WIDTH(r) == w)
3228                         break;
3229
3230         if (r != NULL) {
3231                 TAILQ_REMOVE(&s->orl, r, entry);
3232                 /* try to use old region's workspace */
3233                 if (r->ws->r == NULL)
3234                         ws = r->ws;
3235         } else
3236                 if ((r = calloc(1, sizeof(struct swm_region))) == NULL)
3237                         errx(1, "calloc: failed to allocate memory for screen");
3238
3239         /* if we don't have a workspace already, find one */
3240         if (ws == NULL) {
3241                 for (i = 0; i < SWM_WS_MAX; i++)
3242                         if (s->ws[i].r == NULL) {
3243                                 ws = &s->ws[i];
3244                                 break;
3245                         }
3246         }
3247
3248         if (ws == NULL)
3249                 errx(1, "no free workspaces\n");
3250
3251         X(r) = x;
3252         Y(r) = y;
3253         WIDTH(r) = w;
3254         HEIGHT(r) = h;
3255         r->s = s;
3256         r->ws = ws;
3257         ws->r = r;
3258         TAILQ_INSERT_TAIL(&s->rl, r, entry);
3259 }
3260
3261 void
3262 scan_xrandr(int i)
3263 {
3264 #ifdef SWM_XRR_HAS_CRTC
3265         XRRCrtcInfo             *ci;
3266         XRRScreenResources      *sr;
3267         int                     c;
3268         int                     ncrtc = 0;
3269 #endif /* SWM_XRR_HAS_CRTC */
3270         struct swm_region       *r;
3271
3272
3273         if (i >= ScreenCount(display))
3274                 errx(1, "invalid screen");
3275
3276         /* remove any old regions */
3277         while ((r = TAILQ_FIRST(&screens[i].rl)) != NULL) {
3278                 r->ws->r = NULL;
3279                 XDestroyWindow(display, r->bar_window);
3280                 TAILQ_REMOVE(&screens[i].rl, r, entry);
3281                 TAILQ_INSERT_TAIL(&screens[i].orl, r, entry);
3282         }
3283
3284         /* map virtual screens onto physical screens */
3285 #ifdef SWM_XRR_HAS_CRTC
3286         if (xrandr_support) {
3287                 sr = XRRGetScreenResources(display, screens[i].root);
3288                 if (sr == NULL)
3289                         new_region(&screens[i], 0, 0,
3290                             DisplayWidth(display, i),
3291                             DisplayHeight(display, i));
3292                 else
3293                         ncrtc = sr->ncrtc;
3294
3295                 for (c = 0, ci = NULL; c < ncrtc; c++) {
3296                         ci = XRRGetCrtcInfo(display, sr, sr->crtcs[c]);
3297                         if (ci->noutput == 0)
3298                                 continue;
3299
3300                         if (ci != NULL && ci->mode == None)
3301                                 new_region(&screens[i], 0, 0,
3302                                     DisplayWidth(display, i),
3303                                     DisplayHeight(display, i));
3304                         else
3305                                 new_region(&screens[i],
3306                                     ci->x, ci->y, ci->width, ci->height);
3307                 }
3308                 if (ci)
3309                         XRRFreeCrtcInfo(ci);
3310                 XRRFreeScreenResources(sr);
3311         } else
3312 #endif /* SWM_XRR_HAS_CRTC */
3313         {
3314                 new_region(&screens[i], 0, 0, DisplayWidth(display, i),
3315                     DisplayHeight(display, i));
3316         }
3317 }
3318
3319 void
3320 screenchange(XEvent *e) {
3321         XRRScreenChangeNotifyEvent      *xe = (XRRScreenChangeNotifyEvent *)e;
3322         struct swm_region               *r;
3323         struct ws_win                   *win;
3324         int                             i;
3325
3326         DNPRINTF(SWM_D_EVENT, "screenchange: %lu\n", xe->root);
3327
3328         if (!XRRUpdateConfiguration(e))
3329                 return;
3330
3331         /* silly event doesn't include the screen index */
3332         for (i = 0; i < ScreenCount(display); i++)
3333                 if (screens[i].root == xe->root)
3334                         break;
3335         if (i >= ScreenCount(display))
3336                 errx(1, "screenchange: screen not found\n");
3337
3338         /* brute force for now, just re-enumerate the regions */
3339         scan_xrandr(i);
3340
3341         /* hide any windows that went away */
3342         TAILQ_FOREACH(r, &screens[i].rl, entry)
3343                 TAILQ_FOREACH(win, &r->ws->winlist, entry)
3344                         XUnmapWindow(display, win->id);
3345
3346         /* add bars to all regions */
3347         for (i = 0; i < ScreenCount(display); i++)
3348                 TAILQ_FOREACH(r, &screens[i].rl, entry)
3349                         bar_setup(r);
3350         stack();
3351 }
3352
3353 void
3354 setup_screens(void)
3355 {
3356         Window                  d1, d2, *wins = NULL;
3357         XWindowAttributes       wa;
3358         unsigned int            no;
3359         int                     i, j, k;
3360         int                     errorbase, major, minor;
3361         struct workspace        *ws;
3362         int                     ws_idx_atom;
3363
3364
3365         if ((screens = calloc(ScreenCount(display),
3366              sizeof(struct swm_screen))) == NULL)
3367                 errx(1, "calloc: screens");
3368
3369         ws_idx_atom = XInternAtom(display, "_SWM_WS", False);
3370
3371         /* initial Xrandr setup */
3372         xrandr_support = XRRQueryExtension(display,
3373             &xrandr_eventbase, &errorbase);
3374         if (xrandr_support)
3375                 if (XRRQueryVersion(display, &major, &minor) && major < 1)
3376                         xrandr_support = 0;
3377
3378         /* map physical screens */
3379         for (i = 0; i < ScreenCount(display); i++) {
3380                 DNPRINTF(SWM_D_WS, "setup_screens: init screen %d\n", i);
3381                 screens[i].idx = i;
3382                 TAILQ_INIT(&screens[i].rl);
3383                 TAILQ_INIT(&screens[i].orl);
3384                 screens[i].root = RootWindow(display, i);
3385
3386                 /* set default colors */
3387                 setscreencolor("red", i + 1, SWM_S_COLOR_FOCUS);
3388                 setscreencolor("rgb:88/88/88", i + 1, SWM_S_COLOR_UNFOCUS);
3389                 setscreencolor("rgb:00/80/80", i + 1, SWM_S_COLOR_BAR_BORDER);
3390                 setscreencolor("black", i + 1, SWM_S_COLOR_BAR);
3391                 setscreencolor("rgb:a0/a0/a0", i + 1, SWM_S_COLOR_BAR_FONT);
3392
3393                 /* init all workspaces */
3394                 /* XXX these should be dynamically allocated too */
3395                 for (j = 0; j < SWM_WS_MAX; j++) {
3396                         ws = &screens[i].ws[j];
3397                         ws->idx = j;
3398                         ws->restack = 1;
3399                         ws->focus = NULL;
3400                         ws->r = NULL;
3401                         TAILQ_INIT(&ws->winlist);
3402
3403                         for (k = 0; layouts[k].l_stack != NULL; k++)
3404                                 if (layouts[k].l_config != NULL)
3405                                         layouts[k].l_config(ws,
3406                                             SWM_ARG_ID_STACKINIT);
3407                         ws->cur_layout = &layouts[0];
3408                 }
3409                 /* grab existing windows (before we build the bars)*/
3410                 if (!XQueryTree(display, screens[i].root, &d1, &d2, &wins, &no))
3411                         continue;
3412
3413                 scan_xrandr(i);
3414
3415                 if (xrandr_support)
3416                         XRRSelectInput(display, screens[i].root,
3417                             RRScreenChangeNotifyMask);
3418
3419                 /* attach windows to a region */
3420                 /* normal windows */
3421                 for (j = 0; j < no; j++) {
3422                         XGetWindowAttributes(display, wins[j], &wa);
3423                         if (!XGetWindowAttributes(display, wins[j], &wa) ||
3424                             wa.override_redirect ||
3425                             XGetTransientForHint(display, wins[j], &d1))
3426                                 continue;
3427
3428                         if (wa.map_state == IsViewable ||
3429                             getstate(wins[j]) == NormalState)
3430                                 manage_window(wins[j]);
3431                 }
3432                 /* transient windows */
3433                 for (j = 0; j < no; j++) {
3434                         if (!XGetWindowAttributes(display, wins[j], &wa))
3435                                 continue;
3436
3437                         if (XGetTransientForHint(display, wins[j], &d1) &&
3438                             (wa.map_state == IsViewable || getstate(wins[j]) ==
3439                             NormalState))
3440                                 manage_window(wins[j]);
3441                 }
3442                 if (wins) {
3443                         XFree(wins);
3444                         wins = NULL;
3445                 }
3446         }
3447 }
3448
3449 void
3450 workaround(void)
3451 {
3452         int                     i;
3453         Atom                    netwmcheck, netwmname, utf8_string;
3454         Window                  root;
3455
3456         /* work around sun jdk bugs, code from wmname */
3457         netwmcheck = XInternAtom(display, "_NET_SUPPORTING_WM_CHECK", False);
3458         netwmname = XInternAtom(display, "_NET_WM_NAME", False);
3459         utf8_string = XInternAtom(display, "UTF8_STRING", False);
3460         for (i = 0; i < ScreenCount(display); i++) {
3461                 root = screens[i].root;
3462                 XChangeProperty(display, root, netwmcheck, XA_WINDOW, 32,
3463                     PropModeReplace, (unsigned char *)&root, 1);
3464                 XChangeProperty(display, root, netwmname, utf8_string, 8,
3465                     PropModeReplace, "LG3D", strlen("LG3D"));
3466         }
3467 }
3468
3469 int
3470 main(int argc, char *argv[])
3471 {
3472         struct passwd           *pwd;
3473         struct swm_region       *r;
3474         char                    conf[PATH_MAX], *cfile = NULL;
3475         struct stat             sb;
3476         XEvent                  e;
3477         int                     xfd, i;
3478         fd_set                  rd;
3479
3480         start_argv = argv;
3481         fprintf(stderr, "Welcome to scrotwm V%s cvs tag: %s\n",
3482             SWM_VERSION, cvstag);
3483         if (!setlocale(LC_CTYPE, "") || !XSupportsLocale())
3484                 warnx("no locale support");
3485
3486         if (!(display = XOpenDisplay(0)))
3487                 errx(1, "can not open display");
3488
3489         if (active_wm())
3490                 errx(1, "other wm running");
3491
3492         astate = XInternAtom(display, "WM_STATE", False);
3493         aprot = XInternAtom(display, "WM_PROTOCOLS", False);
3494         adelete = XInternAtom(display, "WM_DELETE_WINDOW", False);
3495
3496         /* look for local and global conf file */
3497         pwd = getpwuid(getuid());
3498         if (pwd == NULL)
3499                 errx(1, "invalid user %d", getuid());
3500
3501         setup_screens();
3502         setup_keys();
3503         setup_quirks();
3504
3505         snprintf(conf, sizeof conf, "%s/.%s", pwd->pw_dir, SWM_CONF_FILE);
3506         if (stat(conf, &sb) != -1) {
3507                 if (S_ISREG(sb.st_mode))
3508                         cfile = conf;
3509         } else {
3510                 /* try global conf file */
3511                 snprintf(conf, sizeof conf, "/etc/%s", SWM_CONF_FILE);
3512                 if (!stat(conf, &sb))
3513                         if (S_ISREG(sb.st_mode))
3514                                 cfile = conf;
3515         }
3516         if (cfile)
3517                 conf_load(cfile);
3518
3519         /* setup all bars */
3520         for (i = 0; i < ScreenCount(display); i++)
3521                 TAILQ_FOREACH(r, &screens[i].rl, entry)
3522                         bar_setup(r);
3523
3524         /* set some values to work around bad programs */
3525         workaround();
3526
3527         grabkeys();
3528         stack();
3529
3530         xfd = ConnectionNumber(display);
3531         while (running) {
3532                 FD_ZERO(&rd);
3533                 FD_SET(xfd, &rd);
3534                 if (select(xfd + 1, &rd, NULL, NULL, NULL) == -1)
3535                         if (errno != EINTR)
3536                                 errx(1, "select failed");
3537                 if (bar_alarm) {
3538                         bar_alarm = 0;
3539                         bar_update();
3540                 }
3541                 while (XPending(display)) {
3542                         XNextEvent(display, &e);
3543                         if (e.type < LASTEvent) {
3544                                 if (handler[e.type])
3545                                         handler[e.type](&e);
3546                                 else
3547                                         DNPRINTF(SWM_D_EVENT,
3548                                             "win: %lu unknown event: %d\n",
3549                                             e.xany.window, e.type);
3550                         } else {
3551                                 switch (e.type - xrandr_eventbase) {
3552                                 case RRScreenChangeNotify:
3553                                         screenchange(&e);
3554                                         break;
3555                                 default:
3556                                         DNPRINTF(SWM_D_EVENT,
3557                                             "win: %lu unknown xrandr event: "
3558                                             "%d\n", e.xany.window, e.type);
3559                                         break;
3560                                 }
3561                         }
3562                 }
3563         }
3564
3565         XCloseDisplay(display);
3566
3567         return (0);
3568 }