JasonWoof Got questions, comments, patches, etc.? Contact Jason Woofenden
catch up to new top
[spectrwm.git] / scrotwm.c
1 /* $scrotwm$ */
2 /*
3  * Copyright (c) 2009-2010-2011 Marco Peereboom <marco@peereboom.us>
4  * Copyright (c) 2009-2010-2011 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  * Copyright (c) 2010 Tuukka Kataja <stuge@xor.fi>
8  * Copyright (c) 2011 Jason L. Wright <jason@thought.net>
9  *
10  * Permission to use, copy, modify, and distribute this software for any
11  * purpose with or without fee is hereby granted, provided that the above
12  * copyright notice and this permission notice appear in all copies.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
15  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
16  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
17  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
18  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
19  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
20  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
21  */
22 /*
23  * Much code and ideas taken from dwm under the following license:
24  * MIT/X Consortium License
25  *
26  * 2006-2008 Anselm R Garbe <garbeam at gmail dot com>
27  * 2006-2007 Sander van Dijk <a dot h dot vandijk at gmail dot com>
28  * 2006-2007 Jukka Salmi <jukka at salmi dot ch>
29  * 2007 Premysl Hruby <dfenze at gmail dot com>
30  * 2007 Szabolcs Nagy <nszabolcs at gmail dot com>
31  * 2007 Christof Musik <christof at sendfax dot de>
32  * 2007-2008 Enno Gottox Boland <gottox at s01 dot de>
33  * 2007-2008 Peter Hartlich <sgkkr at hartlich dot com>
34  * 2008 Martin Hurton <martin dot hurton at gmail dot com>
35  *
36  * Permission is hereby granted, free of charge, to any person obtaining a
37  * copy of this software and associated documentation files (the "Software"),
38  * to deal in the Software without restriction, including without limitation
39  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
40  * and/or sell copies of the Software, and to permit persons to whom the
41  * Software is furnished to do so, subject to the following conditions:
42  *
43  * The above copyright notice and this permission notice shall be included in
44  * all copies or substantial portions of the Software.
45  *
46  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
47  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
48  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
49  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
50  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
51  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
52  * DEALINGS IN THE SOFTWARE.
53  */
54
55 static const char       *cvstag =
56     "$scrotwm$";
57
58 #define SWM_VERSION     "0.9.32"
59
60 #include <stdio.h>
61 #include <stdlib.h>
62 #include <err.h>
63 #include <errno.h>
64 #include <fcntl.h>
65 #include <locale.h>
66 #include <unistd.h>
67 #include <time.h>
68 #include <signal.h>
69 #include <string.h>
70 #include <util.h>
71 #include <pwd.h>
72 #include <paths.h>
73 #include <ctype.h>
74
75 #include <sys/types.h>
76 #include <sys/time.h>
77 #include <sys/stat.h>
78 #include <sys/wait.h>
79 #include <sys/queue.h>
80 #include <sys/param.h>
81 #include <sys/select.h>
82
83 #include <X11/cursorfont.h>
84 #include <X11/keysym.h>
85 #include <X11/Xatom.h>
86 #include <X11/Xlib.h>
87 #include <X11/Xproto.h>
88 #include <X11/Xutil.h>
89 #include <X11/extensions/Xrandr.h>
90
91 #ifdef __OSX__
92 #include <osx.h>
93 #endif
94
95 #if RANDR_MAJOR < 1
96 #  error XRandR versions less than 1.0 are not supported
97 #endif
98
99 #if RANDR_MAJOR >= 1
100 #if RANDR_MINOR >= 2
101 #define SWM_XRR_HAS_CRTC
102 #endif
103 #endif
104
105 /*#define SWM_DEBUG*/
106 #ifdef SWM_DEBUG
107 #define DPRINTF(x...)           do { if (swm_debug) fprintf(stderr, x); } while (0)
108 #define DNPRINTF(n,x...)        do { if (swm_debug & n) fprintf(stderr, x); } while (0)
109 #define SWM_D_MISC              0x0001
110 #define SWM_D_EVENT             0x0002
111 #define SWM_D_WS                0x0004
112 #define SWM_D_FOCUS             0x0008
113 #define SWM_D_MOVE              0x0010
114 #define SWM_D_STACK             0x0020
115 #define SWM_D_MOUSE             0x0040
116 #define SWM_D_PROP              0x0080
117 #define SWM_D_CLASS             0x0100
118 #define SWM_D_KEY               0x0200
119 #define SWM_D_QUIRK             0x0400
120 #define SWM_D_SPAWN             0x0800
121 #define SWM_D_EVENTQ            0x1000
122 #define SWM_D_CONF              0x2000
123
124 u_int32_t               swm_debug = 0
125                             | SWM_D_MISC
126                             | SWM_D_EVENT
127                             | SWM_D_WS
128                             | SWM_D_FOCUS
129                             | SWM_D_MOVE
130                             | SWM_D_STACK
131                             | SWM_D_MOUSE
132                             | SWM_D_PROP
133                             | SWM_D_CLASS
134                             | SWM_D_KEY
135                             | SWM_D_QUIRK
136                             | SWM_D_SPAWN
137                             | SWM_D_EVENTQ
138                             | SWM_D_CONF
139                             ;
140 #else
141 #define DPRINTF(x...)
142 #define DNPRINTF(n,x...)
143 #endif
144
145 #define LENGTH(x)               (sizeof x / sizeof x[0])
146 #define MODKEY                  Mod1Mask
147 #define CLEANMASK(mask)         (mask & ~(numlockmask | LockMask))
148 #define BUTTONMASK              (ButtonPressMask|ButtonReleaseMask)
149 #define MOUSEMASK               (BUTTONMASK|PointerMotionMask)
150 #define SWM_PROPLEN             (16)
151 #define SWM_FUNCNAME_LEN        (32)
152 #define SWM_KEYS_LEN            (255)
153 #define SWM_QUIRK_LEN           (64)
154 #define X(r)                    (r)->g.x
155 #define Y(r)                    (r)->g.y
156 #define WIDTH(r)                (r)->g.w
157 #define HEIGHT(r)               (r)->g.h
158 #define SWM_MAX_FONT_STEPS      (3)
159 #define WINID(w)                (w ? w->id : 0)
160
161 #define SWM_FOCUS_DEFAULT       (0)
162 #define SWM_FOCUS_SYNERGY       (1)
163 #define SWM_FOCUS_FOLLOW        (2)
164
165 #ifndef SWM_LIB
166 #define SWM_LIB                 "/usr/local/lib/libswmhack.so"
167 #endif
168
169 char                    **start_argv;
170 Atom                    astate;
171 Atom                    aprot;
172 Atom                    adelete;
173 Atom                    takefocus;
174 Atom                    a_wmname;
175 Atom                    a_utf8_string;
176 Atom                    a_string;
177 Atom                    a_swm_iconic;
178 volatile sig_atomic_t   running = 1;
179 volatile sig_atomic_t   restart_wm = 0;
180 int                     outputs = 0;
181 int                     last_focus_event = FocusOut;
182 int                     (*xerrorxlib)(Display *, XErrorEvent *);
183 int                     other_wm;
184 int                     ss_enabled = 0;
185 int                     xrandr_support;
186 int                     xrandr_eventbase;
187 unsigned int            numlockmask = 0;
188 Display                 *display;
189
190 int                     cycle_empty = 0;
191 int                     cycle_visible = 0;
192 int                     term_width = 0;
193 int                     font_adjusted = 0;
194 unsigned int            mod_key = MODKEY;
195
196 /* dmenu search */
197 struct swm_region       *search_r;
198 int                     select_list_pipe[2];
199 int                     select_resp_pipe[2];
200 pid_t                   searchpid;
201 volatile sig_atomic_t   search_resp;
202
203 /* dialog windows */
204 double                  dialog_ratio = .6;
205 /* status bar */
206 #define SWM_BAR_MAX     (256)
207 char                    *bar_argv[] = { NULL, NULL };
208 int                     bar_pipe[2];
209 char                    bar_ext[SWM_BAR_MAX];
210 char                    bar_vertext[SWM_BAR_MAX];
211 int                     bar_version = 0;
212 sig_atomic_t            bar_alarm = 0;
213 int                     bar_delay = 30;
214 int                     bar_enabled = 1;
215 int                     bar_border_width = 1;
216 int                     bar_at_bottom = 0;
217 int                     bar_extra = 1;
218 int                     bar_extra_running = 0;
219 int                     bar_verbose = 1;
220 int                     bar_height = 0;
221 int                     stack_enabled = 1;
222 int                     clock_enabled = 1;
223 char                    *clock_format = NULL;
224 int                     title_name_enabled = 0;
225 int                     title_class_enabled = 0;
226 int                     window_name_enabled = 0;
227 int                     focus_mode = SWM_FOCUS_DEFAULT;
228 int                     disable_border = 0;
229 int                     border_width = 1;
230 pid_t                   bar_pid;
231 GC                      bar_gc;
232 XGCValues               bar_gcv;
233 int                     bar_fidx = 0;
234 XFontStruct             *bar_fs;
235 char                    *bar_fonts[] = { NULL, NULL, NULL, NULL };/* XXX Make fully dynamic */
236 char                    *spawn_term[] = { NULL, NULL };         /* XXX Make fully dynamic */
237
238 #define SWM_MENU_FN     (2)
239 #define SWM_MENU_NB     (4)
240 #define SWM_MENU_NF     (6)
241 #define SWM_MENU_SB     (8)
242 #define SWM_MENU_SF     (10)
243
244 /* layout manager data */
245 struct swm_geometry {
246         int                     x;
247         int                     y;
248         int                     w;
249         int                     h;
250 };
251
252 struct swm_screen;
253 struct workspace;
254
255 /* virtual "screens" */
256 struct swm_region {
257         TAILQ_ENTRY(swm_region) entry;
258         struct swm_geometry     g;
259         struct workspace        *ws;    /* current workspace on this region */
260         struct workspace        *ws_prior; /* prior workspace on this region */
261         struct swm_screen       *s;     /* screen idx */
262         Window                  bar_window;
263 };
264 TAILQ_HEAD(swm_region_list, swm_region);
265
266 struct ws_win {
267         TAILQ_ENTRY(ws_win)     entry;
268         Window                  id;
269         Window                  transient;
270         struct ws_win           *child_trans;   /* transient child window */
271         struct swm_geometry     g;              /* current geometry */
272         struct swm_geometry     g_float;        /* geometry when floating */
273         struct swm_geometry     rg_float;       /* region geom when floating */
274         int                     g_floatvalid;   /* flag: geometry in g_float is valid */
275         int                     floatmaxed;     /* flag: floater was maxed in max_stack */
276         int                     floating;
277         int                     manual;
278         int                     iconic;
279         unsigned int            ewmh_flags;
280         int                     font_size_boundary[SWM_MAX_FONT_STEPS];
281         int                     font_steps;
282         int                     last_inc;
283         int                     can_delete;
284         int                     take_focus;
285         int                     java;
286         unsigned long           quirks;
287         struct workspace        *ws;    /* always valid */
288         struct swm_screen       *s;     /* always valid, never changes */
289         XWindowAttributes       wa;
290         XSizeHints              sh;
291         XClassHint              ch;
292         XWMHints                *hints;
293 };
294 TAILQ_HEAD(ws_win_list, ws_win);
295
296 /* pid goo */
297 struct pid_e {
298         TAILQ_ENTRY(pid_e)      entry;
299         long                    pid;
300         int                     ws;
301 };
302 TAILQ_HEAD(pid_list, pid_e);
303 struct pid_list                 pidlist = TAILQ_HEAD_INITIALIZER(pidlist);
304
305 /* layout handlers */
306 void    stack(void);
307 void    vertical_config(struct workspace *, int);
308 void    vertical_stack(struct workspace *, struct swm_geometry *);
309 void    horizontal_config(struct workspace *, int);
310 void    horizontal_stack(struct workspace *, struct swm_geometry *);
311 void    max_stack(struct workspace *, struct swm_geometry *);
312
313 struct ws_win *find_window(Window);
314
315 void    grabbuttons(struct ws_win *, int);
316 void    new_region(struct swm_screen *, int, int, int, int);
317 void    unmanage_window(struct ws_win *);
318 long    getstate(Window);
319
320 struct layout {
321         void            (*l_stack)(struct workspace *, struct swm_geometry *);
322         void            (*l_config)(struct workspace *, int);
323         u_int32_t       flags;
324 #define SWM_L_FOCUSPREV         (1<<0)
325 #define SWM_L_MAPONFOCUS        (1<<1)
326         char            *name;
327 } layouts[] =  {
328         /* stack,               configure */
329         { vertical_stack,       vertical_config,        0,      "[|]" },
330         { horizontal_stack,     horizontal_config,      0,      "[-]" },
331         { max_stack,            NULL,
332           SWM_L_MAPONFOCUS | SWM_L_FOCUSPREV,                   "[ ]" },
333         { NULL,                 NULL,                   0,      NULL  },
334 };
335
336 /* position of max_stack mode in the layouts array, index into layouts! */
337 #define SWM_V_STACK             (0)
338 #define SWM_H_STACK             (1)
339 #define SWM_MAX_STACK           (2)
340
341 #define SWM_H_SLICE             (32)
342 #define SWM_V_SLICE             (32)
343
344 /* define work spaces */
345 struct workspace {
346         int                     idx;            /* workspace index */
347         int                     always_raise;   /* raise windows on focus */
348         struct layout           *cur_layout;    /* current layout handlers */
349         struct ws_win           *focus;         /* may be NULL */
350         struct ws_win           *focus_prev;    /* may be NULL */
351         struct swm_region       *r;             /* may be NULL */
352         struct swm_region       *old_r;         /* may be NULL */
353         struct ws_win_list      winlist;        /* list of windows in ws */
354         struct ws_win_list      unmanagedlist;  /* list of dead windows in ws */
355
356         /* stacker state */
357         struct {
358                                 int horizontal_msize;
359                                 int horizontal_mwin;
360                                 int horizontal_stacks;
361                                 int vertical_msize;
362                                 int vertical_mwin;
363                                 int vertical_stacks;
364         } l_state;
365 };
366
367 enum    { SWM_S_COLOR_BAR, SWM_S_COLOR_BAR_BORDER, SWM_S_COLOR_BAR_FONT,
368           SWM_S_COLOR_FOCUS, SWM_S_COLOR_UNFOCUS, SWM_S_COLOR_MAX };
369
370 /* physical screen mapping */
371 #define SWM_WS_MAX              (10)
372 struct swm_screen {
373         int                     idx;    /* screen index */
374         struct swm_region_list  rl;     /* list of regions on this screen */
375         struct swm_region_list  orl;    /* list of old regions */
376         Window                  root;
377         struct workspace        ws[SWM_WS_MAX];
378
379         /* colors */
380         struct {
381                 unsigned long   color;
382                 char            *name;
383         } c[SWM_S_COLOR_MAX];
384 };
385 struct swm_screen       *screens;
386 int                     num_screens;
387
388 /* args to functions */
389 union arg {
390         int                     id;
391 #define SWM_ARG_ID_FOCUSNEXT    (0)
392 #define SWM_ARG_ID_FOCUSPREV    (1)
393 #define SWM_ARG_ID_FOCUSMAIN    (2)
394 #define SWM_ARG_ID_FOCUSCUR     (4)
395 #define SWM_ARG_ID_SWAPNEXT     (10)
396 #define SWM_ARG_ID_SWAPPREV     (11)
397 #define SWM_ARG_ID_SWAPMAIN     (12)
398 #define SWM_ARG_ID_MOVELAST     (13)
399 #define SWM_ARG_ID_MASTERSHRINK (20)
400 #define SWM_ARG_ID_MASTERGROW   (21)
401 #define SWM_ARG_ID_MASTERADD    (22)
402 #define SWM_ARG_ID_MASTERDEL    (23)
403 #define SWM_ARG_ID_STACKRESET   (30)
404 #define SWM_ARG_ID_STACKINIT    (31)
405 #define SWM_ARG_ID_CYCLEWS_UP   (40)
406 #define SWM_ARG_ID_CYCLEWS_DOWN (41)
407 #define SWM_ARG_ID_CYCLESC_UP   (42)
408 #define SWM_ARG_ID_CYCLESC_DOWN (43)
409 #define SWM_ARG_ID_STACKINC     (50)
410 #define SWM_ARG_ID_STACKDEC     (51)
411 #define SWM_ARG_ID_SS_ALL       (60)
412 #define SWM_ARG_ID_SS_WINDOW    (61)
413 #define SWM_ARG_ID_DONTCENTER   (70)
414 #define SWM_ARG_ID_CENTER       (71)
415 #define SWM_ARG_ID_KILLWINDOW   (80)
416 #define SWM_ARG_ID_DELETEWINDOW (81)
417         char                    **argv;
418 };
419
420 void    focus(struct swm_region *, union arg *);
421 void    focus_magic(struct ws_win *);
422
423 /* quirks */
424 struct quirk {
425         char                    *class;
426         char                    *name;
427         unsigned long           quirk;
428 #define SWM_Q_FLOAT             (1<<0)  /* float this window */
429 #define SWM_Q_TRANSSZ           (1<<1)  /* transiend window size too small */
430 #define SWM_Q_ANYWHERE          (1<<2)  /* don't position this window */
431 #define SWM_Q_XTERM_FONTADJ     (1<<3)  /* adjust xterm fonts when resizing */
432 #define SWM_Q_FULLSCREEN        (1<<4)  /* remove border */
433 #define SWM_Q_FOCUSPREV         (1<<5)  /* focus on caller */
434 };
435 int                             quirks_size = 0, quirks_length = 0;
436 struct quirk                    *quirks = NULL;
437
438 /*
439  * Supported EWMH hints should be added to
440  * both the enum and the ewmh array
441  */
442 enum { _NET_ACTIVE_WINDOW, _NET_MOVERESIZE_WINDOW, _NET_CLOSE_WINDOW,
443     _NET_WM_WINDOW_TYPE, _NET_WM_WINDOW_TYPE_DOCK,
444     _NET_WM_WINDOW_TYPE_TOOLBAR, _NET_WM_WINDOW_TYPE_UTILITY,
445     _NET_WM_WINDOW_TYPE_SPLASH, _NET_WM_WINDOW_TYPE_DIALOG,
446     _NET_WM_WINDOW_TYPE_NORMAL, _NET_WM_STATE,
447     _NET_WM_STATE_MAXIMIZED_HORZ, _NET_WM_STATE_MAXIMIZED_VERT,
448     _NET_WM_STATE_SKIP_TASKBAR, _NET_WM_STATE_SKIP_PAGER,
449     _NET_WM_STATE_HIDDEN, _NET_WM_STATE_ABOVE, _SWM_WM_STATE_MANUAL,
450     _NET_WM_STATE_FULLSCREEN, _NET_WM_ALLOWED_ACTIONS, _NET_WM_ACTION_MOVE,
451     _NET_WM_ACTION_RESIZE, _NET_WM_ACTION_FULLSCREEN, _NET_WM_ACTION_CLOSE,
452     SWM_EWMH_HINT_MAX };
453
454 struct ewmh_hint {
455         char    *name;
456         Atom     atom;
457 } ewmh[SWM_EWMH_HINT_MAX] =     {
458     /* must be in same order as in the enum */
459     {"_NET_ACTIVE_WINDOW", None},
460     {"_NET_MOVERESIZE_WINDOW", None},
461     {"_NET_CLOSE_WINDOW", None},
462     {"_NET_WM_WINDOW_TYPE", None},
463     {"_NET_WM_WINDOW_TYPE_DOCK", None},
464     {"_NET_WM_WINDOW_TYPE_TOOLBAR", None},
465     {"_NET_WM_WINDOW_TYPE_UTILITY", None},
466     {"_NET_WM_WINDOW_TYPE_SPLASH", None},
467     {"_NET_WM_WINDOW_TYPE_DIALOG", None},
468     {"_NET_WM_WINDOW_TYPE_NORMAL", None},
469     {"_NET_WM_STATE", None},
470     {"_NET_WM_STATE_MAXIMIZED_HORZ", None},
471     {"_NET_WM_STATE_MAXIMIZED_VERT", None},
472     {"_NET_WM_STATE_SKIP_TASKBAR", None},
473     {"_NET_WM_STATE_SKIP_PAGER", None},
474     {"_NET_WM_STATE_HIDDEN", None},
475     {"_NET_WM_STATE_ABOVE", None},
476     {"_SWM_WM_STATE_MANUAL", None},
477     {"_NET_WM_STATE_FULLSCREEN", None},
478     {"_NET_WM_ALLOWED_ACTIONS", None},
479     {"_NET_WM_ACTION_MOVE", None},
480     {"_NET_WM_ACTION_RESIZE", None},
481     {"_NET_WM_ACTION_FULLSCREEN", None},
482     {"_NET_WM_ACTION_CLOSE", None},
483 };
484
485 void    store_float_geom(struct ws_win *win, struct swm_region *r);
486 int     floating_toggle_win(struct ws_win *win);
487 void    spawn_select(struct swm_region *, union arg *, char *, int *);
488
489 int
490 get_property(Window id, Atom atom, long count, Atom type,
491     unsigned long *n, unsigned char **data)
492 {
493         int                     format, status;
494         unsigned long           tmp, extra;
495         unsigned long           *nitems;
496         Atom                    real;
497
498         nitems = n != NULL ? n : &tmp;
499         status = XGetWindowProperty(display, id, atom, 0L, count, False, type,
500             &real, &format, nitems, &extra, data);
501
502         if (status != Success)
503                 return False;
504         if (real != type)
505                 return False;
506
507         return True;
508 }
509
510 void
511 update_iconic(struct ws_win *win, int newv)
512 {
513         int32_t v = newv;
514         Atom iprop;
515
516         win->iconic = newv;
517
518         iprop = XInternAtom(display, "_SWM_ICONIC", False);
519         if (!iprop)
520                 return;
521         if (newv)
522                 XChangeProperty(display, win->id, iprop, XA_INTEGER, 32,
523                     PropModeReplace, (unsigned char *)&v, 1);
524         else
525                 XDeleteProperty(display, win->id, iprop);
526 }
527
528 int
529 get_iconic(struct ws_win *win)
530 {
531         int32_t v = 0;
532         int retfmt, status;
533         Atom iprop, rettype;
534         unsigned long nitems, extra;
535         unsigned char *prop = NULL;
536
537         iprop = XInternAtom(display, "_SWM_ICONIC", False);
538         if (!iprop)
539                 goto out;
540         status = XGetWindowProperty(display, win->id, iprop, 0L, 1L,
541             False, XA_INTEGER, &rettype, &retfmt, &nitems, &extra, &prop);
542         if (status != Success)
543                 goto out;
544         if (rettype != XA_INTEGER || retfmt != 32)
545                 goto out;
546         if (nitems != 1)
547                 goto out;
548         v = *((int32_t *)prop);
549
550 out:
551         if (prop != NULL)
552                 XFree(prop);
553         return (v);
554 }
555
556 void
557 setup_ewmh(void)
558 {
559         int                     i,j;
560         Atom                    sup_list;
561
562         sup_list = XInternAtom(display, "_NET_SUPPORTED", False);
563
564         for (i = 0; i < LENGTH(ewmh); i++)
565                 ewmh[i].atom = XInternAtom(display, ewmh[i].name, False);
566
567         for (i = 0; i < ScreenCount(display); i++) {
568                 /* Support check window will be created by workaround(). */
569
570                 /* Report supported atoms */
571                 XDeleteProperty(display, screens[i].root, sup_list);
572                 for (j = 0; j < LENGTH(ewmh); j++)
573                         XChangeProperty(display, screens[i].root,
574                             sup_list, XA_ATOM, 32,
575                             PropModeAppend, (unsigned char *)&ewmh[j].atom,1);
576         }
577 }
578
579 void
580 teardown_ewmh(void)
581 {
582         int                     i, success;
583         unsigned char           *data = NULL;
584         unsigned long           n;
585         Atom                    sup_check, sup_list;
586         Window                  id;
587
588         sup_check = XInternAtom(display, "_NET_SUPPORTING_WM_CHECK", False);
589         sup_list = XInternAtom(display, "_NET_SUPPORTED", False);
590
591         for (i = 0; i < ScreenCount(display); i++) {
592                 /* Get the support check window and destroy it */
593                 success = get_property(screens[i].root, sup_check, 1, XA_WINDOW,
594                     &n, &data);
595
596                 if (success) {
597                         id = data[0];
598                         XDestroyWindow(display, id);
599                         XDeleteProperty(display, screens[i].root, sup_check);
600                         XDeleteProperty(display, screens[i].root, sup_list);
601                 }
602
603                 XFree(data);
604         }
605 }
606
607 void
608 ewmh_autoquirk(struct ws_win *win)
609 {
610         int                     success, i;
611         unsigned long           *data = NULL;
612         unsigned long           n;
613         Atom                    type;
614
615         success = get_property(win->id, ewmh[_NET_WM_WINDOW_TYPE].atom, (~0L),
616             XA_ATOM, &n, (unsigned char **)&data);
617
618         if (!success) {
619                 XFree(data);
620                 return;
621         }
622
623         for (i = 0; i < n; i++) {
624                 type = data[i];
625                 if (type == ewmh[_NET_WM_WINDOW_TYPE_NORMAL].atom)
626                         break;
627                 if (type == ewmh[_NET_WM_WINDOW_TYPE_DOCK].atom ||
628                     type == ewmh[_NET_WM_WINDOW_TYPE_TOOLBAR].atom ||
629                     type == ewmh[_NET_WM_WINDOW_TYPE_UTILITY].atom) {
630                         win->floating = 1;
631                         win->quirks = SWM_Q_FLOAT | SWM_Q_ANYWHERE;
632                         break;
633                 }
634                 if (type == ewmh[_NET_WM_WINDOW_TYPE_SPLASH].atom ||
635                     type == ewmh[_NET_WM_WINDOW_TYPE_DIALOG].atom) {
636                         win->floating = 1;
637                         win->quirks = SWM_Q_FLOAT;
638                         break;
639                 }
640         }
641
642         XFree(data);
643 }
644
645 #define SWM_EWMH_ACTION_COUNT_MAX       (6)
646 #define EWMH_F_FULLSCREEN               (1<<0)
647 #define EWMH_F_ABOVE                    (1<<1)
648 #define EWMH_F_HIDDEN                   (1<<2)
649 #define EWMH_F_SKIP_PAGER               (1<<3)
650 #define EWMH_F_SKIP_TASKBAR             (1<<4)
651 #define SWM_F_MANUAL                    (1<<5)
652
653 int
654 ewmh_set_win_fullscreen(struct ws_win *win, int fs)
655 {
656         struct swm_geometry     rg;
657
658         if (!win->ws->r)
659                 return 0;
660
661         if (!win->floating)
662                 return 0;
663
664         DNPRINTF(SWM_D_MISC, "ewmh_set_win_fullscreen: win 0x%lx fs: %d\n",
665             win->id, fs);
666
667         rg = win->ws->r->g;
668
669         if (fs) {
670                 store_float_geom(win, win->ws->r);
671
672                 win->g.x = rg.x;
673                 win->g.y = rg.y;
674                 win->g.w = rg.w;
675                 win->g.h = rg.h;
676         } else {
677                 if (win->g_floatvalid) {
678                         /* refloat at last floating relative position */
679                         win->g.x = win->g_float.x - win->rg_float.x + rg.x;
680                         win->g.y = win->g_float.y - win->rg_float.y + rg.y;
681                         win->g.w = win->g_float.w;
682                         win->g.h = win->g_float.h;
683                 }
684         }
685
686         return 1;
687 }
688
689 void
690 ewmh_update_actions(struct ws_win *win)
691 {
692         Atom                    actions[SWM_EWMH_ACTION_COUNT_MAX];
693         int                     n = 0;
694
695         if (win == NULL)
696                 return;
697
698         actions[n++] = ewmh[_NET_WM_ACTION_CLOSE].atom;
699
700         if (win->floating) {
701                 actions[n++] = ewmh[_NET_WM_ACTION_MOVE].atom;
702                 actions[n++] = ewmh[_NET_WM_ACTION_RESIZE].atom;
703         }
704
705         XChangeProperty(display, win->id, ewmh[_NET_WM_ALLOWED_ACTIONS].atom,
706             XA_ATOM, 32, PropModeReplace, (unsigned char *)actions, n);
707 }
708
709 #define _NET_WM_STATE_REMOVE    0    /* remove/unset property */
710 #define _NET_WM_STATE_ADD       1    /* add/set property */
711 #define _NET_WM_STATE_TOGGLE    2    /* toggle property */
712
713 void
714 ewmh_update_win_state(struct ws_win *win, long state, long action)
715 {
716         unsigned int            mask = 0;
717         unsigned int            changed = 0;
718         unsigned int            orig_flags;
719
720         if (win == NULL)
721                 return;
722
723         if (state == ewmh[_NET_WM_STATE_FULLSCREEN].atom)
724                 mask = EWMH_F_FULLSCREEN;
725         if (state == ewmh[_NET_WM_STATE_ABOVE].atom)
726                 mask = EWMH_F_ABOVE;
727         if (state == ewmh[_SWM_WM_STATE_MANUAL].atom)
728                 mask = SWM_F_MANUAL;
729         if (state == ewmh[_NET_WM_STATE_SKIP_PAGER].atom)
730                 mask = EWMH_F_SKIP_PAGER;
731         if (state == ewmh[_NET_WM_STATE_SKIP_TASKBAR].atom)
732                 mask = EWMH_F_SKIP_TASKBAR;
733
734
735         orig_flags = win->ewmh_flags;
736
737         switch (action) {
738         case _NET_WM_STATE_REMOVE:
739                 win->ewmh_flags &= ~mask;
740                 break;
741         case _NET_WM_STATE_ADD:
742                 win->ewmh_flags |= mask;
743                 break;
744         case _NET_WM_STATE_TOGGLE:
745                 win->ewmh_flags ^= mask;
746                 break;
747         }
748
749         changed = (win->ewmh_flags & mask) ^ (orig_flags & mask) ? 1 : 0;
750
751         if (state == ewmh[_NET_WM_STATE_ABOVE].atom)
752                 if (changed)
753                         if (!floating_toggle_win(win))
754                                 win->ewmh_flags = orig_flags; /* revert */
755         if (state == ewmh[_SWM_WM_STATE_MANUAL].atom)
756                 if (changed)
757                         win->manual = (win->ewmh_flags & SWM_F_MANUAL) != 0;
758         if (state == ewmh[_NET_WM_STATE_FULLSCREEN].atom)
759                 if (changed)
760                         if (!ewmh_set_win_fullscreen(win,
761                             win->ewmh_flags & EWMH_F_FULLSCREEN))
762                                 win->ewmh_flags = orig_flags; /* revert */
763
764         XDeleteProperty(display, win->id, ewmh[_NET_WM_STATE].atom);
765
766         if (win->ewmh_flags & EWMH_F_FULLSCREEN)
767                 XChangeProperty(display, win->id, ewmh[_NET_WM_STATE].atom,
768                     XA_ATOM, 32, PropModeAppend,
769                     (unsigned char *)&ewmh[_NET_WM_STATE_FULLSCREEN].atom, 1);
770         if (win->ewmh_flags & EWMH_F_SKIP_PAGER)
771                 XChangeProperty(display, win->id, ewmh[_NET_WM_STATE].atom,
772                     XA_ATOM, 32, PropModeAppend,
773                     (unsigned char *)&ewmh[_NET_WM_STATE_SKIP_PAGER].atom, 1);
774         if (win->ewmh_flags & EWMH_F_SKIP_TASKBAR)
775                 XChangeProperty(display, win->id, ewmh[_NET_WM_STATE].atom,
776                     XA_ATOM, 32, PropModeAppend,
777                     (unsigned char *)&ewmh[_NET_WM_STATE_SKIP_TASKBAR].atom, 1);
778         if (win->ewmh_flags & EWMH_F_ABOVE)
779                 XChangeProperty(display, win->id, ewmh[_NET_WM_STATE].atom,
780                     XA_ATOM, 32, PropModeAppend,
781                     (unsigned char *)&ewmh[_NET_WM_STATE_ABOVE].atom, 1);
782         if (win->ewmh_flags & SWM_F_MANUAL)
783                 XChangeProperty(display, win->id, ewmh[_NET_WM_STATE].atom,
784                     XA_ATOM, 32, PropModeAppend,
785                     (unsigned char *)&ewmh[_SWM_WM_STATE_MANUAL].atom, 1);
786 }
787
788 void
789 ewmh_get_win_state(struct ws_win *win)
790 {
791         int                     success, i;
792         unsigned long           n;
793         Atom                    *states;
794
795         if (win == NULL)
796                 return;
797
798         win->ewmh_flags = 0;
799         if (win->floating)
800                 win->ewmh_flags |= EWMH_F_ABOVE;
801         if (win->manual)
802                 win->ewmh_flags |= SWM_F_MANUAL;
803
804         success = get_property(win->id, ewmh[_NET_WM_STATE].atom,
805             (~0L), XA_ATOM, &n, (unsigned char **)&states);
806
807         if (!success)
808                 return;
809
810         for (i = 0; i < n; i++)
811                 ewmh_update_win_state(win, states[i], _NET_WM_STATE_ADD);
812
813         XFree(states);
814 }
815
816 /* events */
817 #ifdef SWM_DEBUG
818 void
819 dumpevent(XEvent *e)
820 {
821         char                    *name = NULL;
822
823         switch (e->type) {
824         case KeyPress:
825                 name = "KeyPress";
826                 break;
827         case KeyRelease:
828                 name = "KeyRelease";
829                 break;
830         case ButtonPress:
831                 name = "ButtonPress";
832                 break;
833         case ButtonRelease:
834                 name = "ButtonRelease";
835                 break;
836         case MotionNotify:
837                 name = "MotionNotify";
838                 break;
839         case EnterNotify:
840                 name = "EnterNotify";
841                 break;
842         case LeaveNotify:
843                 name = "LeaveNotify";
844                 break;
845         case FocusIn:
846                 name = "FocusIn";
847                 break;
848         case FocusOut:
849                 name = "FocusOut";
850                 break;
851         case KeymapNotify:
852                 name = "KeymapNotify";
853                 break;
854         case Expose:
855                 name = "Expose";
856                 break;
857         case GraphicsExpose:
858                 name = "GraphicsExpose";
859                 break;
860         case NoExpose:
861                 name = "NoExpose";
862                 break;
863         case VisibilityNotify:
864                 name = "VisibilityNotify";
865                 break;
866         case CreateNotify:
867                 name = "CreateNotify";
868                 break;
869         case DestroyNotify:
870                 name = "DestroyNotify";
871                 break;
872         case UnmapNotify:
873                 name = "UnmapNotify";
874                 break;
875         case MapNotify:
876                 name = "MapNotify";
877                 break;
878         case MapRequest:
879                 name = "MapRequest";
880                 break;
881         case ReparentNotify:
882                 name = "ReparentNotify";
883                 break;
884         case ConfigureNotify:
885                 name = "ConfigureNotify";
886                 break;
887         case ConfigureRequest:
888                 name = "ConfigureRequest";
889                 break;
890         case GravityNotify:
891                 name = "GravityNotify";
892                 break;
893         case ResizeRequest:
894                 name = "ResizeRequest";
895                 break;
896         case CirculateNotify:
897                 name = "CirculateNotify";
898                 break;
899         case CirculateRequest:
900                 name = "CirculateRequest";
901                 break;
902         case PropertyNotify:
903                 name = "PropertyNotify";
904                 break;
905         case SelectionClear:
906                 name = "SelectionClear";
907                 break;
908         case SelectionRequest:
909                 name = "SelectionRequest";
910                 break;
911         case SelectionNotify:
912                 name = "SelectionNotify";
913                 break;
914         case ColormapNotify:
915                 name = "ColormapNotify";
916                 break;
917         case ClientMessage:
918                 name = "ClientMessage";
919                 break;
920         case MappingNotify:
921                 name = "MappingNotify";
922                 break;
923         }
924
925         if (name)
926                 DNPRINTF(SWM_D_EVENTQ ,"window: %lu event: %s (%d), %d "
927                     "remaining\n",
928                     e->xany.window, name, e->type, QLength(display));
929         else
930                 DNPRINTF(SWM_D_EVENTQ, "window: %lu unknown event %d, %d "
931                     "remaining\n",
932                     e->xany.window, e->type, QLength(display));
933 }
934
935 void
936 dumpwins(struct swm_region *r, union arg *args)
937 {
938         struct ws_win           *win;
939         unsigned int            state;
940         XWindowAttributes       wa;
941
942         if (r->ws == NULL) {
943                 fprintf(stderr, "invalid workspace\n");
944                 return;
945         }
946
947         fprintf(stderr, "=== managed window list ws %02d ===\n", r->ws->idx);
948
949         TAILQ_FOREACH(win, &r->ws->winlist, entry) {
950                 state = getstate(win->id);
951                 if (!XGetWindowAttributes(display, win->id, &wa))
952                         fprintf(stderr, "window: %lu failed "
953                             "XGetWindowAttributes\n", win->id);
954                 fprintf(stderr, "window: %lu map_state: %d state: %d "
955                     "transient: %lu\n",
956                     win->id, wa.map_state, state, win->transient);
957         }
958
959         fprintf(stderr, "===== unmanaged window list =====\n");
960         TAILQ_FOREACH(win, &r->ws->unmanagedlist, entry) {
961                 state = getstate(win->id);
962                 if (!XGetWindowAttributes(display, win->id, &wa))
963                         fprintf(stderr, "window: %lu failed "
964                             "XGetWindowAttributes\n", win->id);
965                 fprintf(stderr, "window: %lu map_state: %d state: %d "
966                     "transient: %lu\n",
967                     win->id, wa.map_state, state, win->transient);
968         }
969
970         fprintf(stderr, "=================================\n");
971 }
972 #else
973 #define dumpevent(e)
974 void
975 dumpwins(struct swm_region *r, union arg *args)
976 {
977 }
978 #endif /* SWM_DEBUG */
979
980 void                    expose(XEvent *);
981 void                    keypress(XEvent *);
982 void                    buttonpress(XEvent *);
983 void                    configurerequest(XEvent *);
984 void                    configurenotify(XEvent *);
985 void                    destroynotify(XEvent *);
986 void                    enternotify(XEvent *);
987 void                    focusevent(XEvent *);
988 void                    mapnotify(XEvent *);
989 void                    mappingnotify(XEvent *);
990 void                    maprequest(XEvent *);
991 void                    propertynotify(XEvent *);
992 void                    unmapnotify(XEvent *);
993 void                    visibilitynotify(XEvent *);
994 void                    clientmessage(XEvent *);
995
996 void                    (*handler[LASTEvent])(XEvent *) = {
997                                 [Expose] = expose,
998                                 [KeyPress] = keypress,
999                                 [ButtonPress] = buttonpress,
1000                                 [ConfigureRequest] = configurerequest,
1001                                 [ConfigureNotify] = configurenotify,
1002                                 [DestroyNotify] = destroynotify,
1003                                 [EnterNotify] = enternotify,
1004                                 [FocusIn] = focusevent,
1005                                 [FocusOut] = focusevent,
1006                                 [MapNotify] = mapnotify,
1007                                 [MappingNotify] = mappingnotify,
1008                                 [MapRequest] = maprequest,
1009                                 [PropertyNotify] = propertynotify,
1010                                 [UnmapNotify] = unmapnotify,
1011                                 [VisibilityNotify] = visibilitynotify,
1012                                 [ClientMessage] = clientmessage,
1013 };
1014
1015 void
1016 sighdlr(int sig)
1017 {
1018         int                     saved_errno, status;
1019         pid_t                   pid;
1020
1021         saved_errno = errno;
1022
1023         switch (sig) {
1024         case SIGCHLD:
1025                 while ((pid = waitpid(WAIT_ANY, &status, WNOHANG)) != 0) {
1026                         if (pid == -1) {
1027                                 if (errno == EINTR)
1028                                         continue;
1029 #ifdef SWM_DEBUG
1030                                 if (errno != ECHILD)
1031                                         warn("sighdlr: waitpid");
1032 #endif /* SWM_DEBUG */
1033                                 break;
1034                         }
1035                         if (pid == searchpid)
1036                                 search_resp = 1;
1037
1038 #ifdef SWM_DEBUG
1039                         if (WIFEXITED(status)) {
1040                                 if (WEXITSTATUS(status) != 0)
1041                                         warnx("sighdlr: child exit status: %d",
1042                                             WEXITSTATUS(status));
1043                         } else
1044                                 warnx("sighdlr: child is terminated "
1045                                     "abnormally");
1046 #endif /* SWM_DEBUG */
1047                 }
1048                 break;
1049
1050         case SIGHUP:
1051                 restart_wm = 1;
1052                 break;
1053         case SIGINT:
1054         case SIGTERM:
1055         case SIGQUIT:
1056                 running = 0;
1057                 break;
1058         }
1059
1060         errno = saved_errno;
1061 }
1062
1063 struct pid_e *
1064 find_pid(long pid)
1065 {
1066         struct pid_e            *p = NULL;
1067
1068         DNPRINTF(SWM_D_MISC, "find_pid: %lu\n", pid);
1069
1070         if (pid == 0)
1071                 return (NULL);
1072
1073         TAILQ_FOREACH(p, &pidlist, entry) {
1074                 if (p->pid == pid)
1075                         return (p);
1076         }
1077
1078         return (NULL);
1079 }
1080
1081 unsigned long
1082 name_to_color(char *colorname)
1083 {
1084         Colormap                cmap;
1085         Status                  status;
1086         XColor                  screen_def, exact_def;
1087         unsigned long           result = 0;
1088         char                    cname[32] = "#";
1089
1090         cmap = DefaultColormap(display, screens[0].idx);
1091         status = XAllocNamedColor(display, cmap, colorname,
1092             &screen_def, &exact_def);
1093         if (!status) {
1094                 strlcat(cname, colorname + 2, sizeof cname - 1);
1095                 status = XAllocNamedColor(display, cmap, cname, &screen_def,
1096                     &exact_def);
1097         }
1098         if (status)
1099                 result = screen_def.pixel;
1100         else
1101                 fprintf(stderr, "color '%s' not found.\n", colorname);
1102
1103         return (result);
1104 }
1105
1106 void
1107 setscreencolor(char *val, int i, int c)
1108 {
1109         if (i > 0 && i <= ScreenCount(display)) {
1110                 screens[i - 1].c[c].color = name_to_color(val);
1111                 free(screens[i - 1].c[c].name);
1112                 if ((screens[i - 1].c[c].name = strdup(val)) == NULL)
1113                         errx(1, "strdup");
1114         } else if (i == -1) {
1115                 for (i = 0; i < ScreenCount(display); i++) {
1116                         screens[i].c[c].color = name_to_color(val);
1117                         free(screens[i].c[c].name);
1118                         if ((screens[i].c[c].name = strdup(val)) == NULL)
1119                                 errx(1, "strdup");
1120                 }
1121         } else
1122                 errx(1, "invalid screen index: %d out of bounds (maximum %d)\n",
1123                     i, ScreenCount(display));
1124 }
1125
1126 void
1127 custom_region(char *val)
1128 {
1129         unsigned int                    sidx, x, y, w, h;
1130
1131         if (sscanf(val, "screen[%u]:%ux%u+%u+%u", &sidx, &w, &h, &x, &y) != 5)
1132                 errx(1, "invalid custom region, "
1133                     "should be 'screen[<n>]:<n>x<n>+<n>+<n>\n");
1134         if (sidx < 1 || sidx > ScreenCount(display))
1135                 errx(1, "invalid screen index: %d out of bounds (maximum %d)\n",
1136                     sidx, ScreenCount(display));
1137         sidx--;
1138
1139         if (w < 1 || h < 1)
1140                 errx(1, "region %ux%u+%u+%u too small\n", w, h, x, y);
1141
1142         if (x  < 0 || x > DisplayWidth(display, sidx) ||
1143             y < 0 || y > DisplayHeight(display, sidx) ||
1144             w + x > DisplayWidth(display, sidx) ||
1145             h + y > DisplayHeight(display, sidx)) {
1146                 fprintf(stderr, "ignoring region %ux%u+%u+%u "
1147                     "- not within screen boundaries "
1148                     "(%ux%u)\n", w, h, x, y,
1149                     DisplayWidth(display, sidx), DisplayHeight(display, sidx));
1150                 return;
1151         }
1152
1153         new_region(&screens[sidx], x, y, w, h);
1154 }
1155
1156 void
1157 socket_setnonblock(int fd)
1158 {
1159         int                     flags;
1160
1161         if ((flags = fcntl(fd, F_GETFL, 0)) == -1)
1162                 err(1, "fcntl F_GETFL");
1163         flags |= O_NONBLOCK;
1164         if ((flags = fcntl(fd, F_SETFL, flags)) == -1)
1165                 err(1, "fcntl F_SETFL");
1166 }
1167
1168 void
1169 bar_print(struct swm_region *r, char *s)
1170 {
1171         XClearWindow(display, r->bar_window);
1172         XSetForeground(display, bar_gc, r->s->c[SWM_S_COLOR_BAR_FONT].color);
1173         XDrawString(display, r->bar_window, bar_gc, 4, bar_fs->ascent, s,
1174             strlen(s));
1175 }
1176
1177 void
1178 bar_extra_stop(void)
1179 {
1180         if (bar_pipe[0]) {
1181                 close(bar_pipe[0]);
1182                 bzero(bar_pipe, sizeof bar_pipe);
1183         }
1184         if (bar_pid) {
1185                 kill(bar_pid, SIGTERM);
1186                 bar_pid = 0;
1187         }
1188         strlcpy(bar_ext, "", sizeof bar_ext);
1189         bar_extra = 0;
1190 }
1191
1192 void
1193 bar_class_name(char *s, ssize_t sz, struct ws_win *cur_focus)
1194 {
1195         int                     do_class, do_name;
1196         Status                  status;
1197         XClassHint              *xch = NULL;
1198
1199         if ((title_name_enabled == 1 || title_class_enabled == 1) &&
1200             cur_focus != NULL) {
1201                 if ((xch = XAllocClassHint()) == NULL)
1202                         goto out;
1203                 status = XGetClassHint(display, cur_focus->id, xch);
1204                 if (status == BadWindow || status == BadAlloc)
1205                         goto out;
1206                 do_class = (title_class_enabled && xch->res_class != NULL);
1207                 do_name = (title_name_enabled && xch->res_name != NULL);
1208                 if (do_class)
1209                         strlcat(s, xch->res_class, sz);
1210                 if (do_class && do_name)
1211                         strlcat(s, ":", sz);
1212                 if (do_name)
1213                         strlcat(s, xch->res_name, sz);
1214                 strlcat(s, "    ", sz);
1215         }
1216 out:
1217         if (xch)
1218                 XFree(xch);
1219 }
1220
1221 void
1222 bar_window_name(char *s, ssize_t sz, struct ws_win *cur_focus)
1223 {
1224         char                    *title;
1225
1226         if (window_name_enabled && cur_focus != NULL) {
1227                 XFetchName(display, cur_focus->id, &title);
1228                 if (title) {
1229                         if (cur_focus->floating)
1230                                 strlcat(s, "(f) ", sz);
1231                         strlcat(s, title, sz);
1232                         strlcat(s, " ", sz);
1233                         XFree(title);
1234                 }
1235         }
1236 }
1237
1238 void
1239 bar_update(void)
1240 {
1241         time_t                  tmt;
1242         struct tm               tm;
1243         struct swm_region       *r;
1244         int                     i, x;
1245         size_t                  len;
1246         char                    s[SWM_BAR_MAX];
1247         char                    cn[SWM_BAR_MAX];
1248         char                    loc[SWM_BAR_MAX];
1249         char                    *b;
1250         char                    *stack = "";
1251
1252         if (bar_enabled == 0)
1253                 return;
1254         if (bar_extra && bar_extra_running) {
1255                 /* ignore short reads; it'll correct itself */
1256                 while ((b = fgetln(stdin, &len)) != NULL)
1257                         if (b && b[len - 1] == '\n') {
1258                                 b[len - 1] = '\0';
1259                                 strlcpy(bar_ext, b, sizeof bar_ext);
1260                         }
1261                 if (b == NULL && errno != EAGAIN) {
1262                         fprintf(stderr, "bar_extra failed: errno: %d %s\n",
1263                             errno, strerror(errno));
1264                         bar_extra_stop();
1265                 }
1266         } else
1267                 strlcpy(bar_ext, "", sizeof bar_ext);
1268
1269         if (clock_enabled == 0)
1270                 strlcpy(s, "", sizeof s);
1271         else {
1272                 time(&tmt);
1273                 localtime_r(&tmt, &tm);
1274                 strftime(s, sizeof s, clock_format, &tm);
1275                 strlcat(s, "    ", sizeof s);
1276         }
1277
1278         for (i = 0; i < ScreenCount(display); i++) {
1279                 x = 1;
1280                 TAILQ_FOREACH(r, &screens[i].rl, entry) {
1281                         strlcpy(cn, "", sizeof cn);
1282                         if (r && r->ws) {
1283                                 bar_class_name(cn, sizeof cn, r->ws->focus);
1284                                 bar_window_name(cn, sizeof cn, r->ws->focus);
1285                         }
1286
1287                         if (stack_enabled)
1288                                 stack = r->ws->cur_layout->name;
1289
1290                         snprintf(loc, sizeof loc, "%d:%d %s   %s%s    %s    %s",
1291                             x++, r->ws->idx + 1, stack, s, cn, bar_ext,
1292                             bar_vertext);
1293                         bar_print(r, loc);
1294                 }
1295         }
1296         alarm(bar_delay);
1297 }
1298
1299 void
1300 bar_signal(int sig)
1301 {
1302         bar_alarm = 1;
1303 }
1304
1305 void
1306 bar_toggle(struct swm_region *r, union arg *args)
1307 {
1308         struct swm_region       *tmpr;
1309         int                     i, sc = ScreenCount(display);
1310
1311         DNPRINTF(SWM_D_MISC, "bar_toggle\n");
1312
1313         if (bar_enabled)
1314                 for (i = 0; i < sc; i++)
1315                         TAILQ_FOREACH(tmpr, &screens[i].rl, entry)
1316                                 XUnmapWindow(display, tmpr->bar_window);
1317         else
1318                 for (i = 0; i < sc; i++)
1319                         TAILQ_FOREACH(tmpr, &screens[i].rl, entry)
1320                                 XMapRaised(display, tmpr->bar_window);
1321
1322         bar_enabled = !bar_enabled;
1323
1324         stack();
1325         /* must be after stack */
1326         bar_update();
1327 }
1328
1329 void
1330 bar_refresh(void)
1331 {
1332         XSetWindowAttributes    wa;
1333         struct swm_region       *r;
1334         int                     i;
1335
1336         /* do this here because the conf file is in memory */
1337         if (bar_extra && bar_extra_running == 0 && bar_argv[0]) {
1338                 /* launch external status app */
1339                 bar_extra_running = 1;
1340                 if (pipe(bar_pipe) == -1)
1341                         err(1, "pipe error");
1342                 socket_setnonblock(bar_pipe[0]);
1343                 socket_setnonblock(bar_pipe[1]); /* XXX hmmm, really? */
1344                 if (dup2(bar_pipe[0], 0) == -1)
1345                         errx(1, "dup2");
1346                 if (dup2(bar_pipe[1], 1) == -1)
1347                         errx(1, "dup2");
1348                 if (signal(SIGPIPE, SIG_IGN) == SIG_ERR)
1349                         err(1, "could not disable SIGPIPE");
1350                 switch (bar_pid = fork()) {
1351                 case -1:
1352                         err(1, "cannot fork");
1353                         break;
1354                 case 0: /* child */
1355                         close(bar_pipe[0]);
1356                         execvp(bar_argv[0], bar_argv);
1357                         err(1, "%s external app failed", bar_argv[0]);
1358                         break;
1359                 default: /* parent */
1360                         close(bar_pipe[1]);
1361                         break;
1362                 }
1363         }
1364
1365         bzero(&wa, sizeof wa);
1366         for (i = 0; i < ScreenCount(display); i++)
1367                 TAILQ_FOREACH(r, &screens[i].rl, entry) {
1368                         wa.border_pixel =
1369                             screens[i].c[SWM_S_COLOR_BAR_BORDER].color;
1370                         wa.background_pixel =
1371                             screens[i].c[SWM_S_COLOR_BAR].color;
1372                         XChangeWindowAttributes(display, r->bar_window,
1373                             CWBackPixel | CWBorderPixel, &wa);
1374                 }
1375         bar_update();
1376 }
1377
1378 void
1379 bar_setup(struct swm_region *r)
1380 {
1381         int                     i, x, y;
1382
1383         if (bar_fs) {
1384                 XFreeFont(display, bar_fs);
1385                 bar_fs = NULL;
1386         }
1387
1388         for (i = 0; bar_fonts[i] != NULL; i++) {
1389                 bar_fs = XLoadQueryFont(display, bar_fonts[i]);
1390                 if (bar_fs) {
1391                         bar_fidx = i;
1392                         break;
1393                 }
1394         }
1395         if (bar_fonts[i] == NULL)
1396                         errx(1, "couldn't load font");
1397         if (bar_fs == NULL)
1398                 errx(1, "couldn't create font structure");
1399
1400         bar_height = bar_fs->ascent + bar_fs->descent + 1 +
1401             2 * bar_border_width;
1402         x = X(r);
1403         y = bar_at_bottom ? (Y(r) + HEIGHT(r) - bar_height) : Y(r);
1404
1405         r->bar_window = XCreateSimpleWindow(display,
1406             r->s->root, x, y, WIDTH(r) - 2 * bar_border_width,
1407             bar_height - 2 * bar_border_width,
1408             bar_border_width, r->s->c[SWM_S_COLOR_BAR_BORDER].color,
1409             r->s->c[SWM_S_COLOR_BAR].color);
1410         bar_gc = XCreateGC(display, r->bar_window, 0, &bar_gcv);
1411         XSetFont(display, bar_gc, bar_fs->fid);
1412         XSelectInput(display, r->bar_window, VisibilityChangeMask);
1413         if (bar_enabled)
1414                 XMapRaised(display, r->bar_window);
1415         DNPRINTF(SWM_D_MISC, "bar_setup: bar_window %lu\n", r->bar_window);
1416
1417         if (signal(SIGALRM, bar_signal) == SIG_ERR)
1418                 err(1, "could not install bar_signal");
1419         bar_refresh();
1420 }
1421
1422 void
1423 drain_enter_notify(void)
1424 {
1425         int                     i = 0;
1426         XEvent                  cne;
1427
1428         while (XCheckMaskEvent(display, EnterWindowMask, &cne))
1429                 i++;
1430
1431         DNPRINTF(SWM_D_MISC, "drain_enter_notify: drained %d\n", i);
1432 }
1433
1434 void
1435 set_win_state(struct ws_win *win, long state)
1436 {
1437         long                    data[] = {state, None};
1438
1439         DNPRINTF(SWM_D_EVENT, "set_win_state: window: %lu\n", win->id);
1440
1441         if (win == NULL)
1442                 return;
1443
1444         XChangeProperty(display, win->id, astate, astate, 32, PropModeReplace,
1445             (unsigned char *)data, 2);
1446 }
1447
1448 long
1449 getstate(Window w)
1450 {
1451         long                    result = -1;
1452         unsigned char           *p = NULL;
1453         unsigned long           n;
1454
1455         if (!get_property(w, astate, 2L, astate, &n, &p))
1456                 return (-1);
1457         if (n != 0)
1458                 result = *((long *)p);
1459         XFree(p);
1460         return (result);
1461 }
1462
1463 void
1464 version(struct swm_region *r, union arg *args)
1465 {
1466         bar_version = !bar_version;
1467         if (bar_version)
1468                 snprintf(bar_vertext, sizeof bar_vertext, "Version: %s CVS: %s",
1469                     SWM_VERSION, cvstag);
1470         else
1471                 strlcpy(bar_vertext, "", sizeof bar_vertext);
1472         bar_update();
1473 }
1474
1475 void
1476 client_msg(struct ws_win *win, Atom a)
1477 {
1478         XClientMessageEvent     cm;
1479
1480         if (win == NULL)
1481                 return;
1482
1483         bzero(&cm, sizeof cm);
1484         cm.type = ClientMessage;
1485         cm.window = win->id;
1486         cm.message_type = aprot;
1487         cm.format = 32;
1488         cm.data.l[0] = a;
1489         cm.data.l[1] = CurrentTime;
1490         XSendEvent(display, win->id, False, 0L, (XEvent *)&cm);
1491 }
1492
1493 void
1494 config_win(struct ws_win *win, XConfigureRequestEvent  *ev)
1495 {
1496         XConfigureEvent         ce;
1497
1498         if (win == NULL)
1499                 return;
1500
1501         if (ev == NULL) {
1502                 DNPRINTF(SWM_D_MISC,
1503                     "config_win: win %lu x %d y %d w %d h %d\n",
1504                     win->id, win->g.x, win->g.y, win->g.w, win->g.h);
1505
1506                 ce.type = ConfigureNotify;
1507                 ce.display = display;
1508                 ce.event = win->id;
1509                 ce.window = win->id;
1510                 ce.x = win->g.x;
1511                 ce.y = win->g.y;
1512                 ce.width = win->g.w;
1513                 ce.height = win->g.h;
1514                 ce.border_width = border_width;
1515                 ce.above = None;
1516                 ce.override_redirect = False;
1517         } else {
1518                 DNPRINTF(SWM_D_MISC,
1519                     "config_win: ev win %lu x %d y %d w %d h %d\n",
1520                     ev->window, ev->x, ev->y, ev->width, ev->height);
1521                 ce.type = ConfigureNotify;
1522                 ce.display = ev->display;
1523                 ce.event = ev->window;
1524                 ce.window = ev->window;
1525                 ce.x = ev->x;
1526                 ce.y = ev->y;
1527                 ce.width = ev->width;
1528                 ce.height = ev->height;
1529                 ce.border_width = ev->border_width;
1530                 ce.above = ev->above;
1531                 ce.override_redirect = False;
1532         }
1533
1534         XSendEvent(display, win->id, False, StructureNotifyMask, (XEvent *)&ce);
1535 }
1536
1537 int
1538 count_win(struct workspace *ws, int count_transient)
1539 {
1540         struct ws_win           *win;
1541         int                     count = 0;
1542
1543         TAILQ_FOREACH(win, &ws->winlist, entry) {
1544                 if (count_transient == 0 && win->floating)
1545                         continue;
1546                 if (count_transient == 0 && win->transient)
1547                         continue;
1548                 if (win->iconic)
1549                         continue;
1550                 count++;
1551         }
1552         DNPRINTF(SWM_D_MISC, "count_win: %d\n", count);
1553
1554         return (count);
1555 }
1556
1557 void
1558 quit(struct swm_region *r, union arg *args)
1559 {
1560         DNPRINTF(SWM_D_MISC, "quit\n");
1561         running = 0;
1562 }
1563
1564 void
1565 unmap_window(struct ws_win *win)
1566 {
1567         if (win == NULL)
1568                 return;
1569
1570         /* don't unmap again */
1571         if (getstate(win->id) == IconicState)
1572                 return;
1573
1574         set_win_state(win, IconicState);
1575
1576         XUnmapWindow(display, win->id);
1577         XSetWindowBorder(display, win->id,
1578             win->s->c[SWM_S_COLOR_UNFOCUS].color);
1579 }
1580
1581 void
1582 unmap_all(void)
1583 {
1584         struct ws_win           *win;
1585         int                     i, j;
1586
1587         for (i = 0; i < ScreenCount(display); i++)
1588                 for (j = 0; j < SWM_WS_MAX; j++)
1589                         TAILQ_FOREACH(win, &screens[i].ws[j].winlist, entry)
1590                                 unmap_window(win);
1591 }
1592
1593 void
1594 fake_keypress(struct ws_win *win, int keysym, int modifiers)
1595 {
1596         XKeyEvent event;
1597
1598         if (win == NULL)
1599                 return;
1600
1601         event.display = display;        /* Ignored, but what the hell */
1602         event.window = win->id;
1603         event.root = win->s->root;
1604         event.subwindow = None;
1605         event.time = CurrentTime;
1606         event.x = win->g.x;
1607         event.y = win->g.y;
1608         event.x_root = 1;
1609         event.y_root = 1;
1610         event.same_screen = True;
1611         event.keycode = XKeysymToKeycode(display, keysym);
1612         event.state = modifiers;
1613
1614         event.type = KeyPress;
1615         XSendEvent(event.display, event.window, True,
1616             KeyPressMask, (XEvent *)&event);
1617
1618         event.type = KeyRelease;
1619         XSendEvent(event.display, event.window, True,
1620             KeyPressMask, (XEvent *)&event);
1621
1622 }
1623
1624 void
1625 restart(struct swm_region *r, union arg *args)
1626 {
1627         DNPRINTF(SWM_D_MISC, "restart: %s\n", start_argv[0]);
1628
1629         /* disable alarm because the following code may not be interrupted */
1630         alarm(0);
1631         if (signal(SIGALRM, SIG_IGN) == SIG_ERR)
1632                 errx(1, "can't disable alarm");
1633
1634         bar_extra_stop();
1635         bar_extra = 1;
1636         unmap_all();
1637         XCloseDisplay(display);
1638         execvp(start_argv[0], start_argv);
1639         fprintf(stderr, "execvp failed\n");
1640         perror(" failed");
1641         quit(NULL, NULL);
1642 }
1643
1644 struct swm_region *
1645 root_to_region(Window root)
1646 {
1647         struct swm_region       *r = NULL;
1648         Window                  rr, cr;
1649         int                     i, x, y, wx, wy;
1650         unsigned int            mask;
1651
1652         for (i = 0; i < ScreenCount(display); i++)
1653                 if (screens[i].root == root)
1654                         break;
1655
1656         if (XQueryPointer(display, screens[i].root,
1657             &rr, &cr, &x, &y, &wx, &wy, &mask) != False) {
1658                 /* choose a region based on pointer location */
1659                 TAILQ_FOREACH(r, &screens[i].rl, entry)
1660                         if (x >= X(r) && x <= X(r) + WIDTH(r) &&
1661                             y >= Y(r) && y <= Y(r) + HEIGHT(r))
1662                                 break;
1663         }
1664
1665         if (r == NULL)
1666                 r = TAILQ_FIRST(&screens[i].rl);
1667
1668         return (r);
1669 }
1670
1671 struct ws_win *
1672 find_unmanaged_window(Window id)
1673 {
1674         struct ws_win           *win;
1675         int                     i, j;
1676
1677         for (i = 0; i < ScreenCount(display); i++)
1678                 for (j = 0; j < SWM_WS_MAX; j++)
1679                         TAILQ_FOREACH(win, &screens[i].ws[j].unmanagedlist,
1680                             entry)
1681                                 if (id == win->id)
1682                                         return (win);
1683         return (NULL);
1684 }
1685
1686 struct ws_win *
1687 find_window(Window id)
1688 {
1689         struct ws_win           *win;
1690         Window                  wrr, wpr, *wcr = NULL;
1691         int                     i, j;
1692         unsigned int            nc;
1693
1694         for (i = 0; i < ScreenCount(display); i++)
1695                 for (j = 0; j < SWM_WS_MAX; j++)
1696                         TAILQ_FOREACH(win, &screens[i].ws[j].winlist, entry)
1697                                 if (id == win->id)
1698                                         return (win);
1699
1700         /* if we were looking for the parent return that window instead */
1701         if (XQueryTree(display, id, &wrr, &wpr, &wcr, &nc) == 0)
1702                 return (NULL);
1703         if (wcr)
1704                 XFree(wcr);
1705
1706         /* ignore not found and root */
1707         if (wpr == 0 || wrr == wpr)
1708                 return (NULL);
1709
1710         /* look for parent */
1711         for (i = 0; i < ScreenCount(display); i++)
1712                 for (j = 0; j < SWM_WS_MAX; j++)
1713                         TAILQ_FOREACH(win, &screens[i].ws[j].winlist, entry)
1714                                 if (wpr == win->id)
1715                                         return (win);
1716
1717         return (NULL);
1718 }
1719
1720 void
1721 spawn(int ws_idx, union arg *args, int close_fd)
1722 {
1723         int                     fd;
1724         char                    *ret = NULL;
1725
1726         DNPRINTF(SWM_D_MISC, "spawn: %s\n", args->argv[0]);
1727
1728         if (display)
1729                 close(ConnectionNumber(display));
1730
1731         setenv("LD_PRELOAD", SWM_LIB, 1);
1732
1733         if (asprintf(&ret, "%d", ws_idx) == -1) {
1734                 perror("_SWM_WS");
1735                 _exit(1);
1736         }
1737         setenv("_SWM_WS", ret, 1);
1738         free(ret);
1739         ret = NULL;
1740
1741         if (asprintf(&ret, "%d", getpid()) == -1) {
1742                 perror("_SWM_PID");
1743                 _exit(1);
1744         }
1745         setenv("_SWM_PID", ret, 1);
1746         free(ret);
1747         ret = NULL;
1748
1749         if (setsid() == -1) {
1750                 perror("setsid");
1751                 _exit(1);
1752         }
1753
1754         if (close_fd) {
1755                 /*
1756                  * close stdin and stdout to prevent interaction between apps
1757                  * and the baraction script
1758                  * leave stderr open to record errors
1759                 */
1760                 if ((fd = open(_PATH_DEVNULL, O_RDWR, 0)) == -1) {
1761                         perror("open");
1762                         _exit(1);
1763                 }
1764                 dup2(fd, STDIN_FILENO);
1765                 dup2(fd, STDOUT_FILENO);
1766                 if (fd > 2)
1767                         close(fd);
1768         }
1769
1770         execvp(args->argv[0], args->argv);
1771
1772         perror("execvp");
1773         _exit(1);
1774 }
1775
1776 void
1777 spawnterm(struct swm_region *r, union arg *args)
1778 {
1779         DNPRINTF(SWM_D_MISC, "spawnterm\n");
1780
1781         if (fork() == 0) {
1782                 if (term_width)
1783                         setenv("_SWM_XTERM_FONTADJ", "", 1);
1784                 spawn(r->ws->idx, args, 1);
1785         }
1786 }
1787
1788 void
1789 kill_refs(struct ws_win *win)
1790 {
1791         int                     i, x;
1792         struct swm_region       *r;
1793         struct workspace        *ws;
1794
1795         if (win == NULL)
1796                 return;
1797
1798         for (i = 0; i < ScreenCount(display); i++)
1799                 TAILQ_FOREACH(r, &screens[i].rl, entry)
1800                         for (x = 0; x < SWM_WS_MAX; x++) {
1801                                 ws = &r->s->ws[x];
1802                                 if (win == ws->focus)
1803                                         ws->focus = NULL;
1804                                 if (win == ws->focus_prev)
1805                                         ws->focus_prev = NULL;
1806                         }
1807 }
1808
1809 int
1810 validate_win(struct ws_win *testwin)
1811 {
1812         struct ws_win           *win;
1813         struct workspace        *ws;
1814         struct swm_region       *r;
1815         int                     i, x, foundit = 0;
1816
1817         if (testwin == NULL)
1818                 return (0);
1819
1820         for (i = 0, foundit = 0; i < ScreenCount(display); i++)
1821                 TAILQ_FOREACH(r, &screens[i].rl, entry)
1822                         for (x = 0; x < SWM_WS_MAX; x++) {
1823                                 ws = &r->s->ws[x];
1824                                 TAILQ_FOREACH(win, &ws->winlist, entry)
1825                                         if (win == testwin)
1826                                                 return (0);
1827                         }
1828         return (1);
1829 }
1830
1831 int
1832 validate_ws(struct workspace *testws)
1833 {
1834         struct swm_region       *r;
1835         struct workspace        *ws;
1836         int                     foundit, i, x;
1837
1838         /* validate all ws */
1839         for (i = 0, foundit = 0; i < ScreenCount(display); i++)
1840                 TAILQ_FOREACH(r, &screens[i].rl, entry)
1841                         for (x = 0; x < SWM_WS_MAX; x++) {
1842                                 ws = &r->s->ws[x];
1843                                 if (ws == testws)
1844                                         return (0);
1845                         }
1846         return (1);
1847 }
1848
1849 void
1850 unfocus_win(struct ws_win *win)
1851 {
1852         XEvent                  cne;
1853         Window                  none = None;
1854
1855         DNPRINTF(SWM_D_FOCUS, "unfocus_win: id: %lu\n", WINID(win));
1856
1857         if (win == NULL)
1858                 return;
1859         if (win->ws == NULL)
1860                 return;
1861
1862         if (validate_ws(win->ws))
1863                 return; /* XXX this gets hit with thunderbird, needs fixing */
1864
1865         if (win->ws->r == NULL)
1866                 return;
1867
1868         if (validate_win(win)) {
1869                 kill_refs(win);
1870                 return;
1871         }
1872
1873         if (win->ws->focus == win) {
1874                 win->ws->focus = NULL;
1875                 win->ws->focus_prev = win;
1876         }
1877
1878         if (validate_win(win->ws->focus)) {
1879                 kill_refs(win->ws->focus);
1880                 win->ws->focus = NULL;
1881         }
1882         if (validate_win(win->ws->focus_prev)) {
1883                 kill_refs(win->ws->focus_prev);
1884                 win->ws->focus_prev = NULL;
1885         }
1886
1887         /* drain all previous unfocus events */
1888         while (XCheckTypedEvent(display, FocusOut, &cne) == True)
1889                 ;
1890
1891         grabbuttons(win, 0);
1892         XSetWindowBorder(display, win->id,
1893             win->ws->r->s->c[SWM_S_COLOR_UNFOCUS].color);
1894
1895         XChangeProperty(display, win->s->root,
1896             ewmh[_NET_ACTIVE_WINDOW].atom, XA_WINDOW, 32,
1897             PropModeReplace, (unsigned char *)&none,1);
1898 }
1899
1900 void
1901 unfocus_all(void)
1902 {
1903         struct ws_win           *win;
1904         int                     i, j;
1905
1906         DNPRINTF(SWM_D_FOCUS, "unfocus_all\n");
1907
1908         for (i = 0; i < ScreenCount(display); i++)
1909                 for (j = 0; j < SWM_WS_MAX; j++)
1910                         TAILQ_FOREACH(win, &screens[i].ws[j].winlist, entry)
1911                                 unfocus_win(win);
1912 }
1913
1914 void
1915 focus_win(struct ws_win *win)
1916 {
1917         XEvent                  cne;
1918         Window                  cur_focus;
1919         int                     rr;
1920         struct ws_win           *cfw = NULL;
1921
1922
1923         DNPRINTF(SWM_D_FOCUS, "focus_win: id: %lu\n", win ? win->id : 0);
1924
1925         if (win == NULL)
1926                 return;
1927         if (win->ws == NULL)
1928                 return;
1929
1930         if (validate_ws(win->ws))
1931                 return; /* XXX this gets hit with thunderbird, needs fixing */
1932
1933         if (validate_win(win)) {
1934                 kill_refs(win);
1935                 return;
1936         }
1937
1938         if (validate_win(win)) {
1939                 kill_refs(win);
1940                 return;
1941         }
1942
1943         XGetInputFocus(display, &cur_focus, &rr);
1944         if ((cfw = find_window(cur_focus)) != NULL)
1945                 unfocus_win(cfw);
1946         else {
1947                 /* use larger hammer since the window was killed somehow */
1948                 TAILQ_FOREACH(cfw, &win->ws->winlist, entry)
1949                         if (cfw->ws && cfw->ws->r && cfw->ws->r->s)
1950                                 XSetWindowBorder(display, cfw->id,
1951                                     cfw->ws->r->s->c[SWM_S_COLOR_UNFOCUS].color);
1952         }
1953
1954         win->ws->focus = win;
1955
1956         if (win->ws->r != NULL) {
1957                 /* drain all previous focus events */
1958                 while (XCheckTypedEvent(display, FocusIn, &cne) == True)
1959                         ;
1960
1961                 if (win->java == 0)
1962                         XSetInputFocus(display, win->id,
1963                             RevertToParent, CurrentTime);
1964                 grabbuttons(win, 1);
1965                 XSetWindowBorder(display, win->id,
1966                     win->ws->r->s->c[SWM_S_COLOR_FOCUS].color);
1967                 if (win->ws->cur_layout->flags & SWM_L_MAPONFOCUS ||
1968                     win->ws->always_raise)
1969                         XMapRaised(display, win->id);
1970
1971                 XChangeProperty(display, win->s->root,
1972                     ewmh[_NET_ACTIVE_WINDOW].atom, XA_WINDOW, 32,
1973                     PropModeReplace, (unsigned char *)&win->id,1);
1974         }
1975
1976         if (window_name_enabled)
1977                 bar_update();
1978 }
1979
1980 void
1981 switchws(struct swm_region *r, union arg *args)
1982 {
1983         int                     wsid = args->id, unmap_old = 0;
1984         struct swm_region       *this_r, *other_r;
1985         struct ws_win           *win;
1986         struct workspace        *new_ws, *old_ws;
1987         union arg               a;
1988
1989         if (!(r && r->s))
1990                 return;
1991
1992         this_r = r;
1993         old_ws = this_r->ws;
1994         new_ws = &this_r->s->ws[wsid];
1995
1996         DNPRINTF(SWM_D_WS, "switchws screen[%d]:%dx%d+%d+%d: "
1997             "%d -> %d\n", r->s->idx, WIDTH(r), HEIGHT(r), X(r), Y(r),
1998             old_ws->idx, wsid);
1999
2000         if (new_ws == NULL || old_ws == NULL)
2001                 return;
2002         if (new_ws == old_ws)
2003                 return;
2004
2005         other_r = new_ws->r;
2006         if (other_r == NULL) {
2007                 /* the other workspace is hidden, hide this one */
2008                 old_ws->r = NULL;
2009                 unmap_old = 1;
2010         } else {
2011                 /* the other ws is visible in another region, exchange them */
2012                 other_r->ws_prior = new_ws;
2013                 other_r->ws = old_ws;
2014                 old_ws->r = other_r;
2015         }
2016         this_r->ws_prior = old_ws;
2017         this_r->ws = new_ws;
2018         new_ws->r = this_r;
2019
2020         /* this is needed so that we can click on a window after a restart */
2021         unfocus_all();
2022
2023         stack();
2024         a.id = SWM_ARG_ID_FOCUSCUR;
2025         focus(new_ws->r, &a);
2026
2027         bar_update();
2028
2029         /* unmap old windows */
2030         if (unmap_old)
2031                 TAILQ_FOREACH(win, &old_ws->winlist, entry)
2032                         unmap_window(win);
2033
2034         if (focus_mode == SWM_FOCUS_DEFAULT)
2035                 drain_enter_notify();
2036 }
2037
2038 void
2039 cyclews(struct swm_region *r, union arg *args)
2040 {
2041         union                   arg a;
2042         struct swm_screen       *s = r->s;
2043
2044         DNPRINTF(SWM_D_WS, "cyclews id %d "
2045             "in screen[%d]:%dx%d+%d+%d ws %d\n", args->id,
2046             r->s->idx, WIDTH(r), HEIGHT(r), X(r), Y(r), r->ws->idx);
2047
2048         a.id = r->ws->idx;
2049         do {
2050                 switch (args->id) {
2051                 case SWM_ARG_ID_CYCLEWS_UP:
2052                         if (a.id < SWM_WS_MAX - 1)
2053                                 a.id++;
2054                         else
2055                                 a.id = 0;
2056                         break;
2057                 case SWM_ARG_ID_CYCLEWS_DOWN:
2058                         if (a.id > 0)
2059                                 a.id--;
2060                         else
2061                                 a.id = SWM_WS_MAX - 1;
2062                         break;
2063                 default:
2064                         return;
2065                 };
2066
2067                 if (cycle_empty == 0 && TAILQ_EMPTY(&s->ws[a.id].winlist))
2068                         continue;
2069                 if (cycle_visible == 0 && s->ws[a.id].r != NULL)
2070                         continue;
2071
2072                 switchws(r, &a);
2073         } while (a.id != r->ws->idx);
2074 }
2075
2076 void
2077 priorws(struct swm_region *r, union arg *args)
2078 {
2079         union arg               a;
2080
2081         DNPRINTF(SWM_D_WS, "priorws id %d "
2082             "in screen[%d]:%dx%d+%d+%d ws %d\n", args->id,
2083             r->s->idx, WIDTH(r), HEIGHT(r), X(r), Y(r), r->ws->idx);
2084
2085         if (r->ws_prior == NULL)
2086                 return;
2087
2088         a.id = r->ws_prior->idx;
2089         switchws(r, &a);
2090 }
2091
2092 void
2093 cyclescr(struct swm_region *r, union arg *args)
2094 {
2095         struct swm_region       *rr = NULL;
2096         union arg               a;
2097         int                     i, x, y;
2098
2099         /* do nothing if we don't have more than one screen */
2100         if (!(ScreenCount(display) > 1 || outputs > 1))
2101                 return;
2102
2103         i = r->s->idx;
2104         switch (args->id) {
2105         case SWM_ARG_ID_CYCLESC_UP:
2106                 rr = TAILQ_NEXT(r, entry);
2107                 if (rr == NULL)
2108                         rr = TAILQ_FIRST(&screens[i].rl);
2109                 break;
2110         case SWM_ARG_ID_CYCLESC_DOWN:
2111                 rr = TAILQ_PREV(r, swm_region_list, entry);
2112                 if (rr == NULL)
2113                         rr = TAILQ_LAST(&screens[i].rl, swm_region_list);
2114                 break;
2115         default:
2116                 return;
2117         };
2118         if (rr == NULL)
2119                 return;
2120
2121         /* move mouse to region */
2122         x = rr->g.x + 1;
2123         y = rr->g.y + 1 + (bar_enabled ? bar_height : 0);
2124         XWarpPointer(display, None, rr->s[i].root, 0, 0, 0, 0, x, y);
2125
2126         a.id = SWM_ARG_ID_FOCUSCUR;
2127         focus(rr, &a);
2128
2129         if (rr->ws->focus) {
2130                 /* move to focus window */
2131                 x = rr->ws->focus->g.x + 1;
2132                 y = rr->ws->focus->g.y + 1;
2133                 XWarpPointer(display, None, rr->s[i].root, 0, 0, 0, 0, x, y);
2134         }
2135 }
2136
2137 void
2138 sort_windows(struct ws_win_list *wl)
2139 {
2140         struct ws_win           *win, *parent, *nxt;
2141
2142         if (wl == NULL)
2143                 return;
2144
2145         for (win = TAILQ_FIRST(wl); win != TAILQ_END(wl); win = nxt) {
2146                 nxt = TAILQ_NEXT(win, entry);
2147                 if (win->transient) {
2148                         parent = find_window(win->transient);
2149                         if (parent == NULL) {
2150                                 fprintf(stderr, "not possible bug\n");
2151                                 continue;
2152                         }
2153                         TAILQ_REMOVE(wl, win, entry);
2154                         TAILQ_INSERT_AFTER(wl, parent, win, entry);
2155                 }
2156         }
2157
2158 }
2159
2160 void
2161 swapwin(struct swm_region *r, union arg *args)
2162 {
2163         struct ws_win           *target, *source;
2164         struct ws_win           *cur_focus;
2165         struct ws_win_list      *wl;
2166
2167
2168         DNPRINTF(SWM_D_WS, "swapwin id %d "
2169             "in screen %d region %dx%d+%d+%d ws %d\n", args->id,
2170             r->s->idx, WIDTH(r), HEIGHT(r), X(r), Y(r), r->ws->idx);
2171
2172         cur_focus = r->ws->focus;
2173         if (cur_focus == NULL)
2174                 return;
2175
2176         source = cur_focus;
2177         wl = &source->ws->winlist;
2178
2179         switch (args->id) {
2180         case SWM_ARG_ID_SWAPPREV:
2181                 if (source->transient)
2182                         source = find_window(source->transient);
2183                 target = TAILQ_PREV(source, ws_win_list, entry);
2184                 if (target && target->transient)
2185                         target = find_window(target->transient);
2186                 TAILQ_REMOVE(wl, source, entry);
2187                 if (target == NULL)
2188                         TAILQ_INSERT_TAIL(wl, source, entry);
2189                 else
2190                         TAILQ_INSERT_BEFORE(target, source, entry);
2191                 break;
2192         case SWM_ARG_ID_SWAPNEXT:
2193                 target = TAILQ_NEXT(source, entry);
2194                 /* move the parent and let the sort handle the move */
2195                 if (source->transient)
2196                         source = find_window(source->transient);
2197                 TAILQ_REMOVE(wl, source, entry);
2198                 if (target == NULL)
2199                         TAILQ_INSERT_HEAD(wl, source, entry);
2200                 else
2201                         TAILQ_INSERT_AFTER(wl, target, source, entry);
2202                 break;
2203         case SWM_ARG_ID_SWAPMAIN:
2204                 target = TAILQ_FIRST(wl);
2205                 if (target == source) {
2206                         if (source->ws->focus_prev != NULL &&
2207                             source->ws->focus_prev != target)
2208
2209                                 source = source->ws->focus_prev;
2210                         else
2211                                 return;
2212                 }
2213                 if (target == NULL || source == NULL)
2214                         return;
2215                 source->ws->focus_prev = target;
2216                 TAILQ_REMOVE(wl, target, entry);
2217                 TAILQ_INSERT_BEFORE(source, target, entry);
2218                 TAILQ_REMOVE(wl, source, entry);
2219                 TAILQ_INSERT_HEAD(wl, source, entry);
2220                 break;
2221         case SWM_ARG_ID_MOVELAST:
2222                 TAILQ_REMOVE(wl, source, entry);
2223                 TAILQ_INSERT_TAIL(wl, source, entry);
2224                 break;
2225         default:
2226                 DNPRINTF(SWM_D_MOVE, "invalid id: %d\n", args->id);
2227                 return;
2228         }
2229
2230         sort_windows(wl);
2231
2232         stack();
2233 }
2234
2235 void
2236 focus_prev(struct ws_win *win)
2237 {
2238         struct ws_win           *winfocus = NULL, *winlostfocus = NULL;
2239         struct ws_win           *cur_focus = NULL;
2240         struct ws_win_list      *wl = NULL;
2241         struct workspace        *ws = NULL;
2242
2243         DNPRINTF(SWM_D_FOCUS, "focus_prev: id %lu\n", WINID(win));
2244
2245         if (!(win && win->ws))
2246                 return;
2247
2248         ws = win->ws;
2249         wl = &ws->winlist;
2250         cur_focus = ws->focus;
2251         winlostfocus = cur_focus;
2252
2253         /* pickle, just focus on whatever */
2254         if (cur_focus == NULL) {
2255                 /* use prev_focus if valid */
2256                 if (ws->focus_prev && ws->focus_prev != cur_focus &&
2257                     find_window(WINID(ws->focus_prev)))
2258                         winfocus = ws->focus_prev;
2259                 if (winfocus == NULL)
2260                         winfocus = TAILQ_FIRST(wl);
2261                 goto done;
2262         }
2263
2264         /* if transient focus on parent */
2265         if (cur_focus->transient) {
2266                 winfocus = find_window(cur_focus->transient);
2267                 goto done;
2268         }
2269
2270         /* if in max_stack try harder */
2271         if ((win->quirks & SWM_Q_FOCUSPREV) ||
2272             (ws->cur_layout->flags & SWM_L_FOCUSPREV)) {
2273                 if (cur_focus != ws->focus_prev)
2274                         winfocus = ws->focus_prev;
2275                 else if (cur_focus != ws->focus)
2276                         winfocus = ws->focus;
2277                 else
2278                         winfocus = TAILQ_PREV(win, ws_win_list, entry);
2279                 if (winfocus)
2280                         goto done;
2281         }
2282
2283         if (cur_focus == win)
2284                 winfocus = TAILQ_PREV(win, ws_win_list, entry);
2285         if (winfocus == NULL)
2286                 winfocus = TAILQ_LAST(wl, ws_win_list);
2287         if (winfocus == NULL || winfocus == win)
2288                 winfocus = TAILQ_NEXT(cur_focus, entry);
2289 done:
2290         if (winfocus == winlostfocus || winfocus == NULL)
2291                 return;
2292
2293         focus_magic(winfocus);
2294 }
2295
2296 void
2297 focus(struct swm_region *r, union arg *args)
2298 {
2299         struct ws_win           *winfocus = NULL, *winlostfocus = NULL, *head;
2300         struct ws_win           *cur_focus = NULL;
2301         struct ws_win_list      *wl = NULL;
2302         struct workspace        *ws = NULL;
2303
2304         if (!(r && r->ws))
2305                 return;
2306
2307         DNPRINTF(SWM_D_FOCUS, "focus: id %d\n", args->id);
2308
2309         /* treat FOCUS_CUR special */
2310         if (args->id == SWM_ARG_ID_FOCUSCUR) {
2311                 if (r->ws->focus && r->ws->focus->iconic == 0)
2312                         winfocus = r->ws->focus;
2313                 else if (r->ws->focus_prev && r->ws->focus_prev->iconic == 0)
2314                         winfocus = r->ws->focus_prev;
2315                 else
2316                         TAILQ_FOREACH(winfocus, &r->ws->winlist, entry)
2317                                 if (winfocus->iconic == 0)
2318                                         break;
2319
2320                 focus_magic(winfocus);
2321                 return;
2322         }
2323
2324         if ((cur_focus = r->ws->focus) == NULL)
2325                 return;
2326         ws = r->ws;
2327         wl = &ws->winlist;
2328
2329         winlostfocus = cur_focus;
2330
2331         switch (args->id) {
2332         case SWM_ARG_ID_FOCUSPREV:
2333                 head = TAILQ_PREV(cur_focus, ws_win_list, entry);
2334                 if (head == NULL)
2335                         head = TAILQ_LAST(wl, ws_win_list);
2336                 winfocus = head;
2337                 if (WINID(winfocus) == cur_focus->transient) {
2338                         head = TAILQ_PREV(winfocus, ws_win_list, entry);
2339                         if (head == NULL)
2340                                 head = TAILQ_LAST(wl, ws_win_list);
2341                         winfocus = head;
2342                 }
2343
2344                 /* skip iconics */
2345                 if (winfocus && winfocus->iconic) {
2346                         TAILQ_FOREACH_REVERSE(winfocus, wl, ws_win_list, entry)
2347                                 if (winfocus->iconic == 0)
2348                                         break;
2349                 }
2350                 break;
2351
2352         case SWM_ARG_ID_FOCUSNEXT:
2353                 head = TAILQ_NEXT(cur_focus, entry);
2354                 if (head == NULL)
2355                         head = TAILQ_FIRST(wl);
2356                 winfocus = head;
2357
2358                 /* skip iconics */
2359                 if (winfocus && winfocus->iconic) {
2360                         TAILQ_FOREACH(winfocus, wl, entry)
2361                                 if (winfocus->iconic == 0)
2362                                         break;
2363                 }
2364                 break;
2365
2366         case SWM_ARG_ID_FOCUSMAIN:
2367                 winfocus = TAILQ_FIRST(wl);
2368                 if (winfocus == cur_focus)
2369                         winfocus = cur_focus->ws->focus_prev;
2370                 break;
2371
2372         default:
2373                 return;
2374         }
2375         if (winfocus == winlostfocus || winfocus == NULL)
2376                 return;
2377
2378         focus_magic(winfocus);
2379 }
2380
2381 void
2382 cycle_layout(struct swm_region *r, union arg *args)
2383 {
2384         struct workspace        *ws = r->ws;
2385         struct ws_win           *winfocus;
2386         union arg               a;
2387
2388         DNPRINTF(SWM_D_EVENT, "cycle_layout: workspace: %d\n", ws->idx);
2389
2390         winfocus = ws->focus;
2391
2392         ws->cur_layout++;
2393         if (ws->cur_layout->l_stack == NULL)
2394                 ws->cur_layout = &layouts[0];
2395
2396         stack();
2397         if (focus_mode == SWM_FOCUS_DEFAULT)
2398                 drain_enter_notify();
2399         a.id = SWM_ARG_ID_FOCUSCUR;
2400         focus(r, &a);
2401         bar_update();
2402 }
2403
2404 void
2405 stack_config(struct swm_region *r, union arg *args)
2406 {
2407         struct workspace        *ws = r->ws;
2408
2409         DNPRINTF(SWM_D_STACK, "stack_config for workspace %d (id %d\n",
2410             args->id, ws->idx);
2411
2412         if (ws->cur_layout->l_config != NULL)
2413                 ws->cur_layout->l_config(ws, args->id);
2414
2415         if (args->id != SWM_ARG_ID_STACKINIT)
2416                 stack();
2417 }
2418
2419 void
2420 stack(void) {
2421         struct swm_geometry     g;
2422         struct swm_region       *r;
2423         int                     i, j;
2424
2425         DNPRINTF(SWM_D_STACK, "stack\n");
2426
2427         for (i = 0; i < ScreenCount(display); i++) {
2428                 j = 0;
2429                 TAILQ_FOREACH(r, &screens[i].rl, entry) {
2430                         DNPRINTF(SWM_D_STACK, "stacking workspace %d "
2431                             "(screen %d, region %d)\n", r->ws->idx, i, j++);
2432
2433                         /* start with screen geometry, adjust for bar */
2434                         g = r->g;
2435                         g.w -= 2 * border_width;
2436                         g.h -= 2 * border_width;
2437                         if (bar_enabled) {
2438                                 if (!bar_at_bottom)
2439                                         g.y += bar_height;
2440                                 g.h -= bar_height;
2441                         }
2442                         r->ws->cur_layout->l_stack(r->ws, &g);
2443                         /* save r so we can track region changes */
2444                         r->ws->old_r = r;
2445                 }
2446         }
2447         if (font_adjusted)
2448                 font_adjusted--;
2449
2450         if (focus_mode == SWM_FOCUS_DEFAULT)
2451                 drain_enter_notify();
2452 }
2453
2454 void
2455 store_float_geom(struct ws_win *win, struct swm_region *r)
2456 {
2457         /* retain window geom and region geom */
2458         win->g_float.x = win->g.x;
2459         win->g_float.y = win->g.y;
2460         win->g_float.w = win->g.w;
2461         win->g_float.h = win->g.h;
2462         win->rg_float.x = r->g.x;
2463         win->rg_float.y = r->g.y;
2464         win->rg_float.w = r->g.w;
2465         win->rg_float.h = r->g.h;
2466         win->g_floatvalid = 1;
2467 }
2468
2469 void
2470 stack_floater(struct ws_win *win, struct swm_region *r)
2471 {
2472         unsigned int            mask;
2473         XWindowChanges          wc;
2474
2475         if (win == NULL)
2476                 return;
2477
2478         bzero(&wc, sizeof wc);
2479         mask = CWX | CWY | CWBorderWidth | CWWidth | CWHeight;
2480
2481         /*
2482          * to allow windows to change their size (e.g. mplayer fs) only retrieve
2483          * geom on ws switches or return from max mode
2484          */
2485
2486         if (win->floatmaxed || (r != r->ws->old_r && win->g_floatvalid
2487             && !(win->ewmh_flags & EWMH_F_FULLSCREEN))) {
2488                 /*
2489                  * use stored g and rg to set relative position and size
2490                  * as in old region or before max stack mode
2491                  */
2492                 win->g.x = win->g_float.x - win->rg_float.x + r->g.x;
2493                 win->g.y = win->g_float.y - win->rg_float.y + r->g.y;
2494                 win->g.w = win->g_float.w;
2495                 win->g.h = win->g_float.h;
2496                 win->g_floatvalid = 0;
2497         }
2498
2499         win->floatmaxed = 0;
2500
2501         if ((win->quirks & SWM_Q_FULLSCREEN) && (win->g.w >= WIDTH(r)) &&
2502             (win->g.h >= HEIGHT(r)))
2503                 wc.border_width = 0;
2504         else
2505                 wc.border_width = border_width;
2506         if (win->transient && (win->quirks & SWM_Q_TRANSSZ)) {
2507                 win->g.w = (double)WIDTH(r) * dialog_ratio;
2508                 win->g.h = (double)HEIGHT(r) * dialog_ratio;
2509         }
2510
2511         if (!win->manual) {
2512                 /*
2513                  * floaters and transients are auto-centred unless moved
2514                  * or resized
2515                  */
2516                 win->g.x = r->g.x + (WIDTH(r) - win->g.w) /
2517                     2 - wc.border_width;
2518                 win->g.y = r->g.y + (HEIGHT(r) - win->g.h) /
2519                     2 - wc.border_width;
2520         }
2521
2522         /* win can be outside r if new r smaller than old r */
2523         /* Ensure top left corner inside r (move probs otherwise) */
2524         if (win->g.x < r->g.x - wc.border_width)
2525                 win->g.x = r->g.x - wc.border_width;
2526         if (win->g.x > r->g.x + r->g.w - 1)
2527                 win->g.x = (win->g.w > r->g.w) ? r->g.x :
2528                     (r->g.x + r->g.w - win->g.w - 2 * wc.border_width);
2529         if (win->g.y < r->g.y - wc.border_width)
2530                 win->g.y = r->g.y - wc.border_width;
2531         if (win->g.y > r->g.y + r->g.h - 1)
2532                 win->g.y = (win->g.h > r->g.h) ? r->g.y :
2533                     (r->g.y + r->g.h - win->g.h - 2 * wc.border_width);
2534
2535         wc.x = win->g.x;
2536         wc.y = win->g.y;
2537         wc.width = win->g.w;
2538         wc.height = win->g.h;
2539
2540         /*
2541          * Retain floater and transient geometry for correct positioning
2542          * when ws changes region
2543          */
2544         if (!(win->ewmh_flags & EWMH_F_FULLSCREEN))
2545                 store_float_geom(win, r);
2546
2547         DNPRINTF(SWM_D_MISC, "stack_floater: win %lu x %d y %d w %d h %d\n",
2548             win->id, wc.x, wc.y, wc.width, wc.height);
2549
2550         XConfigureWindow(display, win->id, mask, &wc);
2551 }
2552
2553 /*
2554  * Send keystrokes to terminal to decrease/increase the font size as the
2555  * window size changes.
2556  */
2557 void
2558 adjust_font(struct ws_win *win)
2559 {
2560         if (!(win->quirks & SWM_Q_XTERM_FONTADJ) ||
2561             win->floating || win->transient)
2562                 return;
2563
2564         if (win->sh.width_inc && win->last_inc != win->sh.width_inc &&
2565             win->g.w / win->sh.width_inc < term_width &&
2566             win->font_steps < SWM_MAX_FONT_STEPS) {
2567                 win->font_size_boundary[win->font_steps] =
2568                     (win->sh.width_inc * term_width) + win->sh.base_width;
2569                 win->font_steps++;
2570                 font_adjusted++;
2571                 win->last_inc = win->sh.width_inc;
2572                 fake_keypress(win, XK_KP_Subtract, ShiftMask);
2573         } else if (win->font_steps && win->last_inc != win->sh.width_inc &&
2574             win->g.w > win->font_size_boundary[win->font_steps - 1]) {
2575                 win->font_steps--;
2576                 font_adjusted++;
2577                 win->last_inc = win->sh.width_inc;
2578                 fake_keypress(win, XK_KP_Add, ShiftMask);
2579         }
2580 }
2581
2582 #define SWAPXY(g)       do {                            \
2583         int tmp;                                        \
2584         tmp = (g)->y; (g)->y = (g)->x; (g)->x = tmp;    \
2585         tmp = (g)->h; (g)->h = (g)->w; (g)->w = tmp;    \
2586 } while (0)
2587 void
2588 stack_master(struct workspace *ws, struct swm_geometry *g, int rot, int flip)
2589 {
2590         XWindowChanges          wc;
2591         XWindowAttributes       wa;
2592         struct swm_geometry     win_g, r_g = *g;
2593         struct ws_win           *win, *fs_win = 0;
2594         int                     i, j, s, stacks;
2595         int                     w_inc = 1, h_inc, w_base = 1, h_base;
2596         int                     hrh, extra = 0, h_slice, last_h = 0;
2597         int                     split, colno, winno, mwin, msize, mscale;
2598         int                     remain, missing, v_slice, reconfigure;
2599         unsigned int            mask;
2600
2601         DNPRINTF(SWM_D_STACK, "stack_master: workspace: %d\n rot=%s flip=%s",
2602             ws->idx, rot ? "yes" : "no", flip ? "yes" : "no");
2603
2604         winno = count_win(ws, 0);
2605         if (winno == 0 && count_win(ws, 1) == 0)
2606                 return;
2607
2608         TAILQ_FOREACH(win, &ws->winlist, entry)
2609                 if (win->transient == 0 && win->floating == 0
2610                     && win->iconic == 0)
2611                         break;
2612
2613         if (win == NULL)
2614                 goto notiles;
2615
2616         if (rot) {
2617                 w_inc = win->sh.width_inc;
2618                 w_base = win->sh.base_width;
2619                 mwin = ws->l_state.horizontal_mwin;
2620                 mscale = ws->l_state.horizontal_msize;
2621                 stacks = ws->l_state.horizontal_stacks;
2622                 SWAPXY(&r_g);
2623         } else {
2624                 w_inc = win->sh.height_inc;
2625                 w_base = win->sh.base_height;
2626                 mwin = ws->l_state.vertical_mwin;
2627                 mscale = ws->l_state.vertical_msize;
2628                 stacks = ws->l_state.vertical_stacks;
2629         }
2630         win_g = r_g;
2631
2632         if (stacks > winno - mwin)
2633                 stacks = winno - mwin;
2634         if (stacks < 1)
2635                 stacks = 1;
2636
2637         h_slice = r_g.h / SWM_H_SLICE;
2638         if (mwin && winno > mwin) {
2639                 v_slice = r_g.w / SWM_V_SLICE;
2640
2641                 split = mwin;
2642                 colno = split;
2643                 win_g.w = v_slice * mscale;
2644
2645                 if (w_inc > 1 && w_inc < v_slice) {
2646                         /* adjust for window's requested size increment */
2647                         remain = (win_g.w - w_base) % w_inc;
2648                         missing = w_inc - remain;
2649                         win_g.w -= remain;
2650                         extra += remain;
2651                 }
2652
2653                 msize = win_g.w;
2654                 if (flip)
2655                         win_g.x += r_g.w - msize;
2656         } else {
2657                 msize = -2;
2658                 colno = split = winno / stacks;
2659                 win_g.w = ((r_g.w - (stacks * 2 * border_width) +
2660                     2 * border_width) / stacks);
2661         }
2662         hrh = r_g.h / colno;
2663         extra = r_g.h - (colno * hrh);
2664         win_g.h = hrh - 2 * border_width;
2665
2666         /*  stack all the tiled windows */
2667         i = j = 0, s = stacks;
2668         TAILQ_FOREACH(win, &ws->winlist, entry) {
2669                 if (win->transient != 0 || win->floating != 0)
2670                         continue;
2671                 if (win->iconic != 0)
2672                         continue;
2673
2674                 if (win->ewmh_flags & EWMH_F_FULLSCREEN) {
2675                         fs_win = win;
2676                         continue;
2677                 }
2678
2679                 if (split && i == split) {
2680                         colno = (winno - mwin) / stacks;
2681                         if (s <= (winno - mwin) % stacks)
2682                                 colno++;
2683                         split = split + colno;
2684                         hrh = (r_g.h / colno);
2685                         extra = r_g.h - (colno * hrh);
2686                         if (flip)
2687                                 win_g.x = r_g.x;
2688                         else
2689                                 win_g.x += win_g.w + 2 * border_width;
2690                         win_g.w = (r_g.w - msize -
2691                             (stacks * 2 * border_width)) / stacks;
2692                         if (s == 1)
2693                                 win_g.w += (r_g.w - msize -
2694                                     (stacks * 2 * border_width)) % stacks;
2695                         s--;
2696                         j = 0;
2697                 }
2698                 win_g.h = hrh - 2 * border_width;
2699                 if (rot) {
2700                         h_inc = win->sh.width_inc;
2701                         h_base = win->sh.base_width;
2702                 } else {
2703                         h_inc = win->sh.height_inc;
2704                         h_base = win->sh.base_height;
2705                 }
2706                 if (j == colno - 1) {
2707                         win_g.h = hrh + extra;
2708                 } else if (h_inc > 1 && h_inc < h_slice) {
2709                         /* adjust for window's requested size increment */
2710                         remain = (win_g.h - h_base) % h_inc;
2711                         missing = h_inc - remain;
2712
2713                         if (missing <= extra || j == 0) {
2714                                 extra -= missing;
2715                                 win_g.h += missing;
2716                         } else {
2717                                 win_g.h -= remain;
2718                                 extra += remain;
2719                         }
2720                 }
2721
2722                 if (j == 0)
2723                         win_g.y = r_g.y;
2724                 else
2725                         win_g.y += last_h + 2 * border_width;
2726
2727                 bzero(&wc, sizeof wc);
2728                 if (disable_border && bar_enabled == 0 && winno == 1){
2729                         wc.border_width = 0;
2730                         win_g.w += 2 * border_width;
2731                         win_g.h += 2 * border_width;
2732                 } else
2733                         wc.border_width = border_width;
2734                 reconfigure = 0;
2735                 if (rot) {
2736                         if (win->g.x != win_g.y || win->g.y != win_g.x ||
2737                             win->g.w != win_g.h || win->g.h != win_g.w) {
2738                                 reconfigure = 1;
2739                                 win->g.x = wc.x = win_g.y;
2740                                 win->g.y = wc.y = win_g.x;
2741                                 win->g.w = wc.width = win_g.h;
2742                                 win->g.h = wc.height = win_g.w;
2743                         }
2744                 } else {
2745                         if (win->g.x != win_g.x || win->g.y != win_g.y ||
2746                             win->g.w != win_g.w || win->g.h != win_g.h) {
2747                                 reconfigure = 1;
2748                                 win->g.x = wc.x = win_g.x;
2749                                 win->g.y = wc.y = win_g.y;
2750                                 win->g.w = wc.width = win_g.w;
2751                                 win->g.h = wc.height = win_g.h;
2752                         }
2753                 }
2754                 if (reconfigure) {
2755                         adjust_font(win);
2756                         mask = CWX | CWY | CWWidth | CWHeight | CWBorderWidth;
2757                         XConfigureWindow(display, win->id, mask, &wc);
2758                 }
2759
2760                 if (XGetWindowAttributes(display, win->id, &wa))
2761                         if (wa.map_state == IsUnmapped)
2762                                 XMapRaised(display, win->id);
2763
2764                 last_h = win_g.h;
2765                 i++;
2766                 j++;
2767         }
2768
2769 notiles:
2770         /* now, stack all the floaters and transients */
2771         TAILQ_FOREACH(win, &ws->winlist, entry) {
2772                 if (win->transient == 0 && win->floating == 0)
2773                         continue;
2774                 if (win->iconic == 1)
2775                         continue;
2776                 if (win->ewmh_flags & EWMH_F_FULLSCREEN) {
2777                         fs_win = win;
2778                         continue;
2779                 }
2780
2781                 stack_floater(win, ws->r);
2782                 XMapRaised(display, win->id);
2783         }
2784
2785         if (fs_win) {
2786                 stack_floater(fs_win, ws->r);
2787                 XMapRaised(display, fs_win->id);
2788         }
2789 }
2790
2791 void
2792 vertical_config(struct workspace *ws, int id)
2793 {
2794         DNPRINTF(SWM_D_STACK, "vertical_resize: workspace: %d\n", ws->idx);
2795
2796         switch (id) {
2797         case SWM_ARG_ID_STACKRESET:
2798         case SWM_ARG_ID_STACKINIT:
2799                 ws->l_state.vertical_msize = SWM_V_SLICE / 2;
2800                 ws->l_state.vertical_mwin = 1;
2801                 ws->l_state.vertical_stacks = 1;
2802                 break;
2803         case SWM_ARG_ID_MASTERSHRINK:
2804                 if (ws->l_state.vertical_msize > 1)
2805                         ws->l_state.vertical_msize--;
2806                 break;
2807         case SWM_ARG_ID_MASTERGROW:
2808                 if (ws->l_state.vertical_msize < SWM_V_SLICE - 1)
2809                         ws->l_state.vertical_msize++;
2810                 break;
2811         case SWM_ARG_ID_MASTERADD:
2812                 ws->l_state.vertical_mwin++;
2813                 break;
2814         case SWM_ARG_ID_MASTERDEL:
2815                 if (ws->l_state.vertical_mwin > 0)
2816                         ws->l_state.vertical_mwin--;
2817                 break;
2818         case SWM_ARG_ID_STACKINC:
2819                 ws->l_state.vertical_stacks++;
2820                 break;
2821         case SWM_ARG_ID_STACKDEC:
2822                 if (ws->l_state.vertical_stacks > 1)
2823                         ws->l_state.vertical_stacks--;
2824                 break;
2825         default:
2826                 return;
2827         }
2828 }
2829
2830 void
2831 vertical_stack(struct workspace *ws, struct swm_geometry *g)
2832 {
2833         DNPRINTF(SWM_D_STACK, "vertical_stack: workspace: %d\n", ws->idx);
2834
2835         stack_master(ws, g, 0, 0);
2836 }
2837
2838 void
2839 horizontal_config(struct workspace *ws, int id)
2840 {
2841         DNPRINTF(SWM_D_STACK, "horizontal_config: workspace: %d\n", ws->idx);
2842
2843         switch (id) {
2844         case SWM_ARG_ID_STACKRESET:
2845         case SWM_ARG_ID_STACKINIT:
2846                 ws->l_state.horizontal_mwin = 1;
2847                 ws->l_state.horizontal_msize = SWM_H_SLICE / 2;
2848                 ws->l_state.horizontal_stacks = 1;
2849                 break;
2850         case SWM_ARG_ID_MASTERSHRINK:
2851                 if (ws->l_state.horizontal_msize > 1)
2852                         ws->l_state.horizontal_msize--;
2853                 break;
2854         case SWM_ARG_ID_MASTERGROW:
2855                 if (ws->l_state.horizontal_msize < SWM_H_SLICE - 1)
2856                         ws->l_state.horizontal_msize++;
2857                 break;
2858         case SWM_ARG_ID_MASTERADD:
2859                 ws->l_state.horizontal_mwin++;
2860                 break;
2861         case SWM_ARG_ID_MASTERDEL:
2862                 if (ws->l_state.horizontal_mwin > 0)
2863                         ws->l_state.horizontal_mwin--;
2864                 break;
2865         case SWM_ARG_ID_STACKINC:
2866                 ws->l_state.horizontal_stacks++;
2867                 break;
2868         case SWM_ARG_ID_STACKDEC:
2869                 if (ws->l_state.horizontal_stacks > 1)
2870                         ws->l_state.horizontal_stacks--;
2871                 break;
2872         default:
2873                 return;
2874         }
2875 }
2876
2877 void
2878 horizontal_stack(struct workspace *ws, struct swm_geometry *g)
2879 {
2880         DNPRINTF(SWM_D_STACK, "horizontal_stack: workspace: %d\n", ws->idx);
2881
2882         stack_master(ws, g, 1, 0);
2883 }
2884
2885 /* fullscreen view */
2886 void
2887 max_stack(struct workspace *ws, struct swm_geometry *g)
2888 {
2889         XWindowChanges          wc;
2890         struct swm_geometry     gg = *g;
2891         struct ws_win           *win, *wintrans = NULL, *parent = NULL;
2892         unsigned int            mask;
2893         int                     winno;
2894
2895         DNPRINTF(SWM_D_STACK, "max_stack: workspace: %d\n", ws->idx);
2896
2897         if (ws == NULL)
2898                 return;
2899
2900         winno = count_win(ws, 0);
2901         if (winno == 0 && count_win(ws, 1) == 0)
2902                 return;
2903
2904         TAILQ_FOREACH(win, &ws->winlist, entry) {
2905                 if (win->transient) {
2906                         wintrans = win;
2907                         parent = find_window(win->transient);
2908                         continue;
2909                 }
2910
2911                 if (win->floating && win->floatmaxed == 0 ) {
2912                         /*
2913                          * retain geometry for retrieval on exit from
2914                          * max_stack mode
2915                          */
2916                         store_float_geom(win, ws->r);
2917                         win->floatmaxed = 1;
2918                 }
2919
2920                 /* only reconfigure if necessary */
2921                 if (win->g.x != gg.x || win->g.y != gg.y || win->g.w != gg.w ||
2922                     win->g.h != gg.h) {
2923                         bzero(&wc, sizeof wc);
2924                         win->g.x = wc.x = gg.x;
2925                         win->g.y = wc.y = gg.y;
2926                         if (bar_enabled){
2927                                 wc.border_width = border_width;
2928                                 win->g.w = wc.width = gg.w;
2929                                 win->g.h = wc.height = gg.h;
2930                         } else {
2931                                 wc.border_width = 0;
2932                                 win->g.w = wc.width = gg.w + 2 * border_width;
2933                                 win->g.h = wc.height = gg.h + 2 * border_width;
2934                         }
2935                         mask = CWX | CWY | CWWidth | CWHeight | CWBorderWidth;
2936                         XConfigureWindow(display, win->id, mask, &wc);
2937                 }
2938                 /* unmap only if we don't have multi screen */
2939                 if (win != ws->focus)
2940                         if (!(ScreenCount(display) > 1 || outputs > 1))
2941                                 unmap_window(win);
2942         }
2943
2944         /* put the last transient on top */
2945         if (wintrans) {
2946                 if (parent)
2947                         XMapRaised(display, parent->id);
2948                 stack_floater(wintrans, ws->r);
2949                 focus_magic(wintrans);
2950         }
2951 }
2952
2953 void
2954 send_to_ws(struct swm_region *r, union arg *args)
2955 {
2956         int                     wsid = args->id;
2957         struct ws_win           *win = NULL, *parent;
2958         struct workspace        *ws, *nws;
2959         Atom                    ws_idx_atom = 0;
2960         unsigned char           ws_idx_str[SWM_PROPLEN];
2961         union arg               a;
2962
2963         if (r && r->ws)
2964                 win = r->ws->focus;
2965         else
2966                 return;
2967         if (win == NULL)
2968                 return;
2969         if (win->ws->idx == wsid)
2970                 return;
2971
2972         DNPRINTF(SWM_D_MOVE, "send_to_ws: win: %lu\n", win->id);
2973
2974         ws = win->ws;
2975         nws = &win->s->ws[wsid];
2976
2977         a.id = SWM_ARG_ID_FOCUSPREV;
2978         focus(r, &a);
2979         if (win->transient) {
2980                 parent = find_window(win->transient);
2981                 if (parent) {
2982                         unmap_window(parent);
2983                         TAILQ_REMOVE(&ws->winlist, parent, entry);
2984                         TAILQ_INSERT_TAIL(&nws->winlist, parent, entry);
2985                         parent->ws = nws;
2986                 }
2987         }
2988         unmap_window(win);
2989         TAILQ_REMOVE(&ws->winlist, win, entry);
2990         TAILQ_INSERT_TAIL(&nws->winlist, win, entry);
2991         win->ws = nws;
2992
2993         /* Try to update the window's workspace property */
2994         ws_idx_atom = XInternAtom(display, "_SWM_WS", False);
2995         if (ws_idx_atom &&
2996             snprintf(ws_idx_str, SWM_PROPLEN, "%d", nws->idx) < SWM_PROPLEN) {
2997                 DNPRINTF(SWM_D_PROP, "setting property _SWM_WS to %s\n",
2998                     ws_idx_str);
2999                 XChangeProperty(display, win->id, ws_idx_atom, XA_STRING, 8,
3000                     PropModeReplace, ws_idx_str, SWM_PROPLEN);
3001         }
3002
3003         stack();
3004 }
3005
3006 void
3007 raise_toggle(struct swm_region *r, union arg *args)
3008 {
3009         if (r && r->ws == NULL)
3010                 return;
3011
3012         r->ws->always_raise = !r->ws->always_raise;
3013
3014         /* bring floaters back to top */
3015         if (r->ws->always_raise == 0)
3016                 stack();
3017 }
3018
3019 void
3020 iconify(struct swm_region *r, union arg *args)
3021 {
3022         union arg a;
3023
3024         if (r->ws->focus == NULL)
3025                 return;
3026         unmap_window(r->ws->focus);
3027         update_iconic(r->ws->focus, 1);
3028         stack();
3029         if (focus_mode == SWM_FOCUS_DEFAULT)
3030                 drain_enter_notify();
3031         r->ws->focus = NULL;
3032         a.id = SWM_ARG_ID_FOCUSCUR;
3033         focus(r, &a);
3034 }
3035
3036 unsigned char *
3037 get_win_name(Display *dpy, Window win, Atom wname, Atom stype,
3038     unsigned long *slen)
3039 {
3040         int                     status, retfmt;
3041         unsigned long           nitems, nbytes, nextra;
3042         unsigned char           *prop = NULL;
3043         Atom                    rettype;
3044
3045         status = XGetWindowProperty(dpy, win, wname, 0L, 0L, False, stype,
3046             &rettype, &retfmt,  &nitems, &nbytes, &prop);
3047         if (status != Success)
3048                 return (NULL);
3049         XFree(prop);
3050
3051         status = XGetWindowProperty(dpy, win, wname, 0L, nbytes, False,
3052             stype, &rettype, &retfmt, &nitems, &nextra, &prop);
3053         if (status != Success) {
3054                 XFree(prop);
3055                 return (NULL);
3056         }
3057         if (rettype != stype) {
3058                 XFree(prop);
3059                 return (NULL);
3060         }
3061         *slen = nitems;
3062         return (prop);
3063 }
3064
3065 void
3066 uniconify(struct swm_region *r, union arg *args)
3067 {
3068         struct ws_win           *win;
3069         FILE                    *lfile;
3070         char                    *name;
3071         int                     count = 0;
3072         unsigned long           len;
3073
3074         DNPRINTF(SWM_D_MISC, "uniconify\n");
3075
3076         if (r && r->ws == NULL)
3077                 return;
3078
3079         /* make sure we have anything to uniconify */
3080         TAILQ_FOREACH(win, &r->ws->winlist, entry) {
3081                 if (win->ws == NULL)
3082                         continue; /* should never happen */
3083                 if (win->iconic == 0)
3084                         continue;
3085                 count++;
3086         }
3087         if (count == 0)
3088                 return;
3089
3090         search_r = r;
3091
3092         spawn_select(r, args, "uniconify", &searchpid);
3093
3094         if ((lfile = fdopen(select_list_pipe[1], "w")) == NULL)
3095                 return;
3096
3097         TAILQ_FOREACH(win, &r->ws->winlist, entry) {
3098                 if (win->ws == NULL)
3099                         continue; /* should never happen */
3100                 if (win->iconic == 0)
3101                         continue;
3102
3103                 name = get_win_name(display, win->id, a_wmname, a_string,
3104                     &len);
3105                 if (name == NULL)
3106                         continue;
3107                 fprintf(lfile, "%s.%lu\n", name, win->id);
3108                 XFree(name);
3109         }
3110
3111         fclose(lfile);
3112 }
3113
3114 #define MAX_RESP_LEN    1024
3115
3116 void
3117 search_do_resp(void)
3118 {
3119         ssize_t                 rbytes;
3120         struct ws_win           *win;
3121         char                    *name, *resp, *s;
3122         unsigned long           len;
3123
3124         DNPRINTF(SWM_D_MISC, "search_do_resp:\n");
3125
3126         search_resp = 0;
3127         searchpid = 0;
3128
3129         if ((resp = calloc(1, MAX_RESP_LEN + 1)) == NULL) {
3130                 fprintf(stderr, "search: calloc\n");
3131                 return;
3132         }
3133
3134         rbytes = read(select_resp_pipe[0], resp, MAX_RESP_LEN);
3135         if (rbytes <= 0) {
3136                 fprintf(stderr, "search: read error: %s\n", strerror(errno));
3137                 goto done;
3138         }
3139         resp[rbytes] = '\0';
3140         len = strlen(resp);
3141
3142         DNPRINTF(SWM_D_MISC, "search_do_resp: resp %s\n", resp);
3143         TAILQ_FOREACH(win, &search_r->ws->winlist, entry) {
3144                 if (win->iconic == 0)
3145                         continue;
3146                 name = get_win_name(display, win->id, a_wmname, a_string, &len);
3147                 if (name == NULL)
3148                         continue;
3149                 if (asprintf(&s, "%s.%lu", name, win->id) == -1) {
3150                         XFree(name);
3151                         continue;
3152                 }
3153                 XFree(name);
3154                 if (strncmp(s, resp, len) == 0) {
3155                         /* XXX this should be a callback to generalize */
3156                         update_iconic(win, 0);
3157                         free(s);
3158                         break;
3159                 }
3160                 free(s);
3161         }
3162 done:
3163         free(resp);
3164 }
3165
3166 void
3167 wkill(struct swm_region *r, union arg *args)
3168 {
3169         DNPRINTF(SWM_D_MISC, "wkill %d\n", args->id);
3170
3171         if (r->ws->focus == NULL)
3172                 return;
3173
3174         if (args->id == SWM_ARG_ID_KILLWINDOW)
3175                 XKillClient(display, r->ws->focus->id);
3176         else
3177                 if (r->ws->focus->can_delete)
3178                         client_msg(r->ws->focus, adelete);
3179 }
3180
3181
3182 int
3183 floating_toggle_win(struct ws_win *win)
3184 {
3185         struct swm_region       *r;
3186
3187         if (win == NULL)
3188                 return 0;
3189
3190         if (!win->ws->r)
3191                 return 0;
3192
3193         r = win->ws->r;
3194
3195         /* reject floating toggles in max stack mode */
3196         if (win->ws->cur_layout == &layouts[SWM_MAX_STACK])
3197                 return 0;
3198
3199         if (win->floating) {
3200                 if (!win->floatmaxed) {
3201                         /* retain position for refloat */
3202                         store_float_geom(win, r);
3203                 }
3204                 win->floating = 0;
3205         } else {
3206                 if (win->g_floatvalid) {
3207                         /* refloat at last floating relative position */
3208                         win->g.x = win->g_float.x - win->rg_float.x + r->g.x;
3209                         win->g.y = win->g_float.y - win->rg_float.y + r->g.y;
3210                         win->g.w = win->g_float.w;
3211                         win->g.h = win->g_float.h;
3212                 }
3213                 win->floating = 1;
3214         }
3215
3216         ewmh_update_actions(win);
3217
3218         return 1;
3219 }
3220
3221 void
3222 floating_toggle(struct swm_region *r, union arg *args)
3223 {
3224         struct ws_win           *win = r->ws->focus;
3225         union arg               a;
3226
3227         if (win == NULL)
3228                 return;
3229
3230         ewmh_update_win_state(win, ewmh[_NET_WM_STATE_ABOVE].atom,
3231             _NET_WM_STATE_TOGGLE);
3232
3233         stack();
3234         if (focus_mode == SWM_FOCUS_DEFAULT)
3235                 drain_enter_notify();
3236
3237         if (win == win->ws->focus) {
3238                 a.id = SWM_ARG_ID_FOCUSCUR;
3239                 focus(win->ws->r, &a);
3240         }
3241 }
3242
3243 void
3244 resize_window(struct ws_win *win, int center)
3245 {
3246         unsigned int            mask;
3247         XWindowChanges          wc;
3248         struct swm_region       *r;
3249
3250         r = root_to_region(win->wa.root);
3251         bzero(&wc, sizeof wc);
3252         mask = CWBorderWidth | CWWidth | CWHeight;
3253         wc.border_width = border_width;
3254         wc.width = win->g.w;
3255         wc.height = win->g.h;
3256         if (center == SWM_ARG_ID_CENTER) {
3257                 wc.x = (WIDTH(r) - win->g.w) / 2 - border_width;
3258                 wc.y = (HEIGHT(r) - win->g.h) / 2 - border_width;
3259                 mask |= CWX | CWY;
3260         }
3261
3262         DNPRINTF(SWM_D_STACK, "resize_window: win %lu x %d y %d w %d h %d\n",
3263             win->id, wc.x, wc.y, wc.width, wc.height);
3264
3265         XConfigureWindow(display, win->id, mask, &wc);
3266 }
3267
3268 void
3269 resize(struct ws_win *win, union arg *args)
3270 {
3271         XEvent                  ev;
3272         Time                    time = 0;
3273         struct swm_region       *r = win->ws->r;
3274         int                     relx, rely;
3275
3276
3277         DNPRINTF(SWM_D_MOUSE, "resize: win %lu floating %d trans %lu\n",
3278             win->id, win->floating, win->transient);
3279
3280         if (!(win->transient != 0 || win->floating != 0))
3281                 return;
3282
3283         /* reject resizes in max mode for floaters (transient ok) */
3284         if (win->floatmaxed)
3285                 return;
3286
3287         win->manual = 1;
3288         ewmh_update_win_state(win, ewmh[_SWM_WM_STATE_MANUAL].atom,
3289             _NET_WM_STATE_ADD);
3290
3291         stack();
3292         if (focus_mode == SWM_FOCUS_DEFAULT)
3293                 drain_enter_notify();
3294
3295         if (XGrabPointer(display, win->id, False, MOUSEMASK, GrabModeAsync,
3296             GrabModeAsync, None, None /* cursor */, CurrentTime) != GrabSuccess)
3297                 return;
3298
3299         /* place pointer at bottom left corner or nearest point inside r */
3300         if ( win->g.x + win->g.w < r->g.x + r->g.w - 1)
3301                 relx = win->g.w - 1;
3302         else
3303                 relx = r->g.x + r->g.w - win->g.x - 1;
3304
3305         if ( win->g.y + win->g.h < r->g.y + r->g.h - 1)
3306                 rely = win->g.h - 1;
3307         else
3308                 rely = r->g.y + r->g.h - win->g.y - 1;
3309
3310         XWarpPointer(display, None, win->id, 0, 0, 0, 0, relx, rely);
3311         do {
3312                 XMaskEvent(display, MOUSEMASK | ExposureMask |
3313                     SubstructureRedirectMask, &ev);
3314                 switch (ev.type) {
3315                 case ConfigureRequest:
3316                 case Expose:
3317                 case MapRequest:
3318                         handler[ev.type](&ev);
3319                         break;
3320                 case MotionNotify:
3321                         /* do not allow resize outside of region */
3322                         if (    ev.xmotion.x_root < r->g.x ||
3323                                 ev.xmotion.x_root > r->g.x + r->g.w - 1 ||
3324                                 ev.xmotion.y_root < r->g.y ||
3325                                 ev.xmotion.y_root > r->g.y + r->g.h - 1)
3326                                 continue;
3327
3328                         if (ev.xmotion.x <= 1)
3329                                 ev.xmotion.x = 1;
3330                         if (ev.xmotion.y <= 1)
3331                                 ev.xmotion.y = 1;
3332                         win->g.w = ev.xmotion.x + 1;
3333                         win->g.h = ev.xmotion.y + 1;
3334
3335                         /* not free, don't sync more than 120 times / second */
3336                         if ((ev.xmotion.time - time) > (1000 / 120) ) {
3337                                 time = ev.xmotion.time;
3338                                 XSync(display, False);
3339                                 resize_window(win, args->id);
3340                         }
3341                         break;
3342                 }
3343         } while (ev.type != ButtonRelease);
3344         if (time) {
3345                 XSync(display, False);
3346                 resize_window(win, args->id);
3347         }
3348         store_float_geom(win,r);
3349
3350         XWarpPointer(display, None, win->id, 0, 0, 0, 0, win->g.w - 1,
3351             win->g.h - 1);
3352         XUngrabPointer(display, CurrentTime);
3353
3354         /* drain events */
3355         drain_enter_notify();
3356 }
3357
3358 void
3359 move_window(struct ws_win *win)
3360 {
3361         unsigned int            mask;
3362         XWindowChanges          wc;
3363         struct swm_region       *r;
3364
3365         r = root_to_region(win->wa.root);
3366         bzero(&wc, sizeof wc);
3367         mask = CWX | CWY;
3368         wc.x = win->g.x;
3369         wc.y = win->g.y;
3370         wc.border_width = border_width;
3371
3372         DNPRINTF(SWM_D_STACK, "move_window: win %lu x %d y %d w %d h %d\n",
3373             win->id, wc.x, wc.y, wc.width, wc.height);
3374
3375         XConfigureWindow(display, win->id, mask, &wc);
3376 }
3377
3378 void
3379 move(struct ws_win *win, union arg *args)
3380 {
3381         XEvent                  ev;
3382         Time                    time = 0;
3383         struct swm_region       *r = win->ws->r;
3384
3385         DNPRINTF(SWM_D_MOUSE, "move: win %lu floating %d trans %lu\n",
3386             win->id, win->floating, win->transient);
3387
3388         /* in max_stack mode should only move transients */
3389         if (win->ws->cur_layout == &layouts[SWM_MAX_STACK] && !win->transient)
3390                 return;
3391
3392         win->manual = 1;
3393         if (win->floating == 0 && !win->transient) {
3394                 ewmh_update_win_state(win, ewmh[_NET_WM_STATE_ABOVE].atom,
3395                     _NET_WM_STATE_ADD);
3396         }
3397         ewmh_update_win_state(win, ewmh[_SWM_WM_STATE_MANUAL].atom,
3398             _NET_WM_STATE_ADD);
3399
3400         stack();
3401
3402         if (XGrabPointer(display, win->id, False, MOUSEMASK, GrabModeAsync,
3403             GrabModeAsync, None, None /* cursor */, CurrentTime) != GrabSuccess)
3404                 return;
3405         XWarpPointer(display, None, win->id, 0, 0, 0, 0, 0, 0);
3406         do {
3407                 XMaskEvent(display, MOUSEMASK | ExposureMask |
3408                     SubstructureRedirectMask, &ev);
3409                 switch (ev.type) {
3410                 case ConfigureRequest:
3411                 case Expose:
3412                 case MapRequest:
3413                         handler[ev.type](&ev);
3414                         break;
3415                 case MotionNotify:
3416                         /* don't allow to move window origin out of region */
3417                         if (    ev.xmotion.x_root < r->g.x ||
3418                                 ev.xmotion.x_root > r->g.x + r->g.w - 1 ||
3419                                 ev.xmotion.y_root < r->g.y ||
3420                                 ev.xmotion.y_root > r->g.y + r->g.h - 1)
3421                                 continue;
3422
3423                         win->g.x = ev.xmotion.x_root - border_width;
3424                         win->g.y = ev.xmotion.y_root - border_width;
3425
3426                         /* not free, don't sync more than 120 times / second */
3427                         if ((ev.xmotion.time - time) > (1000 / 120) ) {
3428                                 time = ev.xmotion.time;
3429                                 XSync(display, False);
3430                                 move_window(win);
3431                         }
3432                         break;
3433                 }
3434         } while (ev.type != ButtonRelease);
3435         if (time) {
3436                 XSync(display, False);
3437                 move_window(win);
3438         }
3439         store_float_geom(win,r);
3440         XWarpPointer(display, None, win->id, 0, 0, 0, 0, 0, 0);
3441         XUngrabPointer(display, CurrentTime);
3442
3443         /* drain events */
3444         drain_enter_notify();
3445 }
3446
3447 /* user/key callable function IDs */
3448 enum keyfuncid {
3449         kf_cycle_layout,
3450         kf_stack_reset,
3451         kf_master_shrink,
3452         kf_master_grow,
3453         kf_master_add,
3454         kf_master_del,
3455         kf_stack_inc,
3456         kf_stack_dec,
3457         kf_swap_main,
3458         kf_focus_next,
3459         kf_focus_prev,
3460         kf_swap_next,
3461         kf_swap_prev,
3462         kf_spawn_term,
3463         kf_spawn_menu,
3464         kf_quit,
3465         kf_restart,
3466         kf_focus_main,
3467         kf_ws_1,
3468         kf_ws_2,
3469         kf_ws_3,
3470         kf_ws_4,
3471         kf_ws_5,
3472         kf_ws_6,
3473         kf_ws_7,
3474         kf_ws_8,
3475         kf_ws_9,
3476         kf_ws_10,
3477         kf_ws_next,
3478         kf_ws_prev,
3479         kf_ws_prior,
3480         kf_screen_next,
3481         kf_screen_prev,
3482         kf_mvws_1,
3483         kf_mvws_2,
3484         kf_mvws_3,
3485         kf_mvws_4,
3486         kf_mvws_5,
3487         kf_mvws_6,
3488         kf_mvws_7,
3489         kf_mvws_8,
3490         kf_mvws_9,
3491         kf_mvws_10,
3492         kf_bar_toggle,
3493         kf_wind_kill,
3494         kf_wind_del,
3495         kf_screenshot_all,
3496         kf_screenshot_wind,
3497         kf_float_toggle,
3498         kf_version,
3499         kf_spawn_lock,
3500         kf_spawn_initscr,
3501         kf_spawn_custom,
3502         kf_iconify,
3503         kf_uniconify,
3504         kf_raise_toggle,
3505         kf_dumpwins, /* MUST BE LAST */
3506         kf_invalid
3507 };
3508
3509 /* key definitions */
3510 void
3511 dummykeyfunc(struct swm_region *r, union arg *args)
3512 {
3513 };
3514
3515 void
3516 legacyfunc(struct swm_region *r, union arg *args)
3517 {
3518 };
3519
3520 struct keyfunc {
3521         char                    name[SWM_FUNCNAME_LEN];
3522         void                    (*func)(struct swm_region *r, union arg *);
3523         union arg               args;
3524 } keyfuncs[kf_invalid + 1] = {
3525         /* name                 function        argument */
3526         { "cycle_layout",       cycle_layout,   {0} },
3527         { "stack_reset",        stack_config,   {.id = SWM_ARG_ID_STACKRESET} },
3528         { "master_shrink",      stack_config,   {.id = SWM_ARG_ID_MASTERSHRINK} },
3529         { "master_grow",        stack_config,   {.id = SWM_ARG_ID_MASTERGROW} },
3530         { "master_add",         stack_config,   {.id = SWM_ARG_ID_MASTERADD} },
3531         { "master_del",         stack_config,   {.id = SWM_ARG_ID_MASTERDEL} },
3532         { "stack_inc",          stack_config,   {.id = SWM_ARG_ID_STACKINC} },
3533         { "stack_dec",          stack_config,   {.id = SWM_ARG_ID_STACKDEC} },
3534         { "swap_main",          swapwin,        {.id = SWM_ARG_ID_SWAPMAIN} },
3535         { "focus_next",         focus,          {.id = SWM_ARG_ID_FOCUSNEXT} },
3536         { "focus_prev",         focus,          {.id = SWM_ARG_ID_FOCUSPREV} },
3537         { "swap_next",          swapwin,        {.id = SWM_ARG_ID_SWAPNEXT} },
3538         { "swap_prev",          swapwin,        {.id = SWM_ARG_ID_SWAPPREV} },
3539         { "spawn_term",         spawnterm,      {.argv = spawn_term} },
3540         { "spawn_menu",         legacyfunc,     {0} },
3541         { "quit",               quit,           {0} },
3542         { "restart",            restart,        {0} },
3543         { "focus_main",         focus,          {.id = SWM_ARG_ID_FOCUSMAIN} },
3544         { "ws_1",               switchws,       {.id = 0} },
3545         { "ws_2",               switchws,       {.id = 1} },
3546         { "ws_3",               switchws,       {.id = 2} },
3547         { "ws_4",               switchws,       {.id = 3} },
3548         { "ws_5",               switchws,       {.id = 4} },
3549         { "ws_6",               switchws,       {.id = 5} },
3550         { "ws_7",               switchws,       {.id = 6} },
3551         { "ws_8",               switchws,       {.id = 7} },
3552         { "ws_9",               switchws,       {.id = 8} },
3553         { "ws_10",              switchws,       {.id = 9} },
3554         { "ws_next",            cyclews,        {.id = SWM_ARG_ID_CYCLEWS_UP} },
3555         { "ws_prev",            cyclews,        {.id = SWM_ARG_ID_CYCLEWS_DOWN} },
3556         { "ws_prior",           priorws,        {0} },
3557         { "screen_next",        cyclescr,       {.id = SWM_ARG_ID_CYCLESC_UP} },
3558         { "screen_prev",        cyclescr,       {.id = SWM_ARG_ID_CYCLESC_DOWN} },
3559         { "mvws_1",             send_to_ws,     {.id = 0} },
3560         { "mvws_2",             send_to_ws,     {.id = 1} },
3561         { "mvws_3",             send_to_ws,     {.id = 2} },
3562         { "mvws_4",             send_to_ws,     {.id = 3} },
3563         { "mvws_5",             send_to_ws,     {.id = 4} },
3564         { "mvws_6",             send_to_ws,     {.id = 5} },
3565         { "mvws_7",             send_to_ws,     {.id = 6} },
3566         { "mvws_8",             send_to_ws,     {.id = 7} },
3567         { "mvws_9",             send_to_ws,     {.id = 8} },
3568         { "mvws_10",            send_to_ws,     {.id = 9} },
3569         { "bar_toggle",         bar_toggle,     {0} },
3570         { "wind_kill",          wkill,          {.id = SWM_ARG_ID_KILLWINDOW} },
3571         { "wind_del",           wkill,          {.id = SWM_ARG_ID_DELETEWINDOW} },
3572         { "screenshot_all",     legacyfunc,     {0} },
3573         { "screenshot_wind",    legacyfunc,     {0} },
3574         { "float_toggle",       floating_toggle,{0} },
3575         { "version",            version,        {0} },
3576         { "spawn_lock",         legacyfunc,     {0} },
3577         { "spawn_initscr",      legacyfunc,     {0} },
3578         { "spawn_custom",       dummykeyfunc,   {0} },
3579         { "iconify",            iconify,        {0} },
3580         { "uniconify",          uniconify,      {0} },
3581         { "raise_toggle",       raise_toggle,   {0} },
3582         { "dumpwins",           dumpwins,       {0} }, /* MUST BE LAST */
3583         { "invalid key func",   NULL,           {0} },
3584 };
3585 struct key {
3586         unsigned int            mod;
3587         KeySym                  keysym;
3588         enum keyfuncid          funcid;
3589         char                    *spawn_name;
3590 };
3591 int                             keys_size = 0, keys_length = 0;
3592 struct key                      *keys = NULL;
3593
3594 /* mouse */
3595 enum { client_click, root_click };
3596 struct button {
3597         unsigned int            action;
3598         unsigned int            mask;
3599         unsigned int            button;
3600         void                    (*func)(struct ws_win *, union arg *);
3601         union arg               args;
3602 } buttons[] = {
3603           /* action     key             mouse button    func    args */
3604         { client_click, MODKEY,         Button3,        resize, {.id = SWM_ARG_ID_DONTCENTER} },
3605         { client_click, MODKEY | ShiftMask, Button3,    resize, {.id = SWM_ARG_ID_CENTER} },
3606         { client_click, MODKEY,         Button1,        move,   {0} },
3607 };
3608
3609 void
3610 update_modkey(unsigned int mod)
3611 {
3612         int                     i;
3613
3614         mod_key = mod;
3615         for (i = 0; i < keys_length; i++)
3616                 if (keys[i].mod & ShiftMask)
3617                         keys[i].mod = mod | ShiftMask;
3618                 else
3619                         keys[i].mod = mod;
3620
3621         for (i = 0; i < LENGTH(buttons); i++)
3622                 if (buttons[i].mask & ShiftMask)
3623                         buttons[i].mask = mod | ShiftMask;
3624                 else
3625                         buttons[i].mask = mod;
3626 }
3627
3628 /* spawn */
3629 struct spawn_prog {
3630         char                    *name;
3631         int                     argc;
3632         char                    **argv;
3633 };
3634
3635 int                             spawns_size = 0, spawns_length = 0;
3636 struct spawn_prog               *spawns = NULL;
3637
3638 int
3639 spawn_expand(struct swm_region *r, union arg *args, char *spawn_name,
3640     char ***ret_args)
3641 {
3642         struct spawn_prog       *prog = NULL;
3643         int                     i;
3644         char                    *ap, **real_args;
3645
3646         DNPRINTF(SWM_D_SPAWN, "spawn_expand %s\n", spawn_name);
3647
3648         /* find program */
3649         for (i = 0; i < spawns_length; i++) {
3650                 if (!strcasecmp(spawn_name, spawns[i].name))
3651                         prog = &spawns[i];
3652         }
3653         if (prog == NULL) {
3654                 fprintf(stderr, "spawn_custom: program %s not found\n",
3655                     spawn_name);
3656                 return (-1);
3657         }
3658
3659         /* make room for expanded args */
3660         if ((real_args = calloc(prog->argc + 1, sizeof(char *))) == NULL)
3661                 err(1, "spawn_custom: calloc real_args");
3662
3663         /* expand spawn_args into real_args */
3664         for (i = 0; i < prog->argc; i++) {
3665                 ap = prog->argv[i];
3666                 DNPRINTF(SWM_D_SPAWN, "spawn_custom: raw arg = %s\n", ap);
3667                 if (!strcasecmp(ap, "$bar_border")) {
3668                         if ((real_args[i] =
3669                             strdup(r->s->c[SWM_S_COLOR_BAR_BORDER].name))
3670                             == NULL)
3671                                 err(1,  "spawn_custom border color");
3672                 } else if (!strcasecmp(ap, "$bar_color")) {
3673                         if ((real_args[i] =
3674                             strdup(r->s->c[SWM_S_COLOR_BAR].name))
3675                             == NULL)
3676                                 err(1, "spawn_custom bar color");
3677                 } else if (!strcasecmp(ap, "$bar_font")) {
3678                         if ((real_args[i] = strdup(bar_fonts[bar_fidx]))
3679                             == NULL)
3680                                 err(1, "spawn_custom bar fonts");
3681                 } else if (!strcasecmp(ap, "$bar_font_color")) {
3682                         if ((real_args[i] =
3683                             strdup(r->s->c[SWM_S_COLOR_BAR_FONT].name))
3684                             == NULL)
3685                                 err(1, "spawn_custom color font");
3686                 } else if (!strcasecmp(ap, "$color_focus")) {
3687                         if ((real_args[i] =
3688                             strdup(r->s->c[SWM_S_COLOR_FOCUS].name))
3689                             == NULL)
3690                                 err(1, "spawn_custom color focus");
3691                 } else if (!strcasecmp(ap, "$color_unfocus")) {
3692                         if ((real_args[i] =
3693                             strdup(r->s->c[SWM_S_COLOR_UNFOCUS].name))
3694                             == NULL)
3695                                 err(1, "spawn_custom color unfocus");
3696                 } else {
3697                         /* no match --> copy as is */
3698                         if ((real_args[i] = strdup(ap)) == NULL)
3699                                 err(1, "spawn_custom strdup(ap)");
3700                 }
3701                 DNPRINTF(SWM_D_SPAWN, "spawn_custom: cooked arg = %s\n",
3702                     real_args[i]);
3703         }
3704
3705 #ifdef SWM_DEBUG
3706         if ((swm_debug & SWM_D_SPAWN) != 0) {
3707                 fprintf(stderr, "spawn_custom: result = ");
3708                 for (i = 0; i < prog->argc; i++)
3709                         fprintf(stderr, "\"%s\" ", real_args[i]);
3710                 fprintf(stderr, "\n");
3711         }
3712 #endif
3713         *ret_args = real_args;
3714         return (prog->argc);
3715 }
3716
3717 void
3718 spawn_custom(struct swm_region *r, union arg *args, char *spawn_name)
3719 {
3720         union arg               a;
3721         char                    **real_args;
3722         int                     spawn_argc, i;
3723
3724         if ((spawn_argc = spawn_expand(r, args, spawn_name, &real_args)) < 0)
3725                 return;
3726         a.argv = real_args;
3727         if (fork() == 0)
3728                 spawn(r->ws->idx, &a, 1);
3729
3730         for (i = 0; i < spawn_argc; i++)
3731                 free(real_args[i]);
3732         free(real_args);
3733 }
3734
3735 void
3736 spawn_select(struct swm_region *r, union arg *args, char *spawn_name, int *pid)
3737 {
3738         union arg               a;
3739         char                    **real_args;
3740         int                     i, spawn_argc;
3741
3742         if ((spawn_argc = spawn_expand(r, args, spawn_name, &real_args)) < 0)
3743                 return;
3744         a.argv = real_args;
3745
3746         if (pipe(select_list_pipe) == -1)
3747                 err(1, "pipe error");
3748         if (pipe(select_resp_pipe) == -1)
3749                 err(1, "pipe error");
3750
3751         if (signal(SIGPIPE, SIG_IGN) == SIG_ERR)
3752                 err(1, "could not disable SIGPIPE");
3753         switch (*pid = fork()) {
3754         case -1:
3755                 err(1, "cannot fork");
3756                 break;
3757         case 0: /* child */
3758                 if (dup2(select_list_pipe[0], 0) == -1)
3759                         errx(1, "dup2");
3760                 if (dup2(select_resp_pipe[1], 1) == -1)
3761                         errx(1, "dup2");
3762                 close(select_list_pipe[1]);
3763                 close(select_resp_pipe[0]);
3764                 spawn(r->ws->idx, &a, 0);
3765                 break;
3766         default: /* parent */
3767                 close(select_list_pipe[0]);
3768                 close(select_resp_pipe[1]);
3769                 break;
3770         }
3771
3772         for (i = 0; i < spawn_argc; i++)
3773                 free(real_args[i]);
3774         free(real_args);
3775 }
3776
3777 void
3778 setspawn(struct spawn_prog *prog)
3779 {
3780         int                     i, j;
3781
3782         if (prog == NULL || prog->name == NULL)
3783                 return;
3784
3785         /* find existing */
3786         for (i = 0; i < spawns_length; i++) {
3787                 if (!strcmp(spawns[i].name, prog->name)) {
3788                         /* found */
3789                         if (prog->argv == NULL) {
3790                                 /* delete */
3791                                 DNPRINTF(SWM_D_SPAWN,
3792                                     "setspawn: delete #%d %s\n",
3793                                     i, spawns[i].name);
3794                                 free(spawns[i].name);
3795                                 for (j = 0; j < spawns[i].argc; j++)
3796                                         free(spawns[i].argv[j]);
3797                                 free(spawns[i].argv);
3798                                 j = spawns_length - 1;
3799                                 if (i < j)
3800                                         spawns[i] = spawns[j];
3801                                 spawns_length--;
3802                                 free(prog->name);
3803                         } else {
3804                                 /* replace */
3805                                 DNPRINTF(SWM_D_SPAWN,
3806                                     "setspawn: replace #%d %s\n",
3807                                     i, spawns[i].name);
3808                                 free(spawns[i].name);
3809                                 for (j = 0; j < spawns[i].argc; j++)
3810                                         free(spawns[i].argv[j]);
3811                                 free(spawns[i].argv);
3812                                 spawns[i] = *prog;
3813                         }
3814                         /* found case handled */
3815                         free(prog);
3816                         return;
3817                 }
3818         }
3819
3820         if (prog->argv == NULL) {
3821                 fprintf(stderr,
3822                     "error: setspawn: cannot find program %s", prog->name);
3823                 free(prog);
3824                 return;
3825         }
3826
3827         /* not found: add */
3828         if (spawns_size == 0 || spawns == NULL) {
3829                 spawns_size = 4;
3830                 DNPRINTF(SWM_D_SPAWN, "setspawn: init list %d\n", spawns_size);
3831                 spawns = malloc((size_t)spawns_size *
3832                     sizeof(struct spawn_prog));
3833                 if (spawns == NULL) {
3834                         fprintf(stderr, "setspawn: malloc failed\n");
3835                         perror(" failed");
3836                         quit(NULL, NULL);
3837                 }
3838         } else if (spawns_length == spawns_size) {
3839                 spawns_size *= 2;
3840                 DNPRINTF(SWM_D_SPAWN, "setspawn: grow list %d\n", spawns_size);
3841                 spawns = realloc(spawns, (size_t)spawns_size *
3842                     sizeof(struct spawn_prog));
3843                 if (spawns == NULL) {
3844                         fprintf(stderr, "setspawn: realloc failed\n");
3845                         perror(" failed");
3846                         quit(NULL, NULL);
3847                 }
3848         }
3849
3850         if (spawns_length < spawns_size) {
3851                 DNPRINTF(SWM_D_SPAWN, "setspawn: add #%d %s\n",
3852                     spawns_length, prog->name);
3853                 i = spawns_length++;
3854                 spawns[i] = *prog;
3855         } else {
3856                 fprintf(stderr, "spawns array problem?\n");
3857                 if (spawns == NULL) {
3858                         fprintf(stderr, "spawns array is NULL!\n");
3859                         quit(NULL, NULL);
3860                 }
3861         }
3862         free(prog);
3863 }
3864
3865 int
3866 setconfspawn(char *selector, char *value, int flags)
3867 {
3868         struct spawn_prog       *prog;
3869         char                    *vp, *cp, *word;
3870
3871         DNPRINTF(SWM_D_SPAWN, "setconfspawn: [%s] [%s]\n", selector, value);
3872         if ((prog = calloc(1, sizeof *prog)) == NULL)
3873                 err(1, "setconfspawn: calloc prog");
3874         prog->name = strdup(selector);
3875         if (prog->name == NULL)
3876                 err(1, "setconfspawn prog->name");
3877         if ((cp = vp = strdup(value)) == NULL)
3878                 err(1, "setconfspawn: strdup(value) ");
3879         while ((word = strsep(&cp, " \t")) != NULL) {
3880                 DNPRINTF(SWM_D_SPAWN, "setconfspawn: arg [%s]\n", word);
3881                 if (cp)
3882                         cp += (long)strspn(cp, " \t");
3883                 if (strlen(word) > 0) {
3884                         prog->argc++;
3885                         if ((prog->argv = realloc(prog->argv,
3886                             prog->argc * sizeof(char *))) == NULL)
3887                                 err(1, "setconfspawn: realloc");
3888                         if ((prog->argv[prog->argc - 1] = strdup(word)) == NULL)
3889                                 err(1, "setconfspawn: strdup");
3890                 }
3891         }
3892         free(vp);
3893
3894         setspawn(prog);
3895
3896         DNPRINTF(SWM_D_SPAWN, "setconfspawn: done\n");
3897         return (0);
3898 }
3899
3900 void
3901 setup_spawn(void)
3902 {
3903         setconfspawn("term",            "xterm",                0);
3904         setconfspawn("screenshot_all",  "screenshot.sh full",   0);
3905         setconfspawn("screenshot_wind", "screenshot.sh window", 0);
3906         setconfspawn("lock",            "xlock",                0);
3907         setconfspawn("initscr",         "initscreen.sh",        0);
3908         setconfspawn("menu",            "dmenu_run"
3909                                         " -fn $bar_font"
3910                                         " -nb $bar_color"
3911                                         " -nf $bar_font_color"
3912                                         " -sb $bar_border"
3913                                         " -sf $bar_color",      0);
3914         setconfspawn("uniconify",       "dmenu"
3915                                         " -i"
3916                                         " -fn $bar_font"
3917                                         " -nb $bar_color"
3918                                         " -nf $bar_font_color"
3919                                         " -sb $bar_border"
3920                                         " -sf $bar_color",      0);
3921 }
3922
3923 /* key bindings */
3924 #define SWM_MODNAME_SIZE        32
3925 #define SWM_KEY_WS              "\n+ \t"
3926 int
3927 parsekeys(char *keystr, unsigned int currmod, unsigned int *mod, KeySym *ks)
3928 {
3929         char                    *cp, *name;
3930         KeySym                  uks;
3931         DNPRINTF(SWM_D_KEY, "parsekeys: enter [%s]\n", keystr);
3932         if (mod == NULL || ks == NULL) {
3933                 DNPRINTF(SWM_D_KEY, "parsekeys: no mod or key vars\n");
3934                 return (1);
3935         }
3936         if (keystr == NULL || strlen(keystr) == 0) {
3937                 DNPRINTF(SWM_D_KEY, "parsekeys: no keystr\n");
3938                 return (1);
3939         }
3940         cp = keystr;
3941         *ks = NoSymbol;
3942         *mod = 0;
3943         while ((name = strsep(&cp, SWM_KEY_WS)) != NULL) {
3944                 DNPRINTF(SWM_D_KEY, "parsekeys: key [%s]\n", name);
3945                 if (cp)
3946                         cp += (long)strspn(cp, SWM_KEY_WS);
3947                 if (strncasecmp(name, "MOD", SWM_MODNAME_SIZE) == 0)
3948                         *mod |= currmod;
3949                 else if (!strncasecmp(name, "Mod1", SWM_MODNAME_SIZE))
3950                         *mod |= Mod1Mask;
3951                 else if (!strncasecmp(name, "Mod2", SWM_MODNAME_SIZE))
3952                         *mod += Mod2Mask;
3953                 else if (!strncmp(name, "Mod3", SWM_MODNAME_SIZE))
3954                         *mod |= Mod3Mask;
3955                 else if (!strncmp(name, "Mod4", SWM_MODNAME_SIZE))
3956                         *mod |= Mod4Mask;
3957                 else if (strncasecmp(name, "SHIFT", SWM_MODNAME_SIZE) == 0)
3958                         *mod |= ShiftMask;
3959                 else if (strncasecmp(name, "CONTROL", SWM_MODNAME_SIZE) == 0)
3960                         *mod |= ControlMask;
3961                 else {
3962                         *ks = XStringToKeysym(name);
3963                         XConvertCase(*ks, ks, &uks);
3964                         if (ks == NoSymbol) {
3965                                 DNPRINTF(SWM_D_KEY,
3966                                     "parsekeys: invalid key %s\n",
3967                                     name);
3968                                 return (1);
3969                         }
3970                 }
3971         }
3972         DNPRINTF(SWM_D_KEY, "parsekeys: leave ok\n");
3973         return (0);
3974 }
3975
3976 char *
3977 strdupsafe(char *str)
3978 {
3979         if (str == NULL)
3980                 return (NULL);
3981         else
3982                 return (strdup(str));
3983 }
3984
3985 void
3986 setkeybinding(unsigned int mod, KeySym ks, enum keyfuncid kfid,
3987     char *spawn_name)
3988 {
3989         int                     i, j;
3990         DNPRINTF(SWM_D_KEY, "setkeybinding: enter %s [%s]\n",
3991             keyfuncs[kfid].name, spawn_name);
3992         /* find existing */
3993         for (i = 0; i < keys_length; i++) {
3994                 if (keys[i].mod == mod && keys[i].keysym == ks) {
3995                         if (kfid == kf_invalid) {
3996                                 /* found: delete */
3997                                 DNPRINTF(SWM_D_KEY,
3998                                     "setkeybinding: delete #%d %s\n",
3999                                     i, keyfuncs[keys[i].funcid].name);
4000                                 free(keys[i].spawn_name);
4001                                 j = keys_length - 1;
4002                                 if (i < j)
4003                                         keys[i] = keys[j];
4004                                 keys_length--;
4005                                 DNPRINTF(SWM_D_KEY, "setkeybinding: leave\n");
4006                                 return;
4007                         } else {
4008                                 /* found: replace */
4009                                 DNPRINTF(SWM_D_KEY,
4010                                     "setkeybinding: replace #%d %s %s\n",
4011                                     i, keyfuncs[keys[i].funcid].name,
4012                                     spawn_name);
4013                                 free(keys[i].spawn_name);
4014                                 keys[i].mod = mod;
4015                                 keys[i].keysym = ks;
4016                                 keys[i].funcid = kfid;
4017                                 keys[i].spawn_name = strdupsafe(spawn_name);
4018                                 DNPRINTF(SWM_D_KEY, "setkeybinding: leave\n");
4019                                 return;
4020                         }
4021                 }
4022         }
4023         if (kfid == kf_invalid) {
4024                 fprintf(stderr,
4025                     "error: setkeybinding: cannot find mod/key combination");
4026                 DNPRINTF(SWM_D_KEY, "setkeybinding: leave\n");
4027                 return;
4028         }
4029         /* not found: add */
4030         if (keys_size == 0 || keys == NULL) {
4031                 keys_size = 4;
4032                 DNPRINTF(SWM_D_KEY, "setkeybinding: init list %d\n", keys_size);
4033                 keys = malloc((size_t)keys_size * sizeof(struct key));
4034                 if (keys == NULL) {
4035                         fprintf(stderr, "malloc failed\n");
4036                         perror(" failed");
4037                         quit(NULL, NULL);
4038                 }
4039         } else if (keys_length == keys_size) {
4040                 keys_size *= 2;
4041                 DNPRINTF(SWM_D_KEY, "setkeybinding: grow list %d\n", keys_size);
4042                 keys = realloc(keys, (size_t)keys_size * sizeof(struct key));
4043                 if (keys == NULL) {
4044                         fprintf(stderr, "realloc failed\n");
4045                         perror(" failed");
4046                         quit(NULL, NULL);
4047                 }
4048         }
4049         if (keys_length < keys_size) {
4050                 j = keys_length++;
4051                 DNPRINTF(SWM_D_KEY, "setkeybinding: add #%d %s %s\n",
4052                     j, keyfuncs[kfid].name, spawn_name);
4053                 keys[j].mod = mod;
4054                 keys[j].keysym = ks;
4055                 keys[j].funcid = kfid;
4056                 keys[j].spawn_name = strdupsafe(spawn_name);
4057         } else {
4058                 fprintf(stderr, "keys array problem?\n");
4059                 if (keys == NULL) {
4060                         fprintf(stderr, "keys array problem\n");
4061                         quit(NULL, NULL);
4062                 }
4063         }
4064         DNPRINTF(SWM_D_KEY, "setkeybinding: leave\n");
4065 }
4066
4067 int
4068 setconfbinding(char *selector, char *value, int flags)
4069 {
4070         enum keyfuncid          kfid;
4071         unsigned int            mod;
4072         KeySym                  ks;
4073         int                     i;
4074         DNPRINTF(SWM_D_KEY, "setconfbinding: enter\n");
4075         if (selector == NULL) {
4076                 DNPRINTF(SWM_D_KEY, "setconfbinding: unbind %s\n", value);
4077                 if (parsekeys(value, mod_key, &mod, &ks) == 0) {
4078                         kfid = kf_invalid;
4079                         setkeybinding(mod, ks, kfid, NULL);
4080                         return (0);
4081                 } else
4082                         return (1);
4083         }
4084         /* search by key function name */
4085         for (kfid = 0; kfid < kf_invalid; (kfid)++) {
4086                 if (strncasecmp(selector, keyfuncs[kfid].name,
4087                     SWM_FUNCNAME_LEN) == 0) {
4088                         DNPRINTF(SWM_D_KEY, "setconfbinding: %s: match\n",
4089                             selector);
4090                         if (parsekeys(value, mod_key, &mod, &ks) == 0) {
4091                                 setkeybinding(mod, ks, kfid, NULL);
4092                                 return (0);
4093                         } else
4094                                 return (1);
4095                 }
4096         }
4097         /* search by custom spawn name */
4098         for (i = 0; i < spawns_length; i++) {
4099                 if (strcasecmp(selector, spawns[i].name) == 0) {
4100                         DNPRINTF(SWM_D_KEY, "setconfbinding: %s: match\n",
4101                             selector);
4102                         if (parsekeys(value, mod_key, &mod, &ks) == 0) {
4103                                 setkeybinding(mod, ks, kf_spawn_custom,
4104                                     spawns[i].name);
4105                                 return (0);
4106                         } else
4107                                 return (1);
4108                 }
4109         }
4110         DNPRINTF(SWM_D_KEY, "setconfbinding: no match\n");
4111         return (1);
4112 }
4113
4114 void
4115 setup_keys(void)
4116 {
4117         setkeybinding(MODKEY,           XK_space,       kf_cycle_layout,NULL);
4118         setkeybinding(MODKEY|ShiftMask, XK_space,       kf_stack_reset, NULL);
4119         setkeybinding(MODKEY,           XK_h,           kf_master_shrink,NULL);
4120         setkeybinding(MODKEY,           XK_l,           kf_master_grow, NULL);
4121         setkeybinding(MODKEY,           XK_comma,       kf_master_add,  NULL);
4122         setkeybinding(MODKEY,           XK_period,      kf_master_del,  NULL);
4123         setkeybinding(MODKEY|ShiftMask, XK_comma,       kf_stack_inc,   NULL);
4124         setkeybinding(MODKEY|ShiftMask, XK_period,      kf_stack_dec,   NULL);
4125         setkeybinding(MODKEY,           XK_Return,      kf_swap_main,   NULL);
4126         setkeybinding(MODKEY,           XK_j,           kf_focus_next,  NULL);
4127         setkeybinding(MODKEY,           XK_k,           kf_focus_prev,  NULL);
4128         setkeybinding(MODKEY|ShiftMask, XK_j,           kf_swap_next,   NULL);
4129         setkeybinding(MODKEY|ShiftMask, XK_k,           kf_swap_prev,   NULL);
4130         setkeybinding(MODKEY|ShiftMask, XK_Return,      kf_spawn_term,  NULL);
4131         setkeybinding(MODKEY,           XK_p,           kf_spawn_custom,        "menu");
4132         setkeybinding(MODKEY|ShiftMask, XK_q,           kf_quit,        NULL);
4133         setkeybinding(MODKEY,           XK_q,           kf_restart,     NULL);
4134         setkeybinding(MODKEY,           XK_m,           kf_focus_main,  NULL);
4135         setkeybinding(MODKEY,           XK_1,           kf_ws_1,        NULL);
4136         setkeybinding(MODKEY,           XK_2,           kf_ws_2,        NULL);
4137         setkeybinding(MODKEY,           XK_3,           kf_ws_3,        NULL);
4138         setkeybinding(MODKEY,           XK_4,           kf_ws_4,        NULL);
4139         setkeybinding(MODKEY,           XK_5,           kf_ws_5,        NULL);
4140         setkeybinding(MODKEY,           XK_6,           kf_ws_6,        NULL);
4141         setkeybinding(MODKEY,           XK_7,           kf_ws_7,        NULL);
4142         setkeybinding(MODKEY,           XK_8,           kf_ws_8,        NULL);
4143         setkeybinding(MODKEY,           XK_9,           kf_ws_9,        NULL);
4144         setkeybinding(MODKEY,           XK_0,           kf_ws_10,       NULL);
4145         setkeybinding(MODKEY,           XK_Right,       kf_ws_next,     NULL);
4146         setkeybinding(MODKEY,           XK_Left,        kf_ws_prev,     NULL);
4147         setkeybinding(MODKEY,           XK_a,           kf_ws_prior,    NULL);
4148         setkeybinding(MODKEY|ShiftMask, XK_Right,       kf_screen_next, NULL);
4149         setkeybinding(MODKEY|ShiftMask, XK_Left,        kf_screen_prev, NULL);
4150         setkeybinding(MODKEY|ShiftMask, XK_1,           kf_mvws_1,      NULL);
4151         setkeybinding(MODKEY|ShiftMask, XK_2,           kf_mvws_2,      NULL);
4152         setkeybinding(MODKEY|ShiftMask, XK_3,           kf_mvws_3,      NULL);
4153         setkeybinding(MODKEY|ShiftMask, XK_4,           kf_mvws_4,      NULL);
4154         setkeybinding(MODKEY|ShiftMask, XK_5,           kf_mvws_5,      NULL);
4155         setkeybinding(MODKEY|ShiftMask, XK_6,           kf_mvws_6,      NULL);
4156         setkeybinding(MODKEY|ShiftMask, XK_7,           kf_mvws_7,      NULL);
4157         setkeybinding(MODKEY|ShiftMask, XK_8,           kf_mvws_8,      NULL);
4158         setkeybinding(MODKEY|ShiftMask, XK_9,           kf_mvws_9,      NULL);
4159         setkeybinding(MODKEY|ShiftMask, XK_0,           kf_mvws_10,     NULL);
4160         setkeybinding(MODKEY,           XK_b,           kf_bar_toggle,  NULL);
4161         setkeybinding(MODKEY,           XK_Tab,         kf_focus_next,  NULL);
4162         setkeybinding(MODKEY|ShiftMask, XK_Tab,         kf_focus_prev,  NULL);
4163         setkeybinding(MODKEY|ShiftMask, XK_x,           kf_wind_kill,   NULL);
4164         setkeybinding(MODKEY,           XK_x,           kf_wind_del,    NULL);
4165         setkeybinding(MODKEY,           XK_s,           kf_spawn_custom,        "screenshot_all");
4166         setkeybinding(MODKEY|ShiftMask, XK_s,           kf_spawn_custom,        "screenshot_wind");
4167         setkeybinding(MODKEY,           XK_t,           kf_float_toggle,NULL);
4168         setkeybinding(MODKEY|ShiftMask, XK_v,           kf_version,     NULL);
4169         setkeybinding(MODKEY|ShiftMask, XK_Delete,      kf_spawn_custom,        "lock");
4170         setkeybinding(MODKEY|ShiftMask, XK_i,           kf_spawn_custom,        "initscr");
4171         setkeybinding(MODKEY,           XK_w,           kf_iconify,     NULL);
4172         setkeybinding(MODKEY|ShiftMask, XK_w,           kf_uniconify,   NULL);
4173         setkeybinding(MODKEY|ShiftMask, XK_r,           kf_raise_toggle,NULL);
4174 #ifdef SWM_DEBUG
4175         setkeybinding(MODKEY|ShiftMask, XK_d,           kf_dumpwins,    NULL);
4176 #endif
4177 }
4178
4179 void
4180 updatenumlockmask(void)
4181 {
4182         unsigned int            i, j;
4183         XModifierKeymap         *modmap;
4184
4185         DNPRINTF(SWM_D_MISC, "updatenumlockmask\n");
4186         numlockmask = 0;
4187         modmap = XGetModifierMapping(display);
4188         for (i = 0; i < 8; i++)
4189                 for (j = 0; j < modmap->max_keypermod; j++)
4190                         if (modmap->modifiermap[i * modmap->max_keypermod + j]
4191                             == XKeysymToKeycode(display, XK_Num_Lock))
4192                                 numlockmask = (1 << i);
4193
4194         XFreeModifiermap(modmap);
4195 }
4196
4197 void
4198 grabkeys(void)
4199 {
4200         unsigned int            i, j, k;
4201         KeyCode                 code;
4202         unsigned int            modifiers[] =
4203             { 0, LockMask, numlockmask, numlockmask | LockMask };
4204
4205         DNPRINTF(SWM_D_MISC, "grabkeys\n");
4206         updatenumlockmask();
4207
4208         for (k = 0; k < ScreenCount(display); k++) {
4209                 if (TAILQ_EMPTY(&screens[k].rl))
4210                         continue;
4211                 XUngrabKey(display, AnyKey, AnyModifier, screens[k].root);
4212                 for (i = 0; i < keys_length; i++) {
4213                         if ((code = XKeysymToKeycode(display, keys[i].keysym)))
4214                                 for (j = 0; j < LENGTH(modifiers); j++)
4215                                         XGrabKey(display, code,
4216                                             keys[i].mod | modifiers[j],
4217                                             screens[k].root, True,
4218                                             GrabModeAsync, GrabModeAsync);
4219                 }
4220         }
4221 }
4222
4223 void
4224 grabbuttons(struct ws_win *win, int focused)
4225 {
4226         unsigned int            i, j;
4227         unsigned int            modifiers[] =
4228             { 0, LockMask, numlockmask, numlockmask|LockMask };
4229
4230         updatenumlockmask();
4231         XUngrabButton(display, AnyButton, AnyModifier, win->id);
4232         if (focused) {
4233                 for (i = 0; i < LENGTH(buttons); i++)
4234                         if (buttons[i].action == client_click)
4235                                 for (j = 0; j < LENGTH(modifiers); j++)
4236                                         XGrabButton(display, buttons[i].button,
4237                                             buttons[i].mask | modifiers[j],
4238                                             win->id, False, BUTTONMASK,
4239                                             GrabModeAsync, GrabModeSync, None,
4240                                             None);
4241         } else
4242                 XGrabButton(display, AnyButton, AnyModifier, win->id, False,
4243                     BUTTONMASK, GrabModeAsync, GrabModeSync, None, None);
4244 }
4245
4246 const char *quirkname[] = {
4247         "NONE",         /* config string for "no value" */
4248         "FLOAT",
4249         "TRANSSZ",
4250         "ANYWHERE",
4251         "XTERM_FONTADJ",
4252         "FULLSCREEN",
4253         "FOCUSPREV",
4254 };
4255
4256 /* SWM_Q_WS: retain '|' for back compat for now (2009-08-11) */
4257 #define SWM_Q_WS                "\n|+ \t"
4258 int
4259 parsequirks(char *qstr, unsigned long *quirk)
4260 {
4261         char                    *cp, *name;
4262         int                     i;
4263
4264         if (quirk == NULL)
4265                 return (1);
4266
4267         cp = qstr;
4268         *quirk = 0;
4269         while ((name = strsep(&cp, SWM_Q_WS)) != NULL) {
4270                 if (cp)
4271                         cp += (long)strspn(cp, SWM_Q_WS);
4272                 for (i = 0; i < LENGTH(quirkname); i++) {
4273                         if (!strncasecmp(name, quirkname[i], SWM_QUIRK_LEN)) {
4274                                 DNPRINTF(SWM_D_QUIRK,
4275                                     "parsequirks: %s\n", name);
4276                                 if (i == 0) {
4277                                         *quirk = 0;
4278                                         return (0);
4279                                 }
4280                                 *quirk |= 1 << (i-1);
4281                                 break;
4282                         }
4283                 }
4284                 if (i >= LENGTH(quirkname)) {
4285                         DNPRINTF(SWM_D_QUIRK,
4286                             "parsequirks: invalid quirk [%s]\n", name);
4287                         return (1);
4288                 }
4289         }
4290         return (0);
4291 }
4292
4293 void
4294 setquirk(const char *class, const char *name, const int quirk)
4295 {
4296         int                     i, j;
4297
4298         /* find existing */
4299         for (i = 0; i < quirks_length; i++) {
4300                 if (!strcmp(quirks[i].class, class) &&
4301                     !strcmp(quirks[i].name, name)) {
4302                         if (!quirk) {
4303                                 /* found: delete */
4304                                 DNPRINTF(SWM_D_QUIRK,
4305                                     "setquirk: delete #%d %s:%s\n",
4306                                     i, quirks[i].class, quirks[i].name);
4307                                 free(quirks[i].class);
4308                                 free(quirks[i].name);
4309                                 j = quirks_length - 1;
4310                                 if (i < j)
4311                                         quirks[i] = quirks[j];
4312                                 quirks_length--;
4313                                 return;
4314                         } else {
4315                                 /* found: replace */
4316                                 DNPRINTF(SWM_D_QUIRK,
4317                                     "setquirk: replace #%d %s:%s\n",
4318                                     i, quirks[i].class, quirks[i].name);
4319                                 free(quirks[i].class);
4320                                 free(quirks[i].name);
4321                                 quirks[i].class = strdup(class);
4322                                 quirks[i].name = strdup(name);
4323                                 quirks[i].quirk = quirk;
4324                                 return;
4325                         }
4326                 }
4327         }
4328         if (!quirk) {
4329                 fprintf(stderr,
4330                     "error: setquirk: cannot find class/name combination");
4331                 return;
4332         }
4333         /* not found: add */
4334         if (quirks_size == 0 || quirks == NULL) {
4335                 quirks_size = 4;
4336                 DNPRINTF(SWM_D_QUIRK, "setquirk: init list %d\n", quirks_size);
4337                 quirks = malloc((size_t)quirks_size * sizeof(struct quirk));
4338                 if (quirks == NULL) {
4339                         fprintf(stderr, "setquirk: malloc failed\n");
4340                         perror(" failed");
4341                         quit(NULL, NULL);
4342                 }
4343         } else if (quirks_length == quirks_size) {
4344                 quirks_size *= 2;
4345                 DNPRINTF(SWM_D_QUIRK, "setquirk: grow list %d\n", quirks_size);
4346                 quirks = realloc(quirks,
4347                     (size_t)quirks_size * sizeof(struct quirk));
4348                 if (quirks == NULL) {
4349                         fprintf(stderr, "setquirk: realloc failed\n");
4350                         perror(" failed");
4351                         quit(NULL, NULL);
4352                 }
4353         }
4354         if (quirks_length < quirks_size) {
4355                 DNPRINTF(SWM_D_QUIRK, "setquirk: add %d\n", quirks_length);
4356                 j = quirks_length++;
4357                 quirks[j].class = strdup(class);
4358                 quirks[j].name = strdup(name);
4359                 quirks[j].quirk = quirk;
4360         } else {
4361                 fprintf(stderr, "quirks array problem?\n");
4362                 if (quirks == NULL) {
4363                         fprintf(stderr, "quirks array problem!\n");
4364                         quit(NULL, NULL);
4365                 }
4366         }
4367 }
4368
4369 int
4370 setconfquirk(char *selector, char *value, int flags)
4371 {
4372         char                    *cp, *class, *name;
4373         int                     retval;
4374         unsigned long           quirks;
4375         if (selector == NULL)
4376                 return (0);
4377         if ((cp = strchr(selector, ':')) == NULL)
4378                 return (0);
4379         *cp = '\0';
4380         class = selector;
4381         name = cp + 1;
4382         if ((retval = parsequirks(value, &quirks)) == 0)
4383                 setquirk(class, name, quirks);
4384         return (retval);
4385 }
4386
4387 void
4388 setup_quirks(void)
4389 {
4390         setquirk("MPlayer",             "xv",           SWM_Q_FLOAT | SWM_Q_FULLSCREEN | SWM_Q_FOCUSPREV);
4391         setquirk("OpenOffice.org 3.2",  "VCLSalFrame",  SWM_Q_FLOAT);
4392         setquirk("Firefox-bin",         "firefox-bin",  SWM_Q_TRANSSZ);
4393         setquirk("Firefox",             "Dialog",       SWM_Q_FLOAT);
4394         setquirk("Gimp",                "gimp",         SWM_Q_FLOAT | SWM_Q_ANYWHERE);
4395         setquirk("XTerm",               "xterm",        SWM_Q_XTERM_FONTADJ);
4396         setquirk("xine",                "Xine Window",  SWM_Q_FLOAT | SWM_Q_ANYWHERE);
4397         setquirk("Xitk",                "Xitk Combo",   SWM_Q_FLOAT | SWM_Q_ANYWHERE);
4398         setquirk("xine",                "xine Panel",   SWM_Q_FLOAT | SWM_Q_ANYWHERE);
4399         setquirk("Xitk",                "Xine Window",  SWM_Q_FLOAT | SWM_Q_ANYWHERE);
4400         setquirk("xine",                "xine Video Fullscreen Window", SWM_Q_FULLSCREEN | SWM_Q_FLOAT);
4401         setquirk("pcb",                 "pcb",          SWM_Q_FLOAT);
4402         setquirk("SDL_App",             "SDL_App",      SWM_Q_FLOAT | SWM_Q_FULLSCREEN);
4403 }
4404
4405 /* conf file stuff */
4406 #define SWM_CONF_FILE   "scrotwm.conf"
4407
4408 enum    { SWM_S_BAR_DELAY, SWM_S_BAR_ENABLED, SWM_S_BAR_BORDER_WIDTH,
4409           SWM_S_STACK_ENABLED, SWM_S_CLOCK_ENABLED, SWM_S_CLOCK_FORMAT,
4410           SWM_S_CYCLE_EMPTY, SWM_S_CYCLE_VISIBLE, SWM_S_SS_ENABLED,
4411           SWM_S_TERM_WIDTH, SWM_S_TITLE_CLASS_ENABLED,
4412           SWM_S_TITLE_NAME_ENABLED, SWM_S_WINDOW_NAME_ENABLED,
4413           SWM_S_FOCUS_MODE, SWM_S_DISABLE_BORDER, SWM_S_BORDER_WIDTH,
4414           SWM_S_BAR_FONT, SWM_S_BAR_ACTION, SWM_S_SPAWN_TERM,
4415           SWM_S_SS_APP, SWM_S_DIALOG_RATIO, SWM_S_BAR_AT_BOTTOM
4416         };
4417
4418 int
4419 setconfvalue(char *selector, char *value, int flags)
4420 {
4421         switch (flags) {
4422         case SWM_S_BAR_DELAY:
4423                 bar_delay = atoi(value);
4424                 break;
4425         case SWM_S_BAR_ENABLED:
4426                 bar_enabled = atoi(value);
4427                 break;
4428         case SWM_S_BAR_BORDER_WIDTH:
4429                 bar_border_width = atoi(value);
4430                 break;
4431         case SWM_S_BAR_AT_BOTTOM:
4432                 bar_at_bottom = atoi(value);
4433                 break;
4434         case SWM_S_STACK_ENABLED:
4435                 stack_enabled = atoi(value);
4436                 break;
4437         case SWM_S_CLOCK_ENABLED:
4438                 clock_enabled = atoi(value);
4439                 break;
4440         case SWM_S_CLOCK_FORMAT:
4441 #ifndef SWM_DENY_CLOCK_FORMAT
4442                 free(clock_format);
4443                 if ((clock_format = strdup(value)) == NULL)
4444                         err(1, "setconfvalue: clock_format");
4445 #endif
4446                 break;
4447         case SWM_S_CYCLE_EMPTY:
4448                 cycle_empty = atoi(value);
4449                 break;
4450         case SWM_S_CYCLE_VISIBLE:
4451                 cycle_visible = atoi(value);
4452                 break;
4453         case SWM_S_SS_ENABLED:
4454                 ss_enabled = atoi(value);
4455                 break;
4456         case SWM_S_TERM_WIDTH:
4457                 term_width = atoi(value);
4458                 break;
4459         case SWM_S_TITLE_CLASS_ENABLED:
4460                 title_class_enabled = atoi(value);
4461                 break;
4462         case SWM_S_WINDOW_NAME_ENABLED:
4463                 window_name_enabled = atoi(value);
4464                 break;
4465         case SWM_S_TITLE_NAME_ENABLED:
4466                 title_name_enabled = atoi(value);
4467                 break;
4468         case SWM_S_FOCUS_MODE:
4469                 if (!strcmp(value, "default"))
4470                         focus_mode = SWM_FOCUS_DEFAULT;
4471                 else if (!strcmp(value, "follow_cursor"))
4472                         focus_mode = SWM_FOCUS_FOLLOW;
4473                 else if (!strcmp(value, "synergy"))
4474                         focus_mode = SWM_FOCUS_SYNERGY;
4475                 else
4476                         err(1, "focus_mode");
4477                 break;
4478         case SWM_S_DISABLE_BORDER:
4479                 disable_border = atoi(value);
4480                 break;
4481         case SWM_S_BORDER_WIDTH:
4482                 border_width = atoi(value);
4483                 break;
4484         case SWM_S_BAR_FONT:
4485                 free(bar_fonts[0]);
4486                 if ((bar_fonts[0] = strdup(value)) == NULL)
4487                         err(1, "setconfvalue: bar_font");
4488                 break;
4489         case SWM_S_BAR_ACTION:
4490                 free(bar_argv[0]);
4491                 if ((bar_argv[0] = strdup(value)) == NULL)
4492                         err(1, "setconfvalue: bar_action");
4493                 break;
4494         case SWM_S_SPAWN_TERM:
4495                 free(spawn_term[0]);
4496                 if ((spawn_term[0] = strdup(value)) == NULL)
4497                         err(1, "setconfvalue: spawn_term");
4498                 break;
4499         case SWM_S_SS_APP:
4500                 break;
4501         case SWM_S_DIALOG_RATIO:
4502                 dialog_ratio = atof(value);
4503                 if (dialog_ratio > 1.0 || dialog_ratio <= .3)
4504                         dialog_ratio = .6;
4505                 break;
4506         default:
4507                 return (1);
4508         }
4509         return (0);
4510 }
4511
4512 int
4513 setconfmodkey(char *selector, char *value, int flags)
4514 {
4515         if (!strncasecmp(value, "Mod1", strlen("Mod1")))
4516                 update_modkey(Mod1Mask);
4517         else if (!strncasecmp(value, "Mod2", strlen("Mod2")))
4518                 update_modkey(Mod2Mask);
4519         else if (!strncasecmp(value, "Mod3", strlen("Mod3")))
4520                 update_modkey(Mod3Mask);
4521         else if (!strncasecmp(value, "Mod4", strlen("Mod4")))
4522                 update_modkey(Mod4Mask);
4523         else
4524                 return (1);
4525         return (0);
4526 }
4527
4528 int
4529 setconfcolor(char *selector, char *value, int flags)
4530 {
4531         setscreencolor(value, ((selector == NULL)?-1:atoi(selector)), flags);
4532         return (0);
4533 }
4534
4535 int
4536 setconfregion(char *selector, char *value, int flags)
4537 {
4538         custom_region(value);
4539         return (0);
4540 }
4541
4542 int
4543 setautorun(char *selector, char *value, int flags)
4544 {
4545         int                     ws_id;
4546         char                    s[1024];
4547         char                    *ap, *sp = s;
4548         union arg               a;
4549         int                     argc = 0;
4550         long                    pid;
4551         struct pid_e            *p;
4552
4553         if (getenv("SWM_STARTED"))
4554                 return (0);
4555
4556         bzero(s, sizeof s);
4557         if (sscanf(value, "ws[%d]:%1023c", &ws_id, s) != 2)
4558                 errx(1, "invalid autorun entry, should be 'ws[<idx>]:command'\n");
4559         ws_id--;
4560         if (ws_id < 0 || ws_id >= SWM_WS_MAX)
4561                 errx(1, "autorun: invalid workspace %d\n", ws_id + 1);
4562
4563         /*
4564          * This is a little intricate
4565          *
4566          * If the pid already exists we simply reuse it because it means it was
4567          * used before AND not claimed by manage_window.  We get away with
4568          * altering it in the parent after INSERT because this can not be a race
4569          */
4570         a.argv = NULL;
4571         while ((ap = strsep(&sp, " \t")) != NULL) {
4572                 if (*ap == '\0')
4573                         continue;
4574                 DNPRINTF(SWM_D_SPAWN, "setautorun: arg [%s]\n", ap);
4575                 argc++;
4576                 if ((a.argv = realloc(a.argv, argc * sizeof(char *))) == NULL)
4577                         err(1, "setautorun: realloc");
4578                 a.argv[argc - 1] = ap;
4579         }
4580
4581         if ((a.argv = realloc(a.argv, (argc + 1) * sizeof(char *))) == NULL)
4582                 err(1, "setautorun: realloc");
4583         a.argv[argc] = NULL;
4584
4585         if ((pid = fork()) == 0) {
4586                 spawn(ws_id, &a, 1);
4587                 /* NOTREACHED */
4588                 _exit(1);
4589         }
4590         free(a.argv);
4591
4592         /* parent */
4593         p = find_pid(pid);
4594         if (p == NULL) {
4595                 p = calloc(1, sizeof *p);
4596                 if (p == NULL)
4597                         return (1);
4598                 TAILQ_INSERT_TAIL(&pidlist, p, entry);
4599         }
4600
4601         p->pid = pid;
4602         p->ws = ws_id;
4603
4604         return (0);
4605 }
4606
4607 int
4608 setlayout(char *selector, char *value, int flags)
4609 {
4610         int                     ws_id, st, i, x, mg, ma, si, raise;
4611         char                    s[1024];
4612         struct workspace        *ws;
4613
4614         if (getenv("SWM_STARTED"))
4615                 return (0);
4616
4617         bzero(s, sizeof s);
4618         if (sscanf(value, "ws[%d]:%d:%d:%d:%d:%1023c",
4619             &ws_id, &mg, &ma, &si, &raise, s) != 6)
4620                 errx(1, "invalid layout entry, should be 'ws[<idx>]:"
4621                     "<master_grow>:<master_add>:<stack_inc>:<always_raise>:"
4622                     "<type>'\n");
4623         ws_id--;
4624         if (ws_id < 0 || ws_id >= SWM_WS_MAX)
4625                 errx(1, "layout: invalid workspace %d\n", ws_id + 1);
4626
4627         if (!strcasecmp(s, "vertical"))
4628                 st = SWM_V_STACK;
4629         else if (!strcasecmp(s, "horizontal"))
4630                 st = SWM_H_STACK;
4631         else if (!strcasecmp(s, "fullscreen"))
4632                 st = SWM_MAX_STACK;
4633         else
4634                 errx(1, "invalid layout entry, should be 'ws[<idx>]:"
4635                     "<master_grow>:<master_add>:<stack_inc>:<always_raise>:"
4636                     "<type>'\n");
4637
4638         for (i = 0; i < ScreenCount(display); i++) {
4639                 ws = (struct workspace *)&screens[i].ws;
4640                 ws[ws_id].cur_layout = &layouts[st];
4641
4642                 ws[ws_id].always_raise = raise;
4643                 if (st == SWM_MAX_STACK)
4644                         continue;
4645
4646                 /* master grow */
4647                 for (x = 0; x < abs(mg); x++) {
4648                         ws[ws_id].cur_layout->l_config(&ws[ws_id],
4649                             mg >= 0 ?  SWM_ARG_ID_MASTERGROW :
4650                             SWM_ARG_ID_MASTERSHRINK);
4651                         stack();
4652                 }
4653                 /* master add */
4654                 for (x = 0; x < abs(ma); x++) {
4655                         ws[ws_id].cur_layout->l_config(&ws[ws_id],
4656                             ma >= 0 ?  SWM_ARG_ID_MASTERADD :
4657                             SWM_ARG_ID_MASTERDEL);
4658                         stack();
4659                 }
4660                 /* stack inc */
4661                 for (x = 0; x < abs(si); x++) {
4662                         ws[ws_id].cur_layout->l_config(&ws[ws_id],
4663                             si >= 0 ?  SWM_ARG_ID_STACKINC :
4664                             SWM_ARG_ID_STACKDEC);
4665                         stack();
4666                 }
4667         }
4668
4669         return (0);
4670 }
4671
4672 /* config options */
4673 struct config_option {
4674         char                    *optname;
4675         int                     (*func)(char*, char*, int);
4676         int                     funcflags;
4677 };
4678 struct config_option configopt[] = {
4679         { "bar_enabled",                setconfvalue,   SWM_S_BAR_ENABLED },
4680         { "bar_at_bottom",              setconfvalue,   SWM_S_BAR_AT_BOTTOM },
4681         { "bar_border",                 setconfcolor,   SWM_S_COLOR_BAR_BORDER },
4682         { "bar_border_width",           setconfvalue,   SWM_S_BAR_BORDER_WIDTH },
4683         { "bar_color",                  setconfcolor,   SWM_S_COLOR_BAR },
4684         { "bar_font_color",             setconfcolor,   SWM_S_COLOR_BAR_FONT },
4685         { "bar_font",                   setconfvalue,   SWM_S_BAR_FONT },
4686         { "bar_action",                 setconfvalue,   SWM_S_BAR_ACTION },
4687         { "bar_delay",                  setconfvalue,   SWM_S_BAR_DELAY },
4688         { "bind",                       setconfbinding, 0 },
4689         { "stack_enabled",              setconfvalue,   SWM_S_STACK_ENABLED },
4690         { "clock_enabled",              setconfvalue,   SWM_S_CLOCK_ENABLED },
4691         { "clock_format",               setconfvalue,   SWM_S_CLOCK_FORMAT },
4692         { "color_focus",                setconfcolor,   SWM_S_COLOR_FOCUS },
4693         { "color_unfocus",              setconfcolor,   SWM_S_COLOR_UNFOCUS },
4694         { "cycle_empty",                setconfvalue,   SWM_S_CYCLE_EMPTY },
4695         { "cycle_visible",              setconfvalue,   SWM_S_CYCLE_VISIBLE },
4696         { "dialog_ratio",               setconfvalue,   SWM_S_DIALOG_RATIO },
4697         { "modkey",                     setconfmodkey,  0 },
4698         { "program",                    setconfspawn,   0 },
4699         { "quirk",                      setconfquirk,   0 },
4700         { "region",                     setconfregion,  0 },
4701         { "spawn_term",                 setconfvalue,   SWM_S_SPAWN_TERM },
4702         { "screenshot_enabled",         setconfvalue,   SWM_S_SS_ENABLED },
4703         { "screenshot_app",             setconfvalue,   SWM_S_SS_APP },
4704         { "window_name_enabled",        setconfvalue,   SWM_S_WINDOW_NAME_ENABLED },
4705         { "term_width",                 setconfvalue,   SWM_S_TERM_WIDTH },
4706         { "title_class_enabled",        setconfvalue,   SWM_S_TITLE_CLASS_ENABLED },
4707         { "title_name_enabled",         setconfvalue,   SWM_S_TITLE_NAME_ENABLED },
4708         { "focus_mode",                 setconfvalue,   SWM_S_FOCUS_MODE },
4709         { "disable_border",             setconfvalue,   SWM_S_DISABLE_BORDER },
4710         { "border_width",               setconfvalue,   SWM_S_BORDER_WIDTH },
4711         { "autorun",                    setautorun,     0 },
4712         { "layout",                     setlayout,      0 },
4713 };
4714
4715
4716 int
4717 conf_load(char *filename)
4718 {
4719         FILE                    *config;
4720         char                    *line, *cp, *optsub, *optval;
4721         size_t                  linelen, lineno = 0;
4722         int                     wordlen, i, optind;
4723         struct config_option    *opt;
4724
4725         DNPRINTF(SWM_D_CONF, "conf_load begin\n");
4726
4727         if (filename == NULL) {
4728                 fprintf(stderr, "conf_load: no filename\n");
4729                 return (1);
4730         }
4731         if ((config = fopen(filename, "r")) == NULL) {
4732                 warn("conf_load: fopen");
4733                 return (1);
4734         }
4735
4736         while (!feof(config)) {
4737                 if ((line = fparseln(config, &linelen, &lineno, NULL, 0))
4738                     == NULL) {
4739                         if (ferror(config))
4740                                 err(1, "%s", filename);
4741                         else
4742                                 continue;
4743                 }
4744                 cp = line;
4745                 cp += strspn(cp, " \t\n"); /* eat whitespace */
4746                 if (cp[0] == '\0') {
4747                         /* empty line */
4748                         free(line);
4749                         continue;
4750                 }
4751                 /* get config option */
4752                 wordlen = strcspn(cp, "=[ \t\n");
4753                 if (wordlen == 0) {
4754                         warnx("%s: line %zd: no option found",
4755                             filename, lineno);
4756                         return (1);
4757                 }
4758                 optind = -1;
4759                 for (i = 0; i < LENGTH(configopt); i++) {
4760                         opt = &configopt[i];
4761                         if (!strncasecmp(cp, opt->optname, wordlen) &&
4762                             strlen(opt->optname) == wordlen) {
4763                                 optind = i;
4764                                 break;
4765                         }
4766                 }
4767                 if (optind == -1) {
4768                         warnx("%s: line %zd: unknown option %.*s",
4769                             filename, lineno, wordlen, cp);
4770                         return (1);
4771                 }
4772                 cp += wordlen;
4773                 cp += strspn(cp, " \t\n"); /* eat whitespace */
4774                 /* get [selector] if any */
4775                 optsub = NULL;
4776                 if (*cp == '[') {
4777                         cp++;
4778                         wordlen = strcspn(cp, "]");
4779                         if (*cp != ']') {
4780                                 if (wordlen == 0) {
4781                                         warnx("%s: line %zd: syntax error",
4782                                             filename, lineno);
4783                                         return (1);
4784                                 }
4785                                 asprintf(&optsub, "%.*s", wordlen, cp);
4786                         }
4787                         cp += wordlen;
4788                         cp += strspn(cp, "] \t\n"); /* eat trailing */
4789                 }
4790                 cp += strspn(cp, "= \t\n"); /* eat trailing */
4791                 /* get RHS value */
4792                 optval = strdup(cp);
4793                 /* call function to deal with it all */
4794                 if (configopt[optind].func(optsub, optval,
4795                     configopt[optind].funcflags) != 0) {
4796                         fprintf(stderr, "%s line %zd: %s\n",
4797                             filename, lineno, line);
4798                         errx(1, "%s: line %zd: invalid data for %s",
4799                             filename, lineno, configopt[optind].optname);
4800                 }
4801                 free(optval);
4802                 free(optsub);
4803                 free(line);
4804         }
4805
4806         fclose(config);
4807         DNPRINTF(SWM_D_CONF, "conf_load end\n");
4808
4809         return (0);
4810 }
4811
4812 void
4813 set_child_transient(struct ws_win *win, Window *trans)
4814 {
4815         struct ws_win           *parent, *w;
4816         XWMHints                *wmh = NULL;
4817         struct swm_region       *r;
4818         struct workspace        *ws;
4819
4820         parent = find_window(win->transient);
4821         if (parent)
4822                 parent->child_trans = win;
4823         else {
4824                 DNPRINTF(SWM_D_MISC, "set_child_transient: parent doesn't exist"
4825                     " for %lu trans %lu\n", win->id, win->transient);
4826
4827                 if (win->hints == NULL) {
4828                         fprintf(stderr, "no hints for %lu\n", win->id);
4829                         return;
4830                 }
4831
4832                 r = root_to_region(win->wa.root);
4833                 ws = r->ws;
4834                 /* parent doen't exist in our window list */
4835                 TAILQ_FOREACH(w, &ws->winlist, entry) {
4836                         if (wmh)
4837                                 XFree(wmh);
4838
4839                         if ((wmh = XGetWMHints(display, w->id)) == NULL) {
4840                                 fprintf(stderr, "can't get hints for %lu\n",
4841                                     w->id);
4842                                 continue;
4843                         }
4844
4845                         if (win->hints->window_group != wmh->window_group)
4846                                 continue;
4847
4848                         w->child_trans = win;
4849                         win->transient = w->id;
4850                         *trans = w->id;
4851                         DNPRINTF(SWM_D_MISC, "set_child_transient: asjusting "
4852                             "transient to %lu\n", win->transient);
4853                         break;
4854                 }
4855         }
4856
4857         if (wmh)
4858                 XFree(wmh);
4859 }
4860
4861 long
4862 window_get_pid(Window win)
4863 {
4864         Atom                    actual_type_return;
4865         int                     actual_format_return = 0;
4866         unsigned long           nitems_return = 0;
4867         unsigned long           bytes_after_return = 0;
4868         long                    *pid = NULL;
4869         long                    ret = 0;
4870         const char              *errstr;
4871         unsigned char           *prop = NULL;
4872
4873         if (XGetWindowProperty(display, win,
4874             XInternAtom(display, "_NET_WM_PID", False), 0, 1, False,
4875             XA_CARDINAL, &actual_type_return, &actual_format_return,
4876             &nitems_return, &bytes_after_return,
4877             (unsigned char**)(void*)&pid) != Success)
4878                 goto tryharder;
4879         if (actual_type_return != XA_CARDINAL)
4880                 goto tryharder;
4881         if (pid == NULL)
4882                 goto tryharder;
4883
4884         ret = *pid;
4885         XFree(pid);
4886
4887         return (ret);
4888
4889 tryharder:
4890         if (XGetWindowProperty(display, win,
4891             XInternAtom(display, "_SWM_PID", False), 0, SWM_PROPLEN, False,
4892             XA_STRING, &actual_type_return, &actual_format_return,
4893             &nitems_return, &bytes_after_return, &prop) != Success)
4894                 return (0);
4895         if (actual_type_return != XA_STRING)
4896                 return (0);
4897         if (prop == NULL)
4898                 return (0);
4899
4900         ret = strtonum(prop, 0, UINT_MAX, &errstr);
4901         /* ignore error because strtonum returns 0 anyway */
4902         XFree(prop);
4903
4904         return (ret);
4905 }
4906
4907 struct ws_win *
4908 manage_window(Window id)
4909 {
4910         Window                  trans = 0;
4911         struct workspace        *ws;
4912         struct ws_win           *win, *ww;
4913         int                     format, i, ws_idx, n, border_me = 0;
4914         unsigned long           nitems, bytes;
4915         Atom                    ws_idx_atom = 0, type;
4916         Atom                    *prot = NULL, *pp;
4917         unsigned char           ws_idx_str[SWM_PROPLEN], *prop = NULL;
4918         struct swm_region       *r;
4919         long                    mask;
4920         const char              *errstr;
4921         XWindowChanges          wc;
4922         struct pid_e            *p;
4923
4924         if ((win = find_window(id)) != NULL)
4925                 return (win);   /* already being managed */
4926
4927         /* see if we are on the unmanaged list */
4928         if ((win = find_unmanaged_window(id)) != NULL) {
4929                 DNPRINTF(SWM_D_MISC, "manage previously unmanaged window "
4930                     "%lu\n", win->id);
4931                 TAILQ_REMOVE(&win->ws->unmanagedlist, win, entry);
4932                 if (win->transient) {
4933                         set_child_transient(win, &trans);
4934                 } if (trans && (ww = find_window(trans)))
4935                         TAILQ_INSERT_AFTER(&win->ws->winlist, ww, win, entry);
4936                 else
4937                         TAILQ_INSERT_TAIL(&win->ws->winlist, win, entry);
4938                 ewmh_update_actions(win);
4939                 return (win);
4940         }
4941
4942         if ((win = calloc(1, sizeof(struct ws_win))) == NULL)
4943                 errx(1, "calloc: failed to allocate memory for new window");
4944
4945         win->id = id;
4946
4947         /* see if we need to override the workspace */
4948         p = find_pid(window_get_pid(id));
4949
4950         /* Get all the window data in one shot */
4951         ws_idx_atom = XInternAtom(display, "_SWM_WS", False);
4952         if (ws_idx_atom) {
4953                 XGetWindowProperty(display, id, ws_idx_atom, 0, SWM_PROPLEN,
4954                     False, XA_STRING, &type, &format, &nitems, &bytes, &prop);
4955         }
4956         XGetWindowAttributes(display, id, &win->wa);
4957         XGetWMNormalHints(display, id, &win->sh, &mask);
4958         win->hints = XGetWMHints(display, id);
4959         XGetTransientForHint(display, id, &trans);
4960         if (trans) {
4961                 win->transient = trans;
4962                 set_child_transient(win, &trans);
4963                 DNPRINTF(SWM_D_MISC, "manage_window: win %lu transient %lu\n",
4964                     win->id, win->transient);
4965         }
4966
4967         /* get supported protocols */
4968         if (XGetWMProtocols(display, id, &prot, &n)) {
4969                 for (i = 0, pp = prot; i < n; i++, pp++) {
4970                         if (*pp == takefocus)
4971                                 win->take_focus = 1;
4972                         if (*pp == adelete)
4973                                 win->can_delete = 1;
4974                 }
4975                 if (prot)
4976                         XFree(prot);
4977         }
4978
4979         win->iconic = get_iconic(win);
4980
4981         /*
4982          * Figure out where to put the window. If it was previously assigned to
4983          * a workspace (either by spawn() or manually moving), and isn't
4984          * transient, * put it in the same workspace
4985          */
4986         r = root_to_region(win->wa.root);
4987         if (p) {
4988                 ws = &r->s->ws[p->ws];
4989                 TAILQ_REMOVE(&pidlist, p, entry);
4990                 free(p);
4991                 p = NULL;
4992         } else if (prop && win->transient == 0) {
4993                 DNPRINTF(SWM_D_PROP, "got property _SWM_WS=%s\n", prop);
4994                 ws_idx = strtonum(prop, 0, 9, &errstr);
4995                 if (errstr) {
4996                         DNPRINTF(SWM_D_EVENT, "window idx is %s: %s",
4997                             errstr, prop);
4998                 }
4999                 ws = &r->s->ws[ws_idx];
5000         } else {
5001                 ws = r->ws;
5002                 /* this should launch transients in the same ws as parent */
5003                 if (id && trans)
5004                         if ((ww = find_window(trans)) != NULL)
5005                                 if (ws->r) {
5006                                         ws = ww->ws;
5007                                         if (ww->ws->r)
5008                                                 r = ww->ws->r;
5009                                         else
5010                                                 fprintf(stderr,
5011                                                     "fix this bug mcbride\n");
5012                                         border_me = 1;
5013                                 }
5014         }
5015
5016         /* set up the window layout */
5017         win->id = id;
5018         win->ws = ws;
5019         win->s = r->s;  /* this never changes */
5020         if (trans && (ww = find_window(trans)))
5021                 TAILQ_INSERT_AFTER(&ws->winlist, ww, win, entry);
5022         else
5023                 TAILQ_INSERT_TAIL(&ws->winlist, win, entry);
5024
5025         win->g.w = win->wa.width;
5026         win->g.h = win->wa.height;
5027         win->g.x = win->wa.x;
5028         win->g.y = win->wa.y;
5029         win->g_floatvalid = 0;
5030         win->floatmaxed = 0;
5031         win->ewmh_flags = 0;
5032
5033         /* Set window properties so we can remember this after reincarnation */
5034         if (ws_idx_atom && prop == NULL &&
5035             snprintf(ws_idx_str, SWM_PROPLEN, "%d", ws->idx) < SWM_PROPLEN) {
5036                 DNPRINTF(SWM_D_PROP, "setting property _SWM_WS to %s\n",
5037                     ws_idx_str);
5038                 XChangeProperty(display, win->id, ws_idx_atom, XA_STRING, 8,
5039                     PropModeReplace, ws_idx_str, SWM_PROPLEN);
5040         }
5041         if (prop)
5042                 XFree(prop);
5043
5044         ewmh_autoquirk(win);
5045
5046         if (XGetClassHint(display, win->id, &win->ch)) {
5047                 DNPRINTF(SWM_D_CLASS, "class: %s name: %s\n",
5048                     win->ch.res_class, win->ch.res_name);
5049
5050                 /* java is retarded so treat it special */
5051                 if (strstr(win->ch.res_name, "sun-awt")) {
5052                         win->java = 1;
5053                         border_me = 1;
5054                 }
5055
5056                 for (i = 0; i < quirks_length; i++){
5057                         if (!strcmp(win->ch.res_class, quirks[i].class) &&
5058                             !strcmp(win->ch.res_name, quirks[i].name)) {
5059                                 DNPRINTF(SWM_D_CLASS, "found: %s name: %s\n",
5060                                     win->ch.res_class, win->ch.res_name);
5061                                 if (quirks[i].quirk & SWM_Q_FLOAT) {
5062                                         win->floating = 1;
5063                                         border_me = 1;
5064                                 }
5065                                 win->quirks = quirks[i].quirk;
5066                         }
5067                 }
5068         }
5069
5070         /* alter window position if quirky */
5071         if (win->quirks & SWM_Q_ANYWHERE) {
5072                 win->manual = 1; /* don't center the quirky windows */
5073                 bzero(&wc, sizeof wc);
5074                 mask = 0;
5075                 if (bar_enabled && win->g.y < bar_height) {
5076                         win->g.y = wc.y = bar_height;
5077                         mask |= CWY;
5078                 }
5079                 if (win->g.w + win->g.x > WIDTH(r)) {
5080                         win->g.x = wc.x = WIDTH(r) - win->g.w - 2;
5081                         mask |= CWX;
5082                 }
5083                 border_me = 1;
5084         }
5085
5086         /* Reset font sizes (the bruteforce way; no default keybinding). */
5087         if (win->quirks & SWM_Q_XTERM_FONTADJ) {
5088                 for (i = 0; i < SWM_MAX_FONT_STEPS; i++)
5089                         fake_keypress(win, XK_KP_Subtract, ShiftMask);
5090                 for (i = 0; i < SWM_MAX_FONT_STEPS; i++)
5091                         fake_keypress(win, XK_KP_Add, ShiftMask);
5092         }
5093
5094         ewmh_get_win_state(win);
5095         ewmh_update_actions(win);
5096         ewmh_update_win_state(win, None, _NET_WM_STATE_REMOVE);
5097
5098         /* border me */
5099         if (border_me) {
5100                 bzero(&wc, sizeof wc);
5101                 wc.border_width = border_width;
5102                 mask = CWBorderWidth;
5103                 XConfigureWindow(display, win->id, mask, &wc);
5104         }
5105
5106         XSelectInput(display, id, EnterWindowMask | FocusChangeMask |
5107             PropertyChangeMask | StructureNotifyMask);
5108
5109         /* floaters need to be mapped if they are in the current workspace */
5110         if ((win->floating || win->transient) && (ws->idx == r->ws->idx))
5111                 XMapRaised(display, win->id);
5112
5113         return (win);
5114 }
5115
5116 void
5117 free_window(struct ws_win *win)
5118 {
5119         DNPRINTF(SWM_D_MISC, "free_window:  %lu\n", win->id);
5120
5121         if (win == NULL)
5122                 return;
5123
5124         /* needed for restart wm */
5125         set_win_state(win, WithdrawnState);
5126
5127         TAILQ_REMOVE(&win->ws->unmanagedlist, win, entry);
5128
5129         if (win->ch.res_class)
5130                 XFree(win->ch.res_class);
5131         if (win->ch.res_name)
5132                 XFree(win->ch.res_name);
5133
5134         kill_refs(win);
5135
5136         /* paint memory */
5137         memset(win, 0xff, sizeof *win); /* XXX kill later */
5138
5139         free(win);
5140 }
5141
5142 void
5143 unmanage_window(struct ws_win *win)
5144 {
5145         struct ws_win           *parent;
5146
5147         if (win == NULL)
5148                 return;
5149
5150         DNPRINTF(SWM_D_MISC, "unmanage_window:  %lu\n", win->id);
5151
5152         if (win->transient) {
5153                 parent = find_window(win->transient);
5154                 if (parent)
5155                         parent->child_trans = NULL;
5156         }
5157
5158         /* focus on root just in case */
5159         XSetInputFocus(display, PointerRoot, PointerRoot, CurrentTime);
5160
5161         focus_prev(win);
5162
5163         TAILQ_REMOVE(&win->ws->winlist, win, entry);
5164         TAILQ_INSERT_TAIL(&win->ws->unmanagedlist, win, entry);
5165
5166         kill_refs(win);
5167 }
5168
5169 void
5170 focus_magic(struct ws_win *win)
5171 {
5172         DNPRINTF(SWM_D_FOCUS, "focus_magic: %lu\n", WINID(win));
5173
5174         if (win == NULL)
5175                 return;
5176
5177         if (win->child_trans) {
5178                 /* win = parent & has a transient so focus on that */
5179                 if (win->java) {
5180                         focus_win(win->child_trans);
5181                         if (win->child_trans->take_focus)
5182                                 client_msg(win, takefocus);
5183                 } else {
5184                         /* make sure transient hasn't dissapeared */
5185                         if (validate_win(win->child_trans) == 0) {
5186                                 focus_win(win->child_trans);
5187                                 if (win->child_trans->take_focus)
5188                                         client_msg(win->child_trans, takefocus);
5189                         } else {
5190                                 win->child_trans = NULL;
5191                                 focus_win(win);
5192                                 if (win->take_focus)
5193                                         client_msg(win, takefocus);
5194                         }
5195                 }
5196         } else {
5197                 /* regular focus */
5198                 focus_win(win);
5199                 if (win->take_focus)
5200                         client_msg(win, takefocus);
5201         }
5202 }
5203
5204 void
5205 expose(XEvent *e)
5206 {
5207         DNPRINTF(SWM_D_EVENT, "expose: window: %lu\n", e->xexpose.window);
5208 }
5209
5210 void
5211 keypress(XEvent *e)
5212 {
5213         unsigned int            i;
5214         KeySym                  keysym;
5215         XKeyEvent               *ev = &e->xkey;
5216
5217         DNPRINTF(SWM_D_EVENT, "keypress: window: %lu\n", ev->window);
5218
5219         keysym = XKeycodeToKeysym(display, (KeyCode)ev->keycode, 0);
5220         for (i = 0; i < keys_length; i++)
5221                 if (keysym == keys[i].keysym
5222                     && CLEANMASK(keys[i].mod) == CLEANMASK(ev->state)
5223                     && keyfuncs[keys[i].funcid].func) {
5224                         if (keys[i].funcid == kf_spawn_custom)
5225                                 spawn_custom(
5226                                     root_to_region(ev->root),
5227                                     &(keyfuncs[keys[i].funcid].args),
5228                                     keys[i].spawn_name
5229                                     );
5230                         else
5231                                 keyfuncs[keys[i].funcid].func(
5232                                     root_to_region(ev->root),
5233                                     &(keyfuncs[keys[i].funcid].args)
5234                                     );
5235                 }
5236 }
5237
5238 void
5239 buttonpress(XEvent *e)
5240 {
5241         struct ws_win           *win;
5242         int                     i, action;
5243         XButtonPressedEvent     *ev = &e->xbutton;
5244
5245         DNPRINTF(SWM_D_EVENT, "buttonpress: window: %lu\n", ev->window);
5246
5247         action = root_click;
5248         if ((win = find_window(ev->window)) == NULL)
5249                 return;
5250
5251         focus_magic(win);
5252         action = client_click;
5253
5254         for (i = 0; i < LENGTH(buttons); i++)
5255                 if (action == buttons[i].action && buttons[i].func &&
5256                     buttons[i].button == ev->button &&
5257                     CLEANMASK(buttons[i].mask) == CLEANMASK(ev->state))
5258                         buttons[i].func(win, &buttons[i].args);
5259 }
5260
5261 void
5262 configurerequest(XEvent *e)
5263 {
5264         XConfigureRequestEvent  *ev = &e->xconfigurerequest;
5265         struct ws_win           *win;
5266         int                     new = 0;
5267         XWindowChanges          wc;
5268
5269         if ((win = find_window(ev->window)) == NULL)
5270                 if ((win = find_unmanaged_window(ev->window)) == NULL)
5271                         new = 1;
5272
5273         if (new) {
5274                 DNPRINTF(SWM_D_EVENT, "configurerequest: new window: %lu\n",
5275                     ev->window);
5276                 bzero(&wc, sizeof wc);
5277                 wc.x = ev->x;
5278                 wc.y = ev->y;
5279                 wc.width = ev->width;
5280                 wc.height = ev->height;
5281                 wc.border_width = ev->border_width;
5282                 wc.sibling = ev->above;
5283                 wc.stack_mode = ev->detail;
5284                 XConfigureWindow(display, ev->window, ev->value_mask, &wc);
5285         } else {
5286                 DNPRINTF(SWM_D_EVENT, "configurerequest: change window: %lu\n",
5287                     ev->window);
5288                 config_win(win, ev);
5289         }
5290 }
5291
5292 void
5293 configurenotify(XEvent *e)
5294 {
5295         struct ws_win           *win;
5296         long                    mask;
5297
5298         DNPRINTF(SWM_D_EVENT, "configurenotify: window: %lu\n",
5299             e->xconfigure.window);
5300
5301         win = find_window(e->xconfigure.window);
5302         if (win) {
5303                 XGetWMNormalHints(display, win->id, &win->sh, &mask);
5304                 adjust_font(win);
5305                 if (font_adjusted)
5306                         stack();
5307                 if (focus_mode == SWM_FOCUS_DEFAULT)
5308                         drain_enter_notify();
5309         }
5310 }
5311
5312 void
5313 destroynotify(XEvent *e)
5314 {
5315         struct ws_win           *win;
5316         XDestroyWindowEvent     *ev = &e->xdestroywindow;
5317
5318         DNPRINTF(SWM_D_EVENT, "destroynotify: window %lu\n", ev->window);
5319
5320         if ((win = find_window(ev->window)) == NULL) {
5321                 if ((win = find_unmanaged_window(ev->window)) == NULL)
5322                         return;
5323                 free_window(win);
5324                 return;
5325         }
5326
5327         /* make sure we focus on something */
5328         win->floating = 0;
5329
5330         unmanage_window(win);
5331         stack();
5332         if (focus_mode == SWM_FOCUS_DEFAULT)
5333                 drain_enter_notify();
5334         free_window(win);
5335 }
5336
5337 void
5338 enternotify(XEvent *e)
5339 {
5340         XCrossingEvent          *ev = &e->xcrossing;
5341         XEvent                  cne;
5342         struct ws_win           *win;
5343 #if 0
5344         struct ws_win           *w;
5345         Window                  focus_return;
5346         int                     revert_to_return;
5347 #endif
5348         DNPRINTF(SWM_D_FOCUS, "enternotify: window: %lu mode %d detail %d root "
5349             "%lu subwindow %lu same_screen %d focus %d state %d\n",
5350             ev->window, ev->mode, ev->detail, ev->root, ev->subwindow,
5351             ev->same_screen, ev->focus, ev->state);
5352
5353         switch (focus_mode) {
5354         case SWM_FOCUS_DEFAULT:
5355                 break;
5356         case SWM_FOCUS_FOLLOW:
5357                 break;
5358         case SWM_FOCUS_SYNERGY:
5359 #if 0
5360         /*
5361          * all these checks need to be in this order because the
5362          * XCheckTypedWindowEvent relies on weeding out the previous events
5363          *
5364          * making this code an option would enable a follow mouse for focus
5365          * feature
5366          */
5367
5368         /*
5369          * state is set when we are switching workspaces and focus is set when
5370          * the window or a subwindow already has focus (occurs during restart).
5371          *
5372          * Only honor the focus flag if last_focus_event is not FocusOut,
5373          * this allows scrotwm to continue to control focus when another
5374          * program is also playing with it.
5375          */
5376         if (ev->state || (ev->focus && last_focus_event != FocusOut)) {
5377                 DNPRINTF(SWM_D_EVENT, "ignoring enternotify: focus\n");
5378                 return;
5379         }
5380
5381         /*
5382          * happens when a window is created or destroyed and the border
5383          * crosses the mouse pointer and when switching ws
5384          *
5385          * we need the subwindow test to see if we came from root in order
5386          * to give focus to floaters
5387          */
5388         if (ev->mode == NotifyNormal && ev->detail == NotifyVirtual &&
5389             ev->subwindow == 0) {
5390                 DNPRINTF(SWM_D_EVENT, "ignoring enternotify: NotifyVirtual\n");
5391                 return;
5392         }
5393
5394         /* this window already has focus */
5395         if (ev->mode == NotifyNormal && ev->detail == NotifyInferior) {
5396                 DNPRINTF(SWM_D_EVENT, "ignoring enternotify: win has focus\n");
5397                 return;
5398         }
5399
5400         /* this window is being deleted or moved to another ws */
5401         if (XCheckTypedWindowEvent(display, ev->window, ConfigureNotify,
5402             &cne) == True) {
5403                 DNPRINTF(SWM_D_EVENT, "ignoring enternotify: configurenotify\n");
5404                 XPutBackEvent(display, &cne);
5405                 return;
5406         }
5407
5408         if ((win = find_window(ev->window)) == NULL) {
5409                 DNPRINTF(SWM_D_EVENT, "ignoring enternotify: win == NULL\n");
5410                 return;
5411         }
5412
5413         /*
5414          * In fullstack kill all enters unless they come from a different ws
5415          * (i.e. another region) or focus has been grabbed externally.
5416          */
5417         if (win->ws->cur_layout->flags & SWM_L_FOCUSPREV &&
5418             last_focus_event != FocusOut) {
5419                 XGetInputFocus(display, &focus_return, &revert_to_return);
5420                 if ((w = find_window(focus_return)) == NULL ||
5421                     w->ws == win->ws) {
5422                         DNPRINTF(SWM_D_EVENT, "ignoring event: fullstack\n");
5423                         return;
5424                 }
5425         }
5426 #endif
5427                 break;
5428         }
5429
5430         if ((win = find_window(ev->window)) == NULL) {
5431                 DNPRINTF(SWM_D_EVENT, "ignoring enternotify: win == NULL\n");
5432                 return;
5433         }
5434
5435         /*
5436          * if we have more enternotifies let them handle it in due time
5437          */
5438         if (XCheckTypedEvent(display, EnterNotify, &cne) == True) {
5439                 DNPRINTF(SWM_D_EVENT,
5440                     "ignoring enternotify: got more enternotify\n");
5441                 XPutBackEvent(display, &cne);
5442                 return;
5443         }
5444
5445         focus_magic(win);
5446 }
5447
5448 /* lets us use one switch statement for arbitrary mode/detail combinations */
5449 #define MERGE_MEMBERS(a,b)      (((a & 0xffff) << 16) | (b & 0xffff))
5450
5451 void
5452 focusevent(XEvent *e)
5453 {
5454 #if 0
5455         struct ws_win           *win;
5456         u_int32_t               mode_detail;
5457         XFocusChangeEvent       *ev = &e->xfocus;
5458
5459         DNPRINTF(SWM_D_EVENT, "focusevent: %s window: %lu mode %d detail %d\n",
5460             ev->type == FocusIn ? "entering" : "leaving",
5461             ev->window, ev->mode, ev->detail);
5462
5463         if (last_focus_event == ev->type) {
5464                 DNPRINTF(SWM_D_FOCUS, "ignoring focusevent: bad ordering\n");
5465                 return;
5466         }
5467
5468         last_focus_event = ev->type;
5469         mode_detail = MERGE_MEMBERS(ev->mode, ev->detail);
5470
5471         switch (mode_detail) {
5472         /* synergy client focus operations */
5473         case MERGE_MEMBERS(NotifyNormal, NotifyNonlinear):
5474         case MERGE_MEMBERS(NotifyNormal, NotifyNonlinearVirtual):
5475
5476         /* synergy server focus operations */
5477         case MERGE_MEMBERS(NotifyWhileGrabbed, NotifyNonlinear):
5478
5479         /* Entering applications like rdesktop that mangle the pointer */
5480         case MERGE_MEMBERS(NotifyNormal, NotifyPointer):
5481
5482                 if ((win = find_window(e->xfocus.window)) != NULL && win->ws->r)
5483                         XSetWindowBorder(display, win->id,
5484                             win->ws->r->s->c[ev->type == FocusIn ?
5485                             SWM_S_COLOR_FOCUS : SWM_S_COLOR_UNFOCUS].color);
5486                 break;
5487         default:
5488                 fprintf(stderr, "ignoring focusevent\n");
5489                 DNPRINTF(SWM_D_FOCUS, "ignoring focusevent\n");
5490                 break;
5491         }
5492 #endif
5493 }
5494
5495 void
5496 mapnotify(XEvent *e)
5497 {
5498         struct ws_win           *win;
5499         XMapEvent               *ev = &e->xmap;
5500
5501         DNPRINTF(SWM_D_EVENT, "mapnotify: window: %lu\n", ev->window);
5502
5503         win = manage_window(ev->window);
5504         if (win)
5505                 set_win_state(win, NormalState);
5506 }
5507
5508 void
5509 mappingnotify(XEvent *e)
5510 {
5511         XMappingEvent           *ev = &e->xmapping;
5512
5513         XRefreshKeyboardMapping(ev);
5514         if (ev->request == MappingKeyboard)
5515                 grabkeys();
5516 }
5517
5518 void
5519 maprequest(XEvent *e)
5520 {
5521         struct ws_win           *win;
5522         struct swm_region       *r;
5523         XWindowAttributes       wa;
5524         XMapRequestEvent        *ev = &e->xmaprequest;
5525
5526         DNPRINTF(SWM_D_EVENT, "maprequest: window: %lu\n",
5527             e->xmaprequest.window);
5528
5529         if (!XGetWindowAttributes(display, ev->window, &wa))
5530                 return;
5531         if (wa.override_redirect)
5532                 return;
5533
5534         win = manage_window(e->xmaprequest.window);
5535         if (win == NULL)
5536                 return; /* can't happen */
5537
5538         stack();
5539
5540         /* make new win focused */
5541         r = root_to_region(win->wa.root);
5542         if (win->ws == r->ws)
5543                 focus_magic(win);
5544 }
5545
5546 void
5547 propertynotify(XEvent *e)
5548 {
5549         struct ws_win           *win;
5550         XPropertyEvent          *ev = &e->xproperty;
5551
5552         DNPRINTF(SWM_D_EVENT, "propertynotify: window: %lu\n",
5553             ev->window);
5554
5555         win = find_window(ev->window);
5556         if (win == NULL)
5557                 return;
5558
5559         if (ev->state == PropertyDelete && ev->atom == a_swm_iconic) {
5560                 update_iconic(win, 0);
5561                 XMapRaised(display, win->id);
5562                 stack();
5563                 focus_win(win);
5564                 return;
5565         }
5566
5567         switch (ev->atom) {
5568         case XA_WM_NORMAL_HINTS:
5569 #if 0
5570                 long            mask;
5571                 XGetWMNormalHints(display, win->id, &win->sh, &mask);
5572                 fprintf(stderr, "normal hints: flag 0x%x\n", win->sh.flags);
5573                 if (win->sh.flags & PMinSize) {
5574                         win->g.w = win->sh.min_width;
5575                         win->g.h = win->sh.min_height;
5576                         fprintf(stderr, "min %d %d\n", win->g.w, win->g.h);
5577                 }
5578                 XMoveResizeWindow(display, win->id,
5579                     win->g.x, win->g.y, win->g.w, win->g.h);
5580 #endif
5581                 if (window_name_enabled)
5582                         bar_update();
5583                 break;
5584         default:
5585                 break;
5586         }
5587 }
5588
5589 void
5590 unmapnotify(XEvent *e)
5591 {
5592         struct ws_win           *win;
5593
5594         DNPRINTF(SWM_D_EVENT, "unmapnotify: window: %lu\n", e->xunmap.window);
5595
5596         /* determine if we need to help unmanage this window */
5597         win = find_window(e->xunmap.window);
5598         if (win == NULL)
5599                 return;
5600
5601         if (getstate(e->xunmap.window) == NormalState) {
5602                 unmanage_window(win);
5603                 stack();
5604
5605                 /* giant hack for apps that don't destroy transient windows */
5606                 /* eat a bunch of events to prevent remanaging the window */
5607                 XEvent                  cne;
5608                 while (XCheckWindowEvent(display, e->xunmap.window,
5609                     EnterWindowMask, &cne))
5610                         ;
5611                 while (XCheckWindowEvent(display, e->xunmap.window,
5612                     StructureNotifyMask, &cne))
5613                         ;
5614                 while (XCheckWindowEvent(display, e->xunmap.window,
5615                     SubstructureNotifyMask, &cne))
5616                         ;
5617                 /* resend unmap because we ated it */
5618                 XUnmapWindow(display, e->xunmap.window);
5619         }
5620
5621         if (focus_mode == SWM_FOCUS_DEFAULT)
5622                 drain_enter_notify();
5623 }
5624
5625 void
5626 visibilitynotify(XEvent *e)
5627 {
5628         int                     i;
5629         struct swm_region       *r;
5630
5631         DNPRINTF(SWM_D_EVENT, "visibilitynotify: window: %lu\n",
5632             e->xvisibility.window);
5633         if (e->xvisibility.state == VisibilityUnobscured)
5634                 for (i = 0; i < ScreenCount(display); i++)
5635                         TAILQ_FOREACH(r, &screens[i].rl, entry)
5636                                 if (e->xvisibility.window == r->bar_window)
5637                                         bar_update();
5638 }
5639
5640 void
5641 clientmessage(XEvent *e)
5642 {
5643         XClientMessageEvent *ev;
5644         struct ws_win *win;
5645
5646         ev = &e->xclient;
5647
5648         win = find_window(ev->window);
5649         if (win == NULL)
5650                 return;
5651
5652         DNPRINTF(SWM_D_EVENT, "clientmessage: window: 0x%lx type: %ld \n",
5653             ev->window, ev->message_type);
5654
5655         if (ev->message_type == ewmh[_NET_ACTIVE_WINDOW].atom) {
5656                 DNPRINTF(SWM_D_EVENT, "clientmessage: _NET_ACTIVE_WINDOW \n");
5657                 focus_win(win);
5658         }
5659         if (ev->message_type == ewmh[_NET_CLOSE_WINDOW].atom) {
5660                 DNPRINTF(SWM_D_EVENT, "clientmessage: _NET_CLOSE_WINDOW \n");
5661                 if (win->can_delete)
5662                         client_msg(win, adelete);
5663                 else
5664                         XKillClient(display, win->id);
5665         }
5666         if (ev->message_type == ewmh[_NET_MOVERESIZE_WINDOW].atom) {
5667                 DNPRINTF(SWM_D_EVENT,
5668                     "clientmessage: _NET_MOVERESIZE_WINDOW \n");
5669                 if (win->floating) {
5670                         if (ev->data.l[0] & (1<<8)) /* x */
5671                                 win->g.x = ev->data.l[1];
5672                         if (ev->data.l[0] & (1<<9)) /* y */
5673                                 win->g.y = ev->data.l[2];
5674                         if (ev->data.l[0] & (1<<10)) /* width */
5675                                 win->g.w = ev->data.l[3];
5676                         if (ev->data.l[0] & (1<<11)) /* height */
5677                                 win->g.h = ev->data.l[4];
5678                 }
5679                 else {
5680                         /* TODO: Change stack sizes */
5681                 }
5682                 config_win(win, NULL);
5683         }
5684         if (ev->message_type == ewmh[_NET_WM_STATE].atom) {
5685                 DNPRINTF(SWM_D_EVENT, "clientmessage: _NET_WM_STATE \n");
5686                 ewmh_update_win_state(win, ev->data.l[1], ev->data.l[0]);
5687                 if (ev->data.l[2])
5688                         ewmh_update_win_state(win, ev->data.l[2],
5689                             ev->data.l[0]);
5690
5691                 stack();
5692         }
5693 }
5694
5695 int
5696 xerror_start(Display *d, XErrorEvent *ee)
5697 {
5698         other_wm = 1;
5699         return (-1);
5700 }
5701
5702 int
5703 xerror(Display *d, XErrorEvent *ee)
5704 {
5705         /* fprintf(stderr, "error: %p %p\n", display, ee); */
5706         return (-1);
5707 }
5708
5709 int
5710 active_wm(void)
5711 {
5712         other_wm = 0;
5713         xerrorxlib = XSetErrorHandler(xerror_start);
5714
5715         /* this causes an error if some other window manager is running */
5716         XSelectInput(display, DefaultRootWindow(display),
5717             SubstructureRedirectMask);
5718         XSync(display, False);
5719         if (other_wm)
5720                 return (1);
5721
5722         XSetErrorHandler(xerror);
5723         XSync(display, False);
5724         return (0);
5725 }
5726
5727 void
5728 new_region(struct swm_screen *s, int x, int y, int w, int h)
5729 {
5730         struct swm_region       *r, *n;
5731         struct workspace        *ws = NULL;
5732         int                     i;
5733
5734         DNPRINTF(SWM_D_MISC, "new region: screen[%d]:%dx%d+%d+%d\n",
5735              s->idx, w, h, x, y);
5736
5737         /* remove any conflicting regions */
5738         n = TAILQ_FIRST(&s->rl);
5739         while (n) {
5740                 r = n;
5741                 n = TAILQ_NEXT(r, entry);
5742                 if (X(r) < (x + w) &&
5743                     (X(r) + WIDTH(r)) > x &&
5744                     Y(r) < (y + h) &&
5745                     (Y(r) + HEIGHT(r)) > y) {
5746                         if (r->ws->r != NULL)
5747                                 r->ws->old_r = r->ws->r;
5748                         r->ws->r = NULL;
5749                         XDestroyWindow(display, r->bar_window);
5750                         TAILQ_REMOVE(&s->rl, r, entry);
5751                         TAILQ_INSERT_TAIL(&s->orl, r, entry);
5752                 }
5753         }
5754
5755         /* search old regions for one to reuse */
5756
5757         /* size + location match */
5758         TAILQ_FOREACH(r, &s->orl, entry)
5759                 if (X(r) == x && Y(r) == y &&
5760                     HEIGHT(r) == h && WIDTH(r) == w)
5761                         break;
5762
5763         /* size match */
5764         TAILQ_FOREACH(r, &s->orl, entry)
5765                 if (HEIGHT(r) == h && WIDTH(r) == w)
5766                         break;
5767
5768         if (r != NULL) {
5769                 TAILQ_REMOVE(&s->orl, r, entry);
5770                 /* try to use old region's workspace */
5771                 if (r->ws->r == NULL)
5772                         ws = r->ws;
5773         } else
5774                 if ((r = calloc(1, sizeof(struct swm_region))) == NULL)
5775                         errx(1, "calloc: failed to allocate memory for screen");
5776
5777         /* if we don't have a workspace already, find one */
5778         if (ws == NULL) {
5779                 for (i = 0; i < SWM_WS_MAX; i++)
5780                         if (s->ws[i].r == NULL) {
5781                                 ws = &s->ws[i];
5782                                 break;
5783                         }
5784         }
5785
5786         if (ws == NULL)
5787                 errx(1, "no free workspaces\n");
5788
5789         X(r) = x;
5790         Y(r) = y;
5791         WIDTH(r) = w;
5792         HEIGHT(r) = h;
5793         r->s = s;
5794         r->ws = ws;
5795         r->ws_prior = NULL;
5796         ws->r = r;
5797         outputs++;
5798         TAILQ_INSERT_TAIL(&s->rl, r, entry);
5799 }
5800
5801 void
5802 scan_xrandr(int i)
5803 {
5804 #ifdef SWM_XRR_HAS_CRTC
5805         XRRCrtcInfo             *ci;
5806         XRRScreenResources      *sr;
5807         int                     c;
5808         int                     ncrtc = 0;
5809 #endif /* SWM_XRR_HAS_CRTC */
5810         struct swm_region       *r;
5811
5812
5813         if (i >= ScreenCount(display))
5814                 errx(1, "invalid screen");
5815
5816         /* remove any old regions */
5817         while ((r = TAILQ_FIRST(&screens[i].rl)) != NULL) {
5818                 r->ws->old_r = r->ws->r = NULL;
5819                 XDestroyWindow(display, r->bar_window);
5820                 TAILQ_REMOVE(&screens[i].rl, r, entry);
5821                 TAILQ_INSERT_TAIL(&screens[i].orl, r, entry);
5822         }
5823         outputs = 0;
5824
5825         /* map virtual screens onto physical screens */
5826 #ifdef SWM_XRR_HAS_CRTC
5827         if (xrandr_support) {
5828                 sr = XRRGetScreenResources(display, screens[i].root);
5829                 if (sr == NULL)
5830                         new_region(&screens[i], 0, 0,
5831                             DisplayWidth(display, i),
5832                             DisplayHeight(display, i));
5833                 else
5834                         ncrtc = sr->ncrtc;
5835
5836                 for (c = 0, ci = NULL; c < ncrtc; c++) {
5837                         ci = XRRGetCrtcInfo(display, sr, sr->crtcs[c]);
5838                         if (ci->noutput == 0)
5839                                 continue;
5840
5841                         if (ci != NULL && ci->mode == None)
5842                                 new_region(&screens[i], 0, 0,
5843                                     DisplayWidth(display, i),
5844                                     DisplayHeight(display, i));
5845                         else
5846                                 new_region(&screens[i],
5847                                     ci->x, ci->y, ci->width, ci->height);
5848                 }
5849                 if (ci)
5850                         XRRFreeCrtcInfo(ci);
5851                 XRRFreeScreenResources(sr);
5852         } else
5853 #endif /* SWM_XRR_HAS_CRTC */
5854         {
5855                 new_region(&screens[i], 0, 0, DisplayWidth(display, i),
5856                     DisplayHeight(display, i));
5857         }
5858 }
5859
5860 void
5861 screenchange(XEvent *e) {
5862         XRRScreenChangeNotifyEvent      *xe = (XRRScreenChangeNotifyEvent *)e;
5863         struct swm_region               *r;
5864         int                             i;
5865
5866         DNPRINTF(SWM_D_EVENT, "screenchange: %lu\n", xe->root);
5867
5868         if (!XRRUpdateConfiguration(e))
5869                 return;
5870
5871         /* silly event doesn't include the screen index */
5872         for (i = 0; i < ScreenCount(display); i++)
5873                 if (screens[i].root == xe->root)
5874                         break;
5875         if (i >= ScreenCount(display))
5876                 errx(1, "screenchange: screen not found\n");
5877
5878         /* brute force for now, just re-enumerate the regions */
5879         scan_xrandr(i);
5880
5881         /* add bars to all regions */
5882         for (i = 0; i < ScreenCount(display); i++)
5883                 TAILQ_FOREACH(r, &screens[i].rl, entry)
5884                         bar_setup(r);
5885         stack();
5886         if (focus_mode == SWM_FOCUS_DEFAULT)
5887                 drain_enter_notify();
5888 }
5889
5890 void
5891 grab_windows(void)
5892 {
5893         Window                  d1, d2, *wins = NULL;
5894         XWindowAttributes       wa;
5895         unsigned int            no;
5896         int                     i, j;
5897         long                    state, manage;
5898
5899         for (i = 0; i < ScreenCount(display); i++) {
5900                 if (!XQueryTree(display, screens[i].root, &d1, &d2, &wins, &no))
5901                         continue;
5902
5903                 /* attach windows to a region */
5904                 /* normal windows */
5905                 for (j = 0; j < no; j++) {
5906                         if (!XGetWindowAttributes(display, wins[j], &wa) ||
5907                             wa.override_redirect ||
5908                             XGetTransientForHint(display, wins[j], &d1))
5909                                 continue;
5910
5911                         state = getstate(wins[j]);
5912                         manage = state == IconicState;
5913                         if (wa.map_state == IsViewable || manage)
5914                                 manage_window(wins[j]);
5915                 }
5916                 /* transient windows */
5917                 for (j = 0; j < no; j++) {
5918                         if (!XGetWindowAttributes(display, wins[j], &wa) ||
5919                             wa.override_redirect)
5920                                 continue;
5921
5922                         state = getstate(wins[j]);
5923                         manage = state == IconicState;
5924                         if (XGetTransientForHint(display, wins[j], &d1) &&
5925                             manage)
5926                                 manage_window(wins[j]);
5927                 }
5928                 if (wins) {
5929                         XFree(wins);
5930                         wins = NULL;
5931                 }
5932         }
5933 }
5934
5935 void
5936 setup_screens(void)
5937 {
5938         int                     i, j, k;
5939         int                     errorbase, major, minor;
5940         struct workspace        *ws;
5941
5942         if ((screens = calloc(ScreenCount(display),
5943              sizeof(struct swm_screen))) == NULL)
5944                 errx(1, "calloc: screens");
5945
5946         /* initial Xrandr setup */
5947         xrandr_support = XRRQueryExtension(display,
5948             &xrandr_eventbase, &errorbase);
5949         if (xrandr_support)
5950                 if (XRRQueryVersion(display, &major, &minor) && major < 1)
5951                         xrandr_support = 0;
5952
5953         /* map physical screens */
5954         for (i = 0; i < ScreenCount(display); i++) {
5955                 DNPRINTF(SWM_D_WS, "setup_screens: init screen %d\n", i);
5956                 screens[i].idx = i;
5957                 TAILQ_INIT(&screens[i].rl);
5958                 TAILQ_INIT(&screens[i].orl);
5959                 screens[i].root = RootWindow(display, i);
5960
5961                 /* set default colors */
5962                 setscreencolor("red", i + 1, SWM_S_COLOR_FOCUS);
5963                 setscreencolor("rgb:88/88/88", i + 1, SWM_S_COLOR_UNFOCUS);
5964                 setscreencolor("rgb:00/80/80", i + 1, SWM_S_COLOR_BAR_BORDER);
5965                 setscreencolor("black", i + 1, SWM_S_COLOR_BAR);
5966                 setscreencolor("rgb:a0/a0/a0", i + 1, SWM_S_COLOR_BAR_FONT);
5967
5968                 /* set default cursor */
5969                 XDefineCursor(display, screens[i].root,
5970                     XCreateFontCursor(display, XC_left_ptr));
5971
5972                 /* init all workspaces */
5973                 /* XXX these should be dynamically allocated too */
5974                 for (j = 0; j < SWM_WS_MAX; j++) {
5975                         ws = &screens[i].ws[j];
5976                         ws->idx = j;
5977                         ws->focus = NULL;
5978                         ws->r = NULL;
5979                         ws->old_r = NULL;
5980                         TAILQ_INIT(&ws->winlist);
5981                         TAILQ_INIT(&ws->unmanagedlist);
5982
5983                         for (k = 0; layouts[k].l_stack != NULL; k++)
5984                                 if (layouts[k].l_config != NULL)
5985                                         layouts[k].l_config(ws,
5986                                             SWM_ARG_ID_STACKINIT);
5987                         ws->cur_layout = &layouts[0];
5988                 }
5989
5990                 scan_xrandr(i);
5991
5992                 if (xrandr_support)
5993                         XRRSelectInput(display, screens[i].root,
5994                             RRScreenChangeNotifyMask);
5995         }
5996 }
5997
5998 void
5999 setup_globals(void)
6000 {
6001         if ((bar_fonts[0] = strdup("-*-terminus-medium-*-*-*-*-*-*-*-*-*-*-*"))
6002             == NULL)
6003                 err(1, "setup_globals: strdup");
6004         if ((bar_fonts[1] = strdup("-*-times-medium-r-*-*-*-*-*-*-*-*-*-*"))
6005             == NULL)
6006                 err(1, "setup_globals: strdup");
6007         if ((bar_fonts[2] = strdup("-misc-fixed-medium-r-*-*-*-*-*-*-*-*-*-*"))
6008             == NULL)
6009                 err(1, "setup_globals: strdup");
6010         if ((spawn_term[0] = strdup("xterm")) == NULL)
6011                 err(1, "setup_globals: strdup");
6012         if ((clock_format = strdup("%a %b %d %R %Z %Y")) == NULL)
6013                 errx(1, "strdup");
6014 }
6015
6016 void
6017 workaround(void)
6018 {
6019         int                     i;
6020         Atom                    netwmcheck, netwmname, utf8_string;
6021         Window                  root, win;
6022
6023         /* work around sun jdk bugs, code from wmname */
6024         netwmcheck = XInternAtom(display, "_NET_SUPPORTING_WM_CHECK", False);
6025         netwmname = XInternAtom(display, "_NET_WM_NAME", False);
6026         utf8_string = XInternAtom(display, "UTF8_STRING", False);
6027         for (i = 0; i < ScreenCount(display); i++) {
6028                 root = screens[i].root;
6029                 win = XCreateSimpleWindow(display,root, 0, 0, 1, 1, 0,
6030                     screens[i].c[SWM_S_COLOR_UNFOCUS].color,
6031                     screens[i].c[SWM_S_COLOR_UNFOCUS].color);
6032
6033                 XChangeProperty(display, root, netwmcheck, XA_WINDOW, 32,
6034                     PropModeReplace, (unsigned char *)&win,1);
6035                 XChangeProperty(display, win, netwmcheck, XA_WINDOW, 32,
6036                     PropModeReplace, (unsigned char *)&win,1);
6037                 XChangeProperty(display, win, netwmname, utf8_string, 8,
6038                     PropModeReplace, (unsigned char*)"LG3D", strlen("LG3D"));
6039         }
6040 }
6041
6042 int
6043 main(int argc, char *argv[])
6044 {
6045         struct passwd           *pwd;
6046         struct swm_region       *r, *rr;
6047         struct ws_win           *winfocus = NULL;
6048         struct timeval          tv;
6049         union arg               a;
6050         char                    conf[PATH_MAX], *cfile = NULL;
6051         struct stat             sb;
6052         XEvent                  e;
6053         int                     xfd, i;
6054         fd_set                  rd;
6055         struct sigaction        sact;
6056
6057         start_argv = argv;
6058         fprintf(stderr, "Welcome to scrotwm V%s cvs tag: %s\n",
6059             SWM_VERSION, cvstag);
6060         if (!setlocale(LC_CTYPE, "") || !XSupportsLocale())
6061                 warnx("no locale support");
6062
6063         if (!(display = XOpenDisplay(0)))
6064                 errx(1, "can not open display");
6065
6066         if (active_wm())
6067                 errx(1, "other wm running");
6068
6069         /* handle some signals */
6070         bzero(&sact, sizeof(sact));
6071         sigemptyset(&sact.sa_mask);
6072         sact.sa_flags = 0;
6073         sact.sa_handler = sighdlr;
6074         sigaction(SIGINT, &sact, NULL);
6075         sigaction(SIGQUIT, &sact, NULL);
6076         sigaction(SIGTERM, &sact, NULL);
6077         sigaction(SIGHUP, &sact, NULL);
6078
6079         sact.sa_handler = sighdlr;
6080         sact.sa_flags = SA_NOCLDSTOP;
6081         sigaction(SIGCHLD, &sact, NULL);
6082
6083         astate = XInternAtom(display, "WM_STATE", False);
6084         aprot = XInternAtom(display, "WM_PROTOCOLS", False);
6085         adelete = XInternAtom(display, "WM_DELETE_WINDOW", False);
6086         takefocus = XInternAtom(display, "WM_TAKE_FOCUS", False);
6087         a_wmname = XInternAtom(display, "WM_NAME", False);
6088         a_utf8_string = XInternAtom(display, "UTF8_STRING", False);
6089         a_string = XInternAtom(display, "STRING", False);
6090         a_swm_iconic = XInternAtom(display, "_SWM_ICONIC", False);
6091
6092         /* look for local and global conf file */
6093         pwd = getpwuid(getuid());
6094         if (pwd == NULL)
6095                 errx(1, "invalid user %d", getuid());
6096
6097         setup_screens();
6098         setup_globals();
6099         setup_keys();
6100         setup_quirks();
6101         setup_spawn();
6102
6103         /* load config */
6104         snprintf(conf, sizeof conf, "%s/.%s", pwd->pw_dir, SWM_CONF_FILE);
6105         if (stat(conf, &sb) != -1) {
6106                 if (S_ISREG(sb.st_mode))
6107                         cfile = conf;
6108         } else {
6109                 /* try global conf file */
6110                 snprintf(conf, sizeof conf, "/etc/%s", SWM_CONF_FILE);
6111                 if (!stat(conf, &sb))
6112                         if (S_ISREG(sb.st_mode))
6113                                 cfile = conf;
6114         }
6115         if (cfile)
6116                 conf_load(cfile);
6117
6118         setup_ewmh();
6119         /* set some values to work around bad programs */
6120         workaround();
6121
6122         /* grab existing windows (before we build the bars) */
6123         grab_windows();
6124
6125         if (getenv("SWM_STARTED") == NULL)
6126                 setenv("SWM_STARTED", "YES", 1);
6127
6128         /* setup all bars */
6129         for (i = 0; i < ScreenCount(display); i++)
6130                 TAILQ_FOREACH(r, &screens[i].rl, entry) {
6131                         if (winfocus == NULL)
6132                                 winfocus = TAILQ_FIRST(&r->ws->winlist);
6133                         bar_setup(r);
6134                 }
6135
6136         unfocus_all();
6137
6138         grabkeys();
6139         stack();
6140         if (focus_mode == SWM_FOCUS_DEFAULT)
6141                 drain_enter_notify();
6142
6143         xfd = ConnectionNumber(display);
6144         while (running) {
6145                 while (XPending(display)) {
6146                         XNextEvent(display, &e);
6147                         if (running == 0)
6148                                 goto done;
6149                         if (e.type < LASTEvent) {
6150                                 dumpevent(&e);
6151                                 if (handler[e.type])
6152                                         handler[e.type](&e);
6153                                 else
6154                                         DNPRINTF(SWM_D_EVENT,
6155                                             "win: %lu unknown event: %d\n",
6156                                             e.xany.window, e.type);
6157                         } else {
6158                                 switch (e.type - xrandr_eventbase) {
6159                                 case RRScreenChangeNotify:
6160                                         screenchange(&e);
6161                                         break;
6162                                 default:
6163                                         DNPRINTF(SWM_D_EVENT,
6164                                             "win: %lu unknown xrandr event: "
6165                                             "%d\n", e.xany.window, e.type);
6166                                         break;
6167                                 }
6168                         }
6169                 }
6170
6171                 /* if we are being restarted go focus on first window */
6172                 if (winfocus) {
6173                         rr = winfocus->ws->r;
6174                         if (rr == NULL) {
6175                                 /* not a visible window */
6176                                 winfocus = NULL;
6177                                 continue;
6178                         }
6179                         /* move pointer to first screen if multi screen */
6180                         if (ScreenCount(display) > 1 || outputs > 1)
6181                                 XWarpPointer(display, None, rr->s[0].root,
6182                                     0, 0, 0, 0, rr->g.x,
6183                                     rr->g.y + (bar_enabled ? bar_height : 0));
6184
6185                         a.id = SWM_ARG_ID_FOCUSCUR;
6186                         focus(rr, &a);
6187                         winfocus = NULL;
6188                         continue;
6189                 }
6190
6191                 FD_ZERO(&rd);
6192                 FD_SET(xfd, &rd);
6193                 tv.tv_sec = 1;
6194                 tv.tv_usec = 0;
6195                 if (select(xfd + 1, &rd, NULL, NULL, &tv) == -1)
6196                         if (errno != EINTR)
6197                                 DNPRINTF(SWM_D_MISC, "select failed");
6198                 if (restart_wm == 1)
6199                         restart(NULL, NULL);
6200                 if (search_resp == 1)
6201                         search_do_resp();
6202                 if (running == 0)
6203                         goto done;
6204                 if (bar_alarm) {
6205                         bar_alarm = 0;
6206                         bar_update();
6207                 }
6208         }
6209 done:
6210         teardown_ewmh();
6211         bar_extra_stop();
6212         XFreeGC(display, bar_gc);
6213         XCloseDisplay(display);
6214
6215         return (0);
6216 }