JasonWoof Got questions, comments, patches, etc.? Contact Jason Woofenden
fb70acabd0b5e51ddc2348f7e0c7d30b2bea0237
[spectrwm.git] / spectrwm.c
1 /*
2  * Copyright (c) 2009-2012 Marco Peereboom <marco@peereboom.us>
3  * Copyright (c) 2009-2011 Ryan McBride <mcbride@countersiege.com>
4  * Copyright (c) 2009 Darrin Chandler <dwchandler@stilyagin.com>
5  * Copyright (c) 2009 Pierre-Yves Ritschard <pyr@spootnik.org>
6  * Copyright (c) 2010 Tuukka Kataja <stuge@xor.fi>
7  * Copyright (c) 2011 Jason L. Wright <jason@thought.net>
8  * Copyright (c) 2011-2012 Reginald Kennedy <rk@rejii.com>
9  * Copyright (c) 2011-2012 Lawrence Teo <lteo@lteo.net>
10  * Copyright (c) 2011-2012 Tiago Cunha <tcunha@gmx.com>
11  *
12  * Permission to use, copy, modify, and distribute this software for any
13  * purpose with or without fee is hereby granted, provided that the above
14  * copyright notice and this permission notice appear in all copies.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
17  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
18  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
19  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
20  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
21  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
22  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
23  */
24 /*
25  * Much code and ideas taken from dwm under the following license:
26  * MIT/X Consortium License
27  *
28  * 2006-2008 Anselm R Garbe <garbeam at gmail dot com>
29  * 2006-2007 Sander van Dijk <a dot h dot vandijk at gmail dot com>
30  * 2006-2007 Jukka Salmi <jukka at salmi dot ch>
31  * 2007 Premysl Hruby <dfenze at gmail dot com>
32  * 2007 Szabolcs Nagy <nszabolcs at gmail dot com>
33  * 2007 Christof Musik <christof at sendfax dot de>
34  * 2007-2008 Enno Gottox Boland <gottox at s01 dot de>
35  * 2007-2008 Peter Hartlich <sgkkr at hartlich dot com>
36  * 2008 Martin Hurton <martin dot hurton at gmail dot com>
37  *
38  * Permission is hereby granted, free of charge, to any person obtaining a
39  * copy of this software and associated documentation files (the "Software"),
40  * to deal in the Software without restriction, including without limitation
41  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
42  * and/or sell copies of the Software, and to permit persons to whom the
43  * Software is furnished to do so, subject to the following conditions:
44  *
45  * The above copyright notice and this permission notice shall be included in
46  * all copies or substantial portions of the Software.
47  *
48  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
49  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
50  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
51  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
52  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
53  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
54  * DEALINGS IN THE SOFTWARE.
55  */
56
57 #include <stdio.h>
58 #include <stdlib.h>
59 #include <err.h>
60 #include <errno.h>
61 #include <fcntl.h>
62 #include <locale.h>
63 #include <unistd.h>
64 #include <time.h>
65 #include <signal.h>
66 #include <string.h>
67 #include <util.h>
68 #include <pwd.h>
69 #include <paths.h>
70 #include <ctype.h>
71
72 #include <sys/types.h>
73 #include <sys/time.h>
74 #include <sys/stat.h>
75 #include <sys/wait.h>
76 #include <sys/queue.h>
77 #include <sys/param.h>
78 #include <sys/select.h>
79 #if defined(__linux__)
80 #include "tree.h"
81 #elif defined(__OpenBSD__)
82 #include <sys/tree.h>
83 #elif defined(__FreeBSD__)
84 #include <sys/tree.h>
85 #else
86 #include "tree.h"
87 #endif
88
89 #include <X11/cursorfont.h>
90 #include <X11/keysym.h>
91 #include <X11/XKBlib.h>
92 #include <X11/Xatom.h>
93 #include <X11/Xlib-xcb.h>
94 #include <xcb/randr.h>
95 #include <xcb/xcb_atom.h>
96 #include <xcb/xcb_icccm.h>
97 #include <xcb/xcb_keysyms.h>
98 #include <X11/Xproto.h>
99 #include <X11/Xutil.h>
100 #include <X11/extensions/Xrandr.h>
101 #include <X11/extensions/XTest.h>
102
103 #ifdef __OSX__
104 #include <osx.h>
105 #endif
106
107 #include "version.h"
108
109 #ifdef SPECTRWM_BUILDSTR
110 static const char       *buildstr = SPECTRWM_BUILDSTR;
111 #else
112 static const char       *buildstr = SPECTRWM_VERSION;
113 #endif
114
115 #if RANDR_MAJOR < 1
116 #  error XRandR versions less than 1.0 are not supported
117 #endif
118
119 #if RANDR_MAJOR >= 1
120 #if RANDR_MINOR >= 2
121 #define SWM_XRR_HAS_CRTC
122 #endif
123 #endif
124
125 #if defined(__OpenBSD__)
126 #define XCB_ICCCM_SIZE_HINT_P_MIN_SIZE          XCB_SIZE_HINT_P_MIN_SIZE
127 #define XCB_ICCCM_SIZE_HINT_P_MAX_SIZE          XCB_SIZE_HINT_P_MAX_SIZE
128 #define XCB_ICCCM_SIZE_HINT_P_RESIZE_INC        XCB_SIZE_HINT_P_RESIZE_INC
129 #define XCB_ICCCM_WM_HINT_X_URGENCY             XCB_WM_HINT_X_URGENCY
130 #define XCB_ICCCM_WM_STATE_ICONIC               XCB_WM_STATE_ICONIC
131 #define XCB_ICCCM_WM_STATE_WITHDRAWN            XCB_WM_STATE_WITHDRAWN
132 #define XCB_ICCCM_WM_STATE_NORMAL               XCB_WM_STATE_NORMAL
133 #define xcb_icccm_get_text_property_reply_t     xcb_get_text_property_reply_t
134 #define xcb_icccm_get_text_property_reply_wipe  xcb_get_text_property_reply_wipe
135 #define xcb_icccm_get_wm_class                  xcb_get_wm_class
136 #define xcb_icccm_get_wm_class_reply            xcb_get_wm_class_reply
137 #define xcb_icccm_get_wm_class_reply_t          xcb_get_wm_class_reply_t
138 #define xcb_icccm_get_wm_class_reply_wipe       xcb_get_wm_class_reply_wipe
139 #define xcb_icccm_get_wm_hints                  xcb_get_wm_hints
140 #define xcb_icccm_get_wm_hints_reply            xcb_get_wm_hints_reply
141 #define xcb_icccm_get_wm_name                   xcb_get_wm_name
142 #define xcb_icccm_get_wm_name_reply             xcb_get_wm_name_reply
143 #define xcb_icccm_get_wm_normal_hints           xcb_get_wm_normal_hints
144 #define xcb_icccm_get_wm_normal_hints_reply     xcb_get_wm_normal_hints_reply
145 #define xcb_icccm_get_wm_protocols              xcb_get_wm_protocols
146 #define xcb_icccm_get_wm_protocols_reply        xcb_get_wm_protocols_reply
147 #define xcb_icccm_get_wm_protocols_reply_t      xcb_get_wm_protocols_reply_t
148 #define xcb_icccm_get_wm_transient_for          xcb_get_wm_transient_for
149 #define xcb_icccm_get_wm_transient_for_reply    xcb_get_wm_transient_for_reply
150 #define xcb_icccm_wm_hints_t                    xcb_wm_hints_t
151 #endif
152
153 /*#define SWM_DEBUG*/
154 #ifdef SWM_DEBUG
155 #define DPRINTF(x...)           do { if (swm_debug) fprintf(stderr, x); } while (0)
156 #define DNPRINTF(n,x...)        do { if (swm_debug & n) fprintf(stderr, x); } while (0)
157 #define SWM_D_MISC              0x0001
158 #define SWM_D_EVENT             0x0002
159 #define SWM_D_WS                0x0004
160 #define SWM_D_FOCUS             0x0008
161 #define SWM_D_MOVE              0x0010
162 #define SWM_D_STACK             0x0020
163 #define SWM_D_MOUSE             0x0040
164 #define SWM_D_PROP              0x0080
165 #define SWM_D_CLASS             0x0100
166 #define SWM_D_KEY               0x0200
167 #define SWM_D_QUIRK             0x0400
168 #define SWM_D_SPAWN             0x0800
169 #define SWM_D_EVENTQ            0x1000
170 #define SWM_D_CONF              0x2000
171 #define SWM_D_BAR               0x4000
172
173 u_int32_t               swm_debug = 0
174                             | SWM_D_MISC
175                             | SWM_D_EVENT
176                             | SWM_D_WS
177                             | SWM_D_FOCUS
178                             | SWM_D_MOVE
179                             | SWM_D_STACK
180                             | SWM_D_MOUSE
181                             | SWM_D_PROP
182                             | SWM_D_CLASS
183                             | SWM_D_KEY
184                             | SWM_D_QUIRK
185                             | SWM_D_SPAWN
186                             | SWM_D_EVENTQ
187                             | SWM_D_CONF
188                             | SWM_D_BAR
189                             ;
190 #else
191 #define DPRINTF(x...)
192 #define DNPRINTF(n,x...)
193 #endif
194
195 #define LENGTH(x)               (sizeof x / sizeof x[0])
196 #define MODKEY                  Mod1Mask
197 #define CLEANMASK(mask)         (mask & ~(numlockmask | LockMask))
198 #define BUTTONMASK              (ButtonPressMask|ButtonReleaseMask)
199 #define MOUSEMASK               (BUTTONMASK|PointerMotionMask)
200 #define SWM_PROPLEN             (16)
201 #define SWM_FUNCNAME_LEN        (32)
202 #define SWM_KEYS_LEN            (255)
203 #define SWM_QUIRK_LEN           (64)
204 #define X(r)                    (r)->g.x
205 #define Y(r)                    (r)->g.y
206 #define WIDTH(r)                (r)->g.w
207 #define HEIGHT(r)               (r)->g.h
208 #define BORDER(w)               (w->bordered ? border_width : 0)
209 #define MAX_X(r)                ((r)->g.x + (r)->g.w)
210 #define MAX_Y(r)                ((r)->g.y + (r)->g.h)
211 #define SH_MIN(w)               (w)->sh.flags & XCB_ICCCM_SIZE_HINT_P_MIN_SIZE
212 #define SH_MIN_W(w)             (w)->sh.min_width
213 #define SH_MIN_H(w)             (w)->sh.min_height
214 #define SH_MAX(w)               (w)->sh.flags & XCB_ICCCM_SIZE_HINT_P_MAX_SIZE
215 #define SH_MAX_W(w)             (w)->sh.max_width
216 #define SH_MAX_H(w)             (w)->sh.max_height
217 #define SH_INC(w)               (w)->sh.flags & XCB_ICCCM_SIZE_HINT_P_RESIZE_INC
218 #define SH_INC_W(w)             (w)->sh.width_inc
219 #define SH_INC_H(w)             (w)->sh.height_inc
220 #define SWM_MAX_FONT_STEPS      (3)
221 #define WINID(w)                ((w) ? (w)->id : 0)
222 #define YESNO(x)                ((x) ? "yes" : "no")
223
224 #define SWM_FOCUS_DEFAULT       (0)
225 #define SWM_FOCUS_SYNERGY       (1)
226 #define SWM_FOCUS_FOLLOW        (2)
227
228 #define SWM_CONF_DEFAULT        (0)
229 #define SWM_CONF_KEYMAPPING     (1)
230
231 #ifndef SWM_LIB
232 #define SWM_LIB                 "/usr/local/lib/libswmhack.so"
233 #endif
234
235 char                    **start_argv;
236 xcb_atom_t              astate;
237 xcb_atom_t              aprot;
238 xcb_atom_t              adelete;
239 xcb_atom_t              takefocus;
240 xcb_atom_t              a_wmname;
241 xcb_atom_t              a_netwmname;
242 xcb_atom_t              a_utf8_string;
243 xcb_atom_t              a_string;
244 xcb_atom_t              a_swm_iconic;
245 volatile sig_atomic_t   running = 1;
246 volatile sig_atomic_t   restart_wm = 0;
247 int                     outputs = 0;
248 int                     last_focus_event = FocusOut;
249 int                     (*xerrorxlib)(Display *, XErrorEvent *);
250 int                     other_wm;
251 int                     ss_enabled = 0;
252 int                     xrandr_support;
253 int                     xrandr_eventbase;
254 unsigned int            numlockmask = 0;
255 Display                 *display;
256 xcb_connection_t        *conn;
257 xcb_key_symbols_t       *syms;
258
259 int                     cycle_empty = 0;
260 int                     cycle_visible = 0;
261 int                     term_width = 0;
262 int                     font_adjusted = 0;
263 unsigned int            mod_key = MODKEY;
264
265 /* dmenu search */
266 struct swm_region       *search_r;
267 int                     select_list_pipe[2];
268 int                     select_resp_pipe[2];
269 pid_t                   searchpid;
270 volatile sig_atomic_t   search_resp;
271 int                     search_resp_action;
272
273 struct search_window {
274         TAILQ_ENTRY(search_window)      entry;
275         int                             idx;
276         struct ws_win                   *win;
277         xcb_gcontext_t                  gc;
278         xcb_window_t                    indicator;
279 };
280 TAILQ_HEAD(search_winlist, search_window);
281 struct search_winlist                   search_wl;
282
283 /* search actions */
284 enum {
285         SWM_SEARCH_NONE,
286         SWM_SEARCH_UNICONIFY,
287         SWM_SEARCH_NAME_WORKSPACE,
288         SWM_SEARCH_SEARCH_WORKSPACE,
289         SWM_SEARCH_SEARCH_WINDOW
290 };
291
292 #define SWM_STACK_TOP           (0)
293 #define SWM_STACK_BOTTOM        (1)
294 #define SWM_STACK_ABOVE         (2)
295 #define SWM_STACK_BELOW         (3)
296
297 /* dialog windows */
298 double                  dialog_ratio = 0.6;
299 /* status bar */
300 #define SWM_BAR_MAX             (256)
301 #define SWM_BAR_JUSTIFY_LEFT    (0)
302 #define SWM_BAR_JUSTIFY_CENTER  (1)
303 #define SWM_BAR_JUSTIFY_RIGHT   (2)
304 #define SWM_BAR_OFFSET          (4)
305 #define SWM_BAR_FONTS           "-*-terminus-medium-*-*-*-*-*-*-*-*-*-*-*," \
306                                 "-*-profont-*-*-*-*-*-*-*-*-*-*-*-*,"       \
307                                 "-*-times-medium-r-*-*-*-*-*-*-*-*-*-*,"    \
308                                 "-misc-fixed-medium-r-*-*-*-*-*-*-*-*-*-*,"  \
309                                 "-*-*-*-r-*--*-*-*-*-*-*-*-*"
310
311 char                    *bar_argv[] = { NULL, NULL };
312 int                     bar_pipe[2];
313 unsigned char           bar_ext[SWM_BAR_MAX];
314 char                    bar_vertext[SWM_BAR_MAX];
315 int                     bar_version = 0;
316 sig_atomic_t            bar_alarm = 0;
317 int                     bar_delay = 30;
318 int                     bar_enabled = 1;
319 int                     bar_border_width = 1;
320 int                     bar_at_bottom = 0;
321 int                     bar_extra = 1;
322 int                     bar_extra_running = 0;
323 int                     bar_verbose = 1;
324 int                     bar_height = 0;
325 int                     bar_justify = SWM_BAR_JUSTIFY_LEFT;
326 char                    *bar_format = NULL;
327 int                     stack_enabled = 1;
328 int                     clock_enabled = 1;
329 int                     urgent_enabled = 0;
330 char                    *clock_format = NULL;
331 int                     title_name_enabled = 0;
332 int                     title_class_enabled = 0;
333 int                     window_name_enabled = 0;
334 int                     focus_mode = SWM_FOCUS_DEFAULT;
335 int                     focus_close = SWM_STACK_BELOW;
336 int                     focus_close_wrap = 1;
337 int                     focus_default = SWM_STACK_TOP;
338 int                     spawn_position = SWM_STACK_TOP;
339 int                     disable_border = 0;
340 int                     border_width = 1;
341 int                     verbose_layout = 0;
342 pid_t                   bar_pid;
343 XFontSet                bar_fs;
344 XFontSetExtents         *bar_fs_extents;
345 char                    *bar_fonts;
346 struct passwd           *pwd;
347
348 #define SWM_MENU_FN     (2)
349 #define SWM_MENU_NB     (4)
350 #define SWM_MENU_NF     (6)
351 #define SWM_MENU_SB     (8)
352 #define SWM_MENU_SF     (10)
353
354 /* layout manager data */
355 struct swm_geometry {
356         int                     x;
357         int                     y;
358         int                     w;
359         int                     h;
360 };
361
362 struct swm_screen;
363 struct workspace;
364
365 struct swm_bar {
366         xcb_window_t            id;
367         xcb_pixmap_t            buffer;
368         struct swm_geometry     g;
369 };
370
371 /* virtual "screens" */
372 struct swm_region {
373         TAILQ_ENTRY(swm_region) entry;
374         struct swm_geometry     g;
375         struct workspace        *ws;    /* current workspace on this region */
376         struct workspace        *ws_prior; /* prior workspace on this region */
377         struct swm_screen       *s;     /* screen idx */
378         struct swm_bar          *bar;
379 };
380 TAILQ_HEAD(swm_region_list, swm_region);
381
382 struct ws_win {
383         TAILQ_ENTRY(ws_win)     entry;
384         xcb_window_t            id;
385         xcb_window_t            transient;
386         struct ws_win           *child_trans;   /* transient child window */
387         struct swm_geometry     g;              /* current geometry */
388         struct swm_geometry     g_float;        /* region coordinates */
389         int                     g_floatvalid;   /* g_float geometry validity */
390         int                     floatmaxed;     /* whether maxed by max_stack */
391         int                     floating;
392         int                     manual;
393         int32_t                 iconic;
394         int                     bordered;
395         unsigned int            ewmh_flags;
396         int                     font_size_boundary[SWM_MAX_FONT_STEPS];
397         int                     font_steps;
398         int                     last_inc;
399         int                     can_delete;
400         int                     take_focus;
401         int                     java;
402         unsigned long           quirks;
403         struct workspace        *ws;    /* always valid */
404         struct swm_screen       *s;     /* always valid, never changes */
405         xcb_get_geometry_reply_t        *wa;
406         xcb_size_hints_t        sh;
407         xcb_icccm_get_wm_class_reply_t  ch;
408         xcb_icccm_wm_hints_t    hints;
409 };
410 TAILQ_HEAD(ws_win_list, ws_win);
411
412 /* pid goo */
413 struct pid_e {
414         TAILQ_ENTRY(pid_e)      entry;
415         long                    pid;
416         int                     ws;
417 };
418 TAILQ_HEAD(pid_list, pid_e);
419 struct pid_list                 pidlist = TAILQ_HEAD_INITIALIZER(pidlist);
420
421 /* layout handlers */
422 void    stack(void);
423 void    vertical_config(struct workspace *, int);
424 void    vertical_stack(struct workspace *, struct swm_geometry *);
425 void    horizontal_config(struct workspace *, int);
426 void    horizontal_stack(struct workspace *, struct swm_geometry *);
427 void    max_stack(struct workspace *, struct swm_geometry *);
428 void    plain_stacker(struct workspace *);
429 void    fancy_stacker(struct workspace *);
430
431 struct ws_win *find_window(xcb_window_t);
432
433 void            grabbuttons(struct ws_win *, int);
434 void            new_region(struct swm_screen *, int, int, int, int);
435 void            unmanage_window(struct ws_win *);
436 uint16_t        getstate(xcb_window_t);
437
438 int             conf_load(char *, int);
439
440 struct layout {
441         void            (*l_stack)(struct workspace *, struct swm_geometry *);
442         void            (*l_config)(struct workspace *, int);
443         u_int32_t       flags;
444 #define SWM_L_FOCUSPREV         (1<<0)
445 #define SWM_L_MAPONFOCUS        (1<<1)
446         void            (*l_string)(struct workspace *);
447 } layouts[] =  {
448         /* stack,               configure */
449         { vertical_stack,       vertical_config,        0,      plain_stacker },
450         { horizontal_stack,     horizontal_config,      0,      plain_stacker },
451         { max_stack,            NULL,
452           SWM_L_MAPONFOCUS | SWM_L_FOCUSPREV,                   plain_stacker },
453         { NULL,                 NULL,                   0,      NULL  },
454 };
455
456 /* position of max_stack mode in the layouts array, index into layouts! */
457 #define SWM_V_STACK             (0)
458 #define SWM_H_STACK             (1)
459 #define SWM_MAX_STACK           (2)
460
461 #define SWM_H_SLICE             (32)
462 #define SWM_V_SLICE             (32)
463
464 /* define work spaces */
465 struct workspace {
466         int                     idx;            /* workspace index */
467         char                    *name;          /* workspace name */
468         int                     always_raise;   /* raise windows on focus */
469         struct layout           *cur_layout;    /* current layout handlers */
470         struct ws_win           *focus;         /* may be NULL */
471         struct ws_win           *focus_prev;    /* may be NULL */
472         struct swm_region       *r;             /* may be NULL */
473         struct swm_region       *old_r;         /* may be NULL */
474         struct ws_win_list      winlist;        /* list of windows in ws */
475         struct ws_win_list      unmanagedlist;  /* list of dead windows in ws */
476         char                    stacker[10];    /* display stacker and layout */
477
478         /* stacker state */
479         struct {
480                                 int horizontal_msize;
481                                 int horizontal_mwin;
482                                 int horizontal_stacks;
483                                 int horizontal_flip;
484                                 int vertical_msize;
485                                 int vertical_mwin;
486                                 int vertical_stacks;
487                                 int vertical_flip;
488         } l_state;
489 };
490
491 enum {
492         SWM_S_COLOR_BAR,
493         SWM_S_COLOR_BAR_BORDER,
494         SWM_S_COLOR_BAR_FONT,
495         SWM_S_COLOR_FOCUS,
496         SWM_S_COLOR_UNFOCUS,
497         SWM_S_COLOR_MAX
498 };
499
500 /* physical screen mapping */
501 #define SWM_WS_MAX              (22)    /* hard limit */
502 int             workspace_limit = 10;   /* soft limit */
503
504 struct swm_screen {
505         int                     idx;    /* screen index */
506         struct swm_region_list  rl;     /* list of regions on this screen */
507         struct swm_region_list  orl;    /* list of old regions */
508         xcb_window_t            root;
509         struct workspace        ws[SWM_WS_MAX];
510
511         /* colors */
512         struct {
513                 uint32_t        color;
514                 char            *name;
515         } c[SWM_S_COLOR_MAX];
516
517         xcb_gcontext_t          bar_gc;
518 };
519 struct swm_screen       *screens;
520
521 /* args to functions */
522 union arg {
523         int                     id;
524 #define SWM_ARG_ID_FOCUSNEXT    (0)
525 #define SWM_ARG_ID_FOCUSPREV    (1)
526 #define SWM_ARG_ID_FOCUSMAIN    (2)
527 #define SWM_ARG_ID_FOCUSCUR     (4)
528 #define SWM_ARG_ID_SWAPNEXT     (10)
529 #define SWM_ARG_ID_SWAPPREV     (11)
530 #define SWM_ARG_ID_SWAPMAIN     (12)
531 #define SWM_ARG_ID_MOVELAST     (13)
532 #define SWM_ARG_ID_MASTERSHRINK (20)
533 #define SWM_ARG_ID_MASTERGROW   (21)
534 #define SWM_ARG_ID_MASTERADD    (22)
535 #define SWM_ARG_ID_MASTERDEL    (23)
536 #define SWM_ARG_ID_FLIPLAYOUT   (24)
537 #define SWM_ARG_ID_STACKRESET   (30)
538 #define SWM_ARG_ID_STACKINIT    (31)
539 #define SWM_ARG_ID_CYCLEWS_UP   (40)
540 #define SWM_ARG_ID_CYCLEWS_DOWN (41)
541 #define SWM_ARG_ID_CYCLESC_UP   (42)
542 #define SWM_ARG_ID_CYCLESC_DOWN (43)
543 #define SWM_ARG_ID_CYCLEWS_UP_ALL       (44)
544 #define SWM_ARG_ID_CYCLEWS_DOWN_ALL     (45)
545 #define SWM_ARG_ID_STACKINC     (50)
546 #define SWM_ARG_ID_STACKDEC     (51)
547 #define SWM_ARG_ID_SS_ALL       (60)
548 #define SWM_ARG_ID_SS_WINDOW    (61)
549 #define SWM_ARG_ID_DONTCENTER   (70)
550 #define SWM_ARG_ID_CENTER       (71)
551 #define SWM_ARG_ID_KILLWINDOW   (80)
552 #define SWM_ARG_ID_DELETEWINDOW (81)
553 #define SWM_ARG_ID_WIDTHGROW    (90)
554 #define SWM_ARG_ID_WIDTHSHRINK  (91)
555 #define SWM_ARG_ID_HEIGHTGROW   (92)
556 #define SWM_ARG_ID_HEIGHTSHRINK (93)
557 #define SWM_ARG_ID_MOVEUP       (100)
558 #define SWM_ARG_ID_MOVEDOWN     (101)
559 #define SWM_ARG_ID_MOVELEFT     (102)
560 #define SWM_ARG_ID_MOVERIGHT    (103)
561         char                    **argv;
562 };
563
564 void    focus(struct swm_region *, union arg *);
565 void    focus_magic(struct ws_win *);
566
567 /* quirks */
568 struct quirk {
569         TAILQ_ENTRY(quirk)      entry;
570         char                    *class;
571         char                    *name;
572         unsigned long           quirk;
573 #define SWM_Q_FLOAT             (1<<0)  /* float this window */
574 #define SWM_Q_TRANSSZ           (1<<1)  /* transiend window size too small */
575 #define SWM_Q_ANYWHERE          (1<<2)  /* don't position this window */
576 #define SWM_Q_XTERM_FONTADJ     (1<<3)  /* adjust xterm fonts when resizing */
577 #define SWM_Q_FULLSCREEN        (1<<4)  /* remove border */
578 #define SWM_Q_FOCUSPREV         (1<<5)  /* focus on caller */
579 };
580 TAILQ_HEAD(quirk_list, quirk);
581 struct quirk_list               quirks = TAILQ_HEAD_INITIALIZER(quirks);
582
583 /*
584  * Supported EWMH hints should be added to
585  * both the enum and the ewmh array
586  */
587 enum {
588         _NET_ACTIVE_WINDOW,
589         _NET_CLOSE_WINDOW,
590         _NET_MOVERESIZE_WINDOW,
591         _NET_WM_ACTION_CLOSE,
592         _NET_WM_ACTION_FULLSCREEN,
593         _NET_WM_ACTION_MOVE,
594         _NET_WM_ACTION_RESIZE,
595         _NET_WM_ALLOWED_ACTIONS,
596         _NET_WM_STATE,
597         _NET_WM_STATE_ABOVE,
598         _NET_WM_STATE_FULLSCREEN,
599         _NET_WM_STATE_HIDDEN,
600         _NET_WM_STATE_MAXIMIZED_HORZ,
601         _NET_WM_STATE_MAXIMIZED_VERT,
602         _NET_WM_STATE_SKIP_PAGER,
603         _NET_WM_STATE_SKIP_TASKBAR,
604         _NET_WM_WINDOW_TYPE,
605         _NET_WM_WINDOW_TYPE_DIALOG,
606         _NET_WM_WINDOW_TYPE_DOCK,
607         _NET_WM_WINDOW_TYPE_NORMAL,
608         _NET_WM_WINDOW_TYPE_SPLASH,
609         _NET_WM_WINDOW_TYPE_TOOLBAR,
610         _NET_WM_WINDOW_TYPE_UTILITY,
611         _SWM_WM_STATE_MANUAL,
612         SWM_EWMH_HINT_MAX
613 };
614
615 struct ewmh_hint {
616         char            *name;
617         xcb_atom_t      atom;
618 } ewmh[SWM_EWMH_HINT_MAX] =     {
619     /* must be in same order as in the enum */
620     {"_NET_ACTIVE_WINDOW", XCB_ATOM_NONE},
621     {"_NET_CLOSE_WINDOW", XCB_ATOM_NONE},
622     {"_NET_MOVERESIZE_WINDOW", XCB_ATOM_NONE},
623     {"_NET_WM_ACTION_CLOSE", XCB_ATOM_NONE},
624     {"_NET_WM_ACTION_FULLSCREEN", XCB_ATOM_NONE},
625     {"_NET_WM_ACTION_MOVE", XCB_ATOM_NONE},
626     {"_NET_WM_ACTION_RESIZE", XCB_ATOM_NONE},
627     {"_NET_WM_ALLOWED_ACTIONS", XCB_ATOM_NONE},
628     {"_NET_WM_STATE", XCB_ATOM_NONE},
629     {"_NET_WM_STATE_ABOVE", XCB_ATOM_NONE},
630     {"_NET_WM_STATE_FULLSCREEN", XCB_ATOM_NONE},
631     {"_NET_WM_STATE_HIDDEN", XCB_ATOM_NONE},
632     {"_NET_WM_STATE_MAXIMIZED_HORZ", XCB_ATOM_NONE},
633     {"_NET_WM_STATE_MAXIMIZED_VERT", XCB_ATOM_NONE},
634     {"_NET_WM_STATE_SKIP_PAGER", XCB_ATOM_NONE},
635     {"_NET_WM_STATE_SKIP_TASKBAR", XCB_ATOM_NONE},
636     {"_NET_WM_WINDOW_TYPE", XCB_ATOM_NONE},
637     {"_NET_WM_WINDOW_TYPE_DIALOG", XCB_ATOM_NONE},
638     {"_NET_WM_WINDOW_TYPE_DOCK", XCB_ATOM_NONE},
639     {"_NET_WM_WINDOW_TYPE_NORMAL", XCB_ATOM_NONE},
640     {"_NET_WM_WINDOW_TYPE_SPLASH", XCB_ATOM_NONE},
641     {"_NET_WM_WINDOW_TYPE_TOOLBAR", XCB_ATOM_NONE},
642     {"_NET_WM_WINDOW_TYPE_UTILITY", XCB_ATOM_NONE},
643     {"_SWM_WM_STATE_MANUAL", XCB_ATOM_NONE},
644 };
645
646 void             store_float_geom(struct ws_win *, struct swm_region *);
647 int              floating_toggle_win(struct ws_win *);
648 void             constrain_window(struct ws_win *, struct swm_region *, int);
649 void             update_window(struct ws_win *);
650 void             spawn_select(struct swm_region *, union arg *, char *, int *);
651 char            *get_win_name(xcb_window_t);
652 xcb_atom_t       get_atom_from_string(const char *);
653 void            map_window_raised(xcb_window_t);
654 void            do_sync(void);
655 xcb_screen_t    *get_screen(int);
656 int             parse_rgb(const char *, uint16_t *, uint16_t *, uint16_t *);
657
658 int
659 parse_rgb(const char *rgb, uint16_t *rr, uint16_t *gg, uint16_t *bb)
660 {
661         unsigned int    tmpr, tmpg, tmpb;
662
663         if (sscanf(rgb, "rgb:%x/%x/%x", &tmpr, &tmpg, &tmpb) != 3)
664                 return (-1);
665         
666         *rr = tmpr << 8;
667         *gg = tmpg << 8;
668         *bb = tmpb << 8;
669
670         return (0);
671 }
672
673 xcb_screen_t *
674 get_screen(int screen)
675 {
676         xcb_screen_iterator_t i;
677
678         i = xcb_setup_roots_iterator(xcb_get_setup(conn));
679         for (; i.rem; --screen, xcb_screen_next(&i))
680                 if (screen == 0)
681                         return (i.data);
682
683         return (NULL);
684 }
685
686 void
687 do_sync(void)
688 {
689         xcb_get_input_focus_cookie_t    c;
690         xcb_get_input_focus_reply_t     *r;
691
692         /* mimic XSync() */
693         c = xcb_get_input_focus(conn);
694         xcb_flush(conn);
695         r = xcb_get_input_focus_reply(conn, c, NULL);
696         if (r)
697                 free(r);
698 }
699
700 void
701 map_window_raised(xcb_window_t win)
702 {
703         uint32_t        val = XCB_STACK_MODE_ABOVE;
704
705         xcb_configure_window(conn, win,
706                 XCB_CONFIG_WINDOW_STACK_MODE, &val);
707
708         xcb_map_window(conn, win);
709         xcb_flush(conn);
710 }
711
712 xcb_atom_t
713 get_atom_from_string(const char *str)
714 {
715         xcb_intern_atom_cookie_t        c;
716         xcb_intern_atom_reply_t         *r;
717         xcb_atom_t                      atom;
718
719         c = xcb_intern_atom(conn, False, strlen(str), str);
720         r = xcb_intern_atom_reply(conn, c, NULL);
721         if (r) {
722                 atom = r->atom;
723                 free(r);
724
725                 return (atom);
726         }
727
728         return (XCB_ATOM_NONE);
729 }
730
731 void
732 update_iconic(struct ws_win *win, int newv)
733 {
734         int32_t                         v = newv;
735         xcb_atom_t                      iprop;
736
737         win->iconic = newv;
738
739         iprop = get_atom_from_string("_SWM_ICONIC");
740         if (iprop == XCB_ATOM_NONE)
741                 return;
742
743         if (newv)
744                 xcb_change_property(conn, XCB_PROP_MODE_REPLACE, win->id,
745                         iprop, XCB_ATOM_INTEGER, 32, 1, &v);
746         else
747                 xcb_delete_property(conn, win->id, iprop);
748 }
749
750 int32_t
751 get_iconic(struct ws_win *win)
752 {
753         int32_t                         v = 0;
754         xcb_atom_t                      iprop;
755         xcb_get_property_reply_t        *pr = NULL;
756
757         iprop = get_atom_from_string("_SWM_ICONIC");
758         if (iprop == XCB_ATOM_NONE)
759                 goto out;
760
761         pr = xcb_get_property_reply(conn,
762                 xcb_get_property(conn, False, win->id, iprop, XCB_ATOM_INTEGER,
763                         0, 1),
764                 NULL);
765         if (!pr)
766                 goto out;
767         if (pr->type != XCB_ATOM_INTEGER || pr->format != 32)
768                 goto out;
769         v = *((int32_t *)xcb_get_property_value(pr));
770 out:
771         if (pr) 
772                 free(pr);
773         return (v);
774 }
775
776 void
777 setup_ewmh(void)
778 {
779         xcb_atom_t                      sup_list;
780         int                             i, j, num_screens;
781
782         sup_list = get_atom_from_string("_NET_SUPPORTED");
783
784         for (i = 0; i < LENGTH(ewmh); i++)
785                 ewmh[i].atom = get_atom_from_string(ewmh[i].name);
786
787         num_screens = xcb_setup_roots_length(xcb_get_setup(conn));
788         for (i = 0; i < num_screens; i++) {
789                 /* Support check window will be created by workaround(). */
790
791                 /* Report supported atoms */
792                 xcb_delete_property(conn, screens[i].root, sup_list);
793                 for (j = 0; j < LENGTH(ewmh); j++)
794                         xcb_change_property(conn, XCB_PROP_MODE_APPEND,
795                                 screens[i].root, sup_list, XCB_ATOM_ATOM, 32, 1,
796                                 &ewmh[j].atom);
797         }
798 }
799
800 void
801 teardown_ewmh(void)
802 {
803         int                             i, num_screens;
804         xcb_atom_t                      sup_check, sup_list;
805         xcb_window_t                    id;
806         xcb_get_property_cookie_t       pc;
807         xcb_get_property_reply_t        *pr;
808
809         sup_check = get_atom_from_string("_NET_SUPPORTING_WM_CHECK");
810         sup_list = get_atom_from_string("_NET_SUPPORTED");
811         num_screens = xcb_setup_roots_length(xcb_get_setup(conn));
812
813         for (i = 0; i < num_screens; i++) {
814                 /* Get the support check window and destroy it */
815                 pc = xcb_get_property(conn, False, screens[i].root, sup_check,
816                         XCB_ATOM_WINDOW, 0, 1);
817                 pr = xcb_get_property_reply(conn, pc, NULL);
818                 if (pr) {
819                         id = *((xcb_window_t *)xcb_get_property_value(pr));
820
821                         xcb_destroy_window(conn, id);
822                         xcb_delete_property(conn, screens[i].root, sup_check);
823                         xcb_delete_property(conn, screens[i].root, sup_list);
824
825                         free(pr);
826                 }
827         }
828 }
829
830 void
831 ewmh_autoquirk(struct ws_win *win)
832 {
833         int                     i;
834         unsigned long           n;
835         xcb_atom_t              type;
836
837         xcb_get_property_cookie_t       c;
838         xcb_get_property_reply_t        *r;
839
840         c = xcb_get_property(conn, False, win->id,
841                 ewmh[_NET_WM_WINDOW_TYPE].atom, XCB_ATOM_ATOM, 0, (~0L));
842         r = xcb_get_property_reply(conn, c, NULL);
843         if (!r)
844                 return;
845         n = xcb_get_property_value_length(r);
846
847         for (i = 0; i < n; i++) {
848                 type = *((xcb_atom_t *)xcb_get_property_value(r));
849                 if (type == ewmh[_NET_WM_WINDOW_TYPE_NORMAL].atom)
850                         break;
851                 if (type == ewmh[_NET_WM_WINDOW_TYPE_DOCK].atom ||
852                     type == ewmh[_NET_WM_WINDOW_TYPE_TOOLBAR].atom ||
853                     type == ewmh[_NET_WM_WINDOW_TYPE_UTILITY].atom) {
854                         win->floating = 1;
855                         win->quirks = SWM_Q_FLOAT | SWM_Q_ANYWHERE;
856                         break;
857                 }
858                 if (type == ewmh[_NET_WM_WINDOW_TYPE_SPLASH].atom ||
859                     type == ewmh[_NET_WM_WINDOW_TYPE_DIALOG].atom) {
860                         win->floating = 1;
861                         win->quirks = SWM_Q_FLOAT;
862                         break;
863                 }
864         }
865         free(r);
866 }
867
868 #define SWM_EWMH_ACTION_COUNT_MAX       (6)
869 #define EWMH_F_FULLSCREEN               (1<<0)
870 #define EWMH_F_ABOVE                    (1<<1)
871 #define EWMH_F_HIDDEN                   (1<<2)
872 #define EWMH_F_SKIP_PAGER               (1<<3)
873 #define EWMH_F_SKIP_TASKBAR             (1<<4)
874 #define SWM_F_MANUAL                    (1<<5)
875
876 int
877 ewmh_set_win_fullscreen(struct ws_win *win, int fs)
878 {
879         if (!win->ws->r)
880                 return (0);
881
882         if (!win->floating)
883                 return (0);
884
885         DNPRINTF(SWM_D_MISC, "ewmh_set_win_fullscreen: window: 0x%x, "
886             "fullscreen %s\n", win->id, YESNO(fs));
887
888         if (fs) {
889                 if (!win->g_floatvalid)
890                         store_float_geom(win, win->ws->r);
891
892                 win->g = win->ws->r->g;
893                 win->bordered = 0;
894         } else {
895                 if (win->g_floatvalid) {
896                         /* refloat at last floating relative position */
897                         win->g = win->g_float;
898                         X(win) += X(win->ws->r);
899                         Y(win) += Y(win->ws->r);
900                 }
901         }
902
903         return (1);
904 }
905
906 void
907 ewmh_update_actions(struct ws_win *win)
908 {
909         xcb_atom_t              actions[SWM_EWMH_ACTION_COUNT_MAX];
910         int                     n = 0;
911
912         if (win == NULL)
913                 return;
914
915         actions[n++] = ewmh[_NET_WM_ACTION_CLOSE].atom;
916
917         if (win->floating) {
918                 actions[n++] = ewmh[_NET_WM_ACTION_MOVE].atom;
919                 actions[n++] = ewmh[_NET_WM_ACTION_RESIZE].atom;
920         }
921
922         xcb_change_property(conn, XCB_PROP_MODE_REPLACE, win->id,
923                 ewmh[_NET_WM_ALLOWED_ACTIONS].atom, XCB_ATOM_ATOM, 32, 1,
924                 actions);
925 }
926
927 #define _NET_WM_STATE_REMOVE    0    /* remove/unset property */
928 #define _NET_WM_STATE_ADD       1    /* add/set property */
929 #define _NET_WM_STATE_TOGGLE    2    /* toggle property */
930
931 void
932 ewmh_update_win_state(struct ws_win *win, long state, long action)
933 {
934         unsigned int            mask = 0;
935         unsigned int            changed = 0;
936         unsigned int            orig_flags;
937
938         if (win == NULL)
939                 return;
940
941         if (state == ewmh[_NET_WM_STATE_FULLSCREEN].atom)
942                 mask = EWMH_F_FULLSCREEN;
943         if (state == ewmh[_NET_WM_STATE_ABOVE].atom)
944                 mask = EWMH_F_ABOVE;
945         if (state == ewmh[_SWM_WM_STATE_MANUAL].atom)
946                 mask = SWM_F_MANUAL;
947         if (state == ewmh[_NET_WM_STATE_SKIP_PAGER].atom)
948                 mask = EWMH_F_SKIP_PAGER;
949         if (state == ewmh[_NET_WM_STATE_SKIP_TASKBAR].atom)
950                 mask = EWMH_F_SKIP_TASKBAR;
951
952
953         orig_flags = win->ewmh_flags;
954
955         switch (action) {
956         case _NET_WM_STATE_REMOVE:
957                 win->ewmh_flags &= ~mask;
958                 break;
959         case _NET_WM_STATE_ADD:
960                 win->ewmh_flags |= mask;
961                 break;
962         case _NET_WM_STATE_TOGGLE:
963                 win->ewmh_flags ^= mask;
964                 break;
965         }
966
967         changed = (win->ewmh_flags & mask) ^ (orig_flags & mask) ? 1 : 0;
968
969         if (state == ewmh[_NET_WM_STATE_ABOVE].atom)
970                 if (changed)
971                         if (!floating_toggle_win(win))
972                                 win->ewmh_flags = orig_flags; /* revert */
973         if (state == ewmh[_SWM_WM_STATE_MANUAL].atom)
974                 if (changed)
975                         win->manual = (win->ewmh_flags & SWM_F_MANUAL) != 0;
976         if (state == ewmh[_NET_WM_STATE_FULLSCREEN].atom)
977                 if (changed)
978                         if (!ewmh_set_win_fullscreen(win,
979                             win->ewmh_flags & EWMH_F_FULLSCREEN))
980                                 win->ewmh_flags = orig_flags; /* revert */
981
982         xcb_delete_property(conn, win->id, ewmh[_NET_WM_STATE].atom);
983
984         if (win->ewmh_flags & EWMH_F_FULLSCREEN)
985                 xcb_change_property(conn, XCB_PROP_MODE_APPEND, win->id,
986                         ewmh[_NET_WM_STATE].atom, XCB_ATOM_ATOM, 32, 1,
987                         &ewmh[_NET_WM_STATE_FULLSCREEN].atom);
988         if (win->ewmh_flags & EWMH_F_SKIP_PAGER)
989                 xcb_change_property(conn, XCB_PROP_MODE_APPEND, win->id,
990                         ewmh[_NET_WM_STATE].atom, XCB_ATOM_ATOM, 32, 1,
991                         &ewmh[_NET_WM_STATE_SKIP_PAGER].atom);
992         if (win->ewmh_flags & EWMH_F_SKIP_TASKBAR)
993                 xcb_change_property(conn, XCB_PROP_MODE_APPEND, win->id,
994                         ewmh[_NET_WM_STATE].atom, XCB_ATOM_ATOM, 32, 1,
995                         &ewmh[_NET_WM_STATE_SKIP_TASKBAR].atom);
996         if (win->ewmh_flags & EWMH_F_ABOVE)
997                 xcb_change_property(conn, XCB_PROP_MODE_APPEND, win->id,
998                         ewmh[_NET_WM_STATE].atom, XCB_ATOM_ATOM, 32, 1,
999                         &ewmh[_NET_WM_STATE_ABOVE].atom);
1000         if (win->ewmh_flags & SWM_F_MANUAL)
1001                 xcb_change_property(conn, XCB_PROP_MODE_APPEND, win->id,
1002                         ewmh[_NET_WM_STATE].atom, XCB_ATOM_ATOM, 32, 1,
1003                         &ewmh[_SWM_WM_STATE_MANUAL].atom);
1004 }
1005
1006 void
1007 ewmh_get_win_state(struct ws_win *win)
1008 {
1009         xcb_atom_t                      *states;
1010         xcb_get_property_cookie_t       c;
1011         xcb_get_property_reply_t        *r;
1012         int                             i, n;
1013
1014         if (win == NULL)
1015                 return;
1016
1017         win->ewmh_flags = 0;
1018         if (win->floating)
1019                 win->ewmh_flags |= EWMH_F_ABOVE;
1020         if (win->manual)
1021                 win->ewmh_flags |= SWM_F_MANUAL;
1022
1023         c = xcb_get_property(conn, False, win->id, ewmh[_NET_WM_STATE].atom,
1024                 XCB_ATOM_ATOM, 0, (~0L));
1025         r = xcb_get_property_reply(conn, c, NULL);
1026         if (!r)
1027                 return;
1028
1029         states = xcb_get_property_value(r);
1030         n = xcb_get_property_value_length(r);
1031
1032         for (i = 0; i < n; i++)
1033                 ewmh_update_win_state(win, states[i], _NET_WM_STATE_ADD);
1034
1035         free(r);
1036 }
1037
1038 /* events */
1039 #ifdef SWM_DEBUG
1040 char *
1041 geteventname(XEvent *e)
1042 {
1043         char                    *name = NULL;
1044
1045         switch (e->type) {
1046         case KeyPress:
1047                 name = "KeyPress";
1048                 break;
1049         case KeyRelease:
1050                 name = "KeyRelease";
1051                 break;
1052         case ButtonPress:
1053                 name = "ButtonPress";
1054                 break;
1055         case ButtonRelease:
1056                 name = "ButtonRelease";
1057                 break;
1058         case MotionNotify:
1059                 name = "MotionNotify";
1060                 break;
1061         case EnterNotify:
1062                 name = "EnterNotify";
1063                 break;
1064         case LeaveNotify:
1065                 name = "LeaveNotify";
1066                 break;
1067         case FocusIn:
1068                 name = "FocusIn";
1069                 break;
1070         case FocusOut:
1071                 name = "FocusOut";
1072                 break;
1073         case KeymapNotify:
1074                 name = "KeymapNotify";
1075                 break;
1076         case Expose:
1077                 name = "Expose";
1078                 break;
1079         case GraphicsExpose:
1080                 name = "GraphicsExpose";
1081                 break;
1082         case NoExpose:
1083                 name = "NoExpose";
1084                 break;
1085         case VisibilityNotify:
1086                 name = "VisibilityNotify";
1087                 break;
1088         case CreateNotify:
1089                 name = "CreateNotify";
1090                 break;
1091         case DestroyNotify:
1092                 name = "DestroyNotify";
1093                 break;
1094         case UnmapNotify:
1095                 name = "UnmapNotify";
1096                 break;
1097         case MapNotify:
1098                 name = "MapNotify";
1099                 break;
1100         case MapRequest:
1101                 name = "MapRequest";
1102                 break;
1103         case ReparentNotify:
1104                 name = "ReparentNotify";
1105                 break;
1106         case ConfigureNotify:
1107                 name = "ConfigureNotify";
1108                 break;
1109         case ConfigureRequest:
1110                 name = "ConfigureRequest";
1111                 break;
1112         case GravityNotify:
1113                 name = "GravityNotify";
1114                 break;
1115         case ResizeRequest:
1116                 name = "ResizeRequest";
1117                 break;
1118         case CirculateNotify:
1119                 name = "CirculateNotify";
1120                 break;
1121         case CirculateRequest:
1122                 name = "CirculateRequest";
1123                 break;
1124         case PropertyNotify:
1125                 name = "PropertyNotify";
1126                 break;
1127         case SelectionClear:
1128                 name = "SelectionClear";
1129                 break;
1130         case SelectionRequest:
1131                 name = "SelectionRequest";
1132                 break;
1133         case SelectionNotify:
1134                 name = "SelectionNotify";
1135                 break;
1136         case ColormapNotify:
1137                 name = "ColormapNotify";
1138                 break;
1139         case ClientMessage:
1140                 name = "ClientMessage";
1141                 break;
1142         case MappingNotify:
1143                 name = "MappingNotify";
1144                 break;
1145         default:
1146                 name = "Unknown";
1147         }
1148
1149         return (name);
1150 }
1151
1152 char *
1153 xrandr_geteventname(XEvent *e)
1154 {
1155         char                    *name = NULL;
1156
1157         switch(e->type - xrandr_eventbase) {
1158         case RRScreenChangeNotify:
1159                 name = "RRScreenChangeNotify";
1160                 break;
1161         default:
1162                 name = "Unknown";
1163         }
1164
1165         return (name);
1166 }
1167
1168 void
1169 dumpwins(struct swm_region *r, union arg *args)
1170 {
1171         struct ws_win                           *win;
1172         uint16_t                                state;
1173         xcb_get_window_attributes_cookie_t      c;
1174         xcb_get_window_attributes_reply_t       *wa;
1175
1176         if (r->ws == NULL) {
1177                 warnx("dumpwins: invalid workspace");
1178                 return;
1179         }
1180
1181         warnx("=== managed window list ws %02d ===", r->ws->idx);
1182         TAILQ_FOREACH(win, &r->ws->winlist, entry) {
1183                 state = getstate(win->id);
1184                 c = xcb_get_window_attributes(conn, win->id);
1185                 wa = xcb_get_window_attributes_reply(conn, c, NULL);
1186                 if (wa) {
1187                         warnx("window: 0x%x, map_state: %d, state: %u, "
1188                                 "transient: 0x%x", win->id, wa->map_state,
1189                                 state, win->transient);
1190                         free(wa);
1191                 } else
1192                         warnx("window: 0x%x, failed xcb_get_window_attributes",
1193                             win->id);
1194         }
1195
1196         warnx("===== unmanaged window list =====");
1197         TAILQ_FOREACH(win, &r->ws->unmanagedlist, entry) {
1198                 state = getstate(win->id);
1199                 c = xcb_get_window_attributes(conn, win->id);
1200                 wa = xcb_get_window_attributes_reply(conn, c, NULL);
1201                 if (wa) {
1202                         warnx("window: 0x%x, map_state: %d, state: %u, "
1203                                 "transient: 0x%x", win->id, wa->map_state,
1204                                 state, win->transient);
1205                         free(wa);
1206                 } else
1207                         warnx("window: 0x%x, failed xcb_get_window_attributes",
1208                             win->id);
1209         }
1210
1211         warnx("=================================");
1212 }
1213 #else
1214 void
1215 dumpwins(struct swm_region *r, union arg *args)
1216 {
1217 }
1218 #endif /* SWM_DEBUG */
1219
1220 void                    expose(XEvent *);
1221 void                    keypress(XEvent *);
1222 void                    buttonpress(XEvent *);
1223 void                    configurerequest(XEvent *);
1224 void                    configurenotify(XEvent *);
1225 void                    destroynotify(XEvent *);
1226 void                    enternotify(XEvent *);
1227 void                    focusevent(XEvent *);
1228 void                    mapnotify(XEvent *);
1229 void                    mappingnotify(XEvent *);
1230 void                    maprequest(XEvent *);
1231 void                    propertynotify(XEvent *);
1232 void                    unmapnotify(XEvent *);
1233 void                    visibilitynotify(XEvent *);
1234 void                    clientmessage(XEvent *);
1235
1236 void                    (*handler[LASTEvent])(XEvent *) = {
1237                                 [Expose] = expose,
1238                                 [KeyPress] = keypress,
1239                                 [ButtonPress] = buttonpress,
1240                                 [ConfigureRequest] = configurerequest,
1241                                 [ConfigureNotify] = configurenotify,
1242                                 [DestroyNotify] = destroynotify,
1243                                 [EnterNotify] = enternotify,
1244                                 [FocusIn] = focusevent,
1245                                 [FocusOut] = focusevent,
1246                                 [MapNotify] = mapnotify,
1247                                 [MappingNotify] = mappingnotify,
1248                                 [MapRequest] = maprequest,
1249                                 [PropertyNotify] = propertynotify,
1250                                 [UnmapNotify] = unmapnotify,
1251                                 [VisibilityNotify] = visibilitynotify,
1252                                 [ClientMessage] = clientmessage,
1253 };
1254
1255 void
1256 sighdlr(int sig)
1257 {
1258         int                     saved_errno, status;
1259         pid_t                   pid;
1260
1261         saved_errno = errno;
1262
1263         switch (sig) {
1264         case SIGCHLD:
1265                 while ((pid = waitpid(WAIT_ANY, &status, WNOHANG)) != 0) {
1266                         if (pid == -1) {
1267                                 if (errno == EINTR)
1268                                         continue;
1269 #ifdef SWM_DEBUG
1270                                 if (errno != ECHILD)
1271                                         warn("sighdlr: waitpid");
1272 #endif /* SWM_DEBUG */
1273                                 break;
1274                         }
1275                         if (pid == searchpid)
1276                                 search_resp = 1;
1277
1278 #ifdef SWM_DEBUG
1279                         if (WIFEXITED(status)) {
1280                                 if (WEXITSTATUS(status) != 0)
1281                                         warnx("sighdlr: child exit status: %d",
1282                                             WEXITSTATUS(status));
1283                         } else
1284                                 warnx("sighdlr: child is terminated "
1285                                     "abnormally");
1286 #endif /* SWM_DEBUG */
1287                 }
1288                 break;
1289
1290         case SIGHUP:
1291                 restart_wm = 1;
1292                 break;
1293         case SIGINT:
1294         case SIGTERM:
1295         case SIGQUIT:
1296                 running = 0;
1297                 break;
1298         }
1299
1300         errno = saved_errno;
1301 }
1302
1303 struct pid_e *
1304 find_pid(long pid)
1305 {
1306         struct pid_e            *p = NULL;
1307
1308         DNPRINTF(SWM_D_MISC, "find_pid: %lu\n", pid);
1309
1310         if (pid == 0)
1311                 return (NULL);
1312
1313         TAILQ_FOREACH(p, &pidlist, entry) {
1314                 if (p->pid == pid)
1315                         return (p);
1316         }
1317
1318         return (NULL);
1319 }
1320
1321 uint32_t
1322 name_to_color(const char *colorname)
1323 {
1324         uint32_t                        result = 0;
1325         char                            cname[32] = "#";
1326         xcb_screen_t                    *screen;
1327         xcb_colormap_t                  cmap;
1328         xcb_alloc_color_reply_t         *cr;
1329         xcb_alloc_named_color_reply_t   *nr;
1330         uint16_t                        rr, gg, bb;
1331
1332         screen = xcb_setup_roots_iterator(xcb_get_setup(conn)).data;
1333         cmap = screen->default_colormap;
1334
1335         /* color is in format rgb://rr/gg/bb */
1336         if (strncmp(colorname, "rgb:", 4) == 0) {
1337                 if (parse_rgb(colorname, &rr, &gg, &bb) == -1)
1338                         warnx("could not parse rgb %s", colorname);
1339                 else {
1340                         cr = xcb_alloc_color_reply(conn,
1341                                 xcb_alloc_color(conn, cmap, rr, gg, bb),
1342                                 NULL);
1343                         if (cr) {
1344                                 result = cr->pixel;
1345                                 free(cr);
1346                         } else
1347                                 warnx("color '%s' not found", colorname);
1348                 }
1349         } else {
1350                 nr = xcb_alloc_named_color_reply(conn,
1351                         xcb_alloc_named_color(conn, cmap, strlen(colorname),
1352                                 colorname),
1353                         NULL);
1354                 if (!nr) {
1355                         strlcat(cname, colorname + 2, sizeof cname - 1);
1356                         nr = xcb_alloc_named_color_reply(conn,
1357                                 xcb_alloc_named_color(conn, cmap, strlen(cname),
1358                                         cname),
1359                                 NULL);
1360                 }
1361                 if (nr) {
1362                         result = nr->pixel;
1363                         free(nr);
1364                 } else
1365                         warnx("color '%s' not found", colorname);
1366         }
1367
1368         return (result);
1369 }
1370
1371 void
1372 setscreencolor(char *val, int i, int c)
1373 {
1374         int     num_screens;
1375
1376         num_screens = xcb_setup_roots_length(xcb_get_setup(conn));
1377         if (i > 0 && i <= num_screens) {
1378                 screens[i - 1].c[c].color = name_to_color(val);
1379                 free(screens[i - 1].c[c].name);
1380                 if ((screens[i - 1].c[c].name = strdup(val)) == NULL)
1381                         err(1, "strdup");
1382         } else if (i == -1) {
1383                 for (i = 0; i < num_screens; i++) {
1384                         screens[i].c[c].color = name_to_color(val);
1385                         free(screens[i].c[c].name);
1386                         if ((screens[i].c[c].name = strdup(val)) == NULL)
1387                                 err(1, "strdup");
1388                 }
1389         } else
1390                 errx(1, "invalid screen index: %d out of bounds (maximum %d)",
1391                     i, num_screens);
1392 }
1393
1394 void
1395 fancy_stacker(struct workspace *ws)
1396 {
1397         strlcpy(ws->stacker, "[   ]", sizeof ws->stacker);
1398         if (ws->cur_layout->l_stack == vertical_stack)
1399                 snprintf(ws->stacker, sizeof ws->stacker,
1400                     ws->l_state.vertical_flip ? "[%d>%d]" : "[%d|%d]",
1401                     ws->l_state.vertical_mwin, ws->l_state.vertical_stacks);
1402         if (ws->cur_layout->l_stack == horizontal_stack)
1403                 snprintf(ws->stacker, sizeof ws->stacker,
1404                     ws->l_state.horizontal_flip ? "[%dv%d]" : "[%d-%d]",
1405                     ws->l_state.horizontal_mwin, ws->l_state.horizontal_stacks);
1406 }
1407
1408 void
1409 plain_stacker(struct workspace *ws)
1410 {
1411         strlcpy(ws->stacker, "[ ]", sizeof ws->stacker);
1412         if (ws->cur_layout->l_stack == vertical_stack)
1413                 strlcpy(ws->stacker, ws->l_state.vertical_flip ? "[>]" : "[|]",
1414                     sizeof ws->stacker);
1415         if (ws->cur_layout->l_stack == horizontal_stack)
1416                 strlcpy(ws->stacker, ws->l_state.horizontal_flip ? "[v]" : "[-]",
1417                     sizeof ws->stacker);
1418 }
1419
1420 void
1421 custom_region(char *val)
1422 {
1423         unsigned int                    sidx, x, y, w, h;
1424         int                             num_screens;
1425         xcb_screen_t                    *screen;
1426
1427         num_screens = xcb_setup_roots_length(xcb_get_setup(conn));
1428         if (sscanf(val, "screen[%u]:%ux%u+%u+%u", &sidx, &w, &h, &x, &y) != 5)
1429                 errx(1, "invalid custom region, "
1430                     "should be 'screen[<n>]:<n>x<n>+<n>+<n>");
1431         if (sidx < 1 || sidx > num_screens)
1432                 errx(1, "invalid screen index: %d out of bounds (maximum %d)",
1433                     sidx, num_screens);
1434         sidx--;
1435
1436         screen = get_screen(sidx);
1437         if (w < 1 || h < 1)
1438                 errx(1, "region %ux%u+%u+%u too small", w, h, x, y);
1439
1440         if (x > screen->width_in_pixels ||
1441             y > screen->height_in_pixels ||
1442             w + x > screen->width_in_pixels ||
1443             h + y > screen->height_in_pixels) {
1444                 warnx("ignoring region %ux%u+%u+%u - not within screen "
1445                     "boundaries (%ux%u)", w, h, x, y,
1446                     screen->width_in_pixels, screen->height_in_pixels);
1447                 return;
1448         }
1449
1450         new_region(&screens[sidx], x, y, w, h);
1451 }
1452
1453 void
1454 socket_setnonblock(int fd)
1455 {
1456         int                     flags;
1457
1458         if ((flags = fcntl(fd, F_GETFL, 0)) == -1)
1459                 err(1, "fcntl F_GETFL");
1460         flags |= O_NONBLOCK;
1461         if ((flags = fcntl(fd, F_SETFL, flags)) == -1)
1462                 err(1, "fcntl F_SETFL");
1463 }
1464
1465 void
1466 bar_print(struct swm_region *r, const char *s)
1467 {
1468         int                     x = 0;
1469         size_t                  len;
1470         xcb_rectangle_t         rect;
1471         uint32_t                gcv[1];
1472         XRectangle              ibox, lbox;
1473
1474         len = strlen(s);
1475         XmbTextExtents(bar_fs, s, len, &ibox, &lbox);
1476
1477         switch (bar_justify) {
1478         case SWM_BAR_JUSTIFY_LEFT:
1479                 x = SWM_BAR_OFFSET;
1480                 break;
1481         case SWM_BAR_JUSTIFY_CENTER:
1482                 x = (WIDTH(r) - lbox.width) / 2;
1483                 break;
1484         case SWM_BAR_JUSTIFY_RIGHT:
1485                 x = WIDTH(r) - lbox.width - SWM_BAR_OFFSET;
1486                 break;
1487         }
1488
1489         if (x < SWM_BAR_OFFSET)
1490                 x = SWM_BAR_OFFSET;
1491
1492         rect.x = 0;
1493         rect.y = 0;
1494         rect.width = WIDTH(r->bar);
1495         rect.height = HEIGHT(r->bar);
1496
1497         /* clear back buffer */
1498         gcv[0] = r->s->c[SWM_S_COLOR_BAR].color;
1499         xcb_change_gc(conn, r->s->bar_gc, XCB_GC_FOREGROUND, gcv);
1500         xcb_poly_fill_rectangle(conn, r->bar->buffer, r->s->bar_gc,
1501                 sizeof(rect), &rect);
1502
1503         /* draw back buffer */
1504         gcv[0] = r->s->c[SWM_S_COLOR_BAR_FONT].color;
1505         xcb_change_gc(conn, r->s->bar_gc, XCB_GC_FOREGROUND, gcv);
1506         xcb_image_text_8(conn, len, r->bar->buffer, r->s->bar_gc, x,
1507                 (bar_fs_extents->max_logical_extent.height - lbox.height) / 2 -
1508                 lbox.y, s);
1509
1510         /* blt */
1511         xcb_copy_area(conn, r->bar->buffer, r->bar->id, r->s->bar_gc, 0, 0,
1512                 0, 0, WIDTH(r->bar), HEIGHT(r->bar));
1513 }
1514
1515 void
1516 bar_extra_stop(void)
1517 {
1518         if (bar_pipe[0]) {
1519                 close(bar_pipe[0]);
1520                 bzero(bar_pipe, sizeof bar_pipe);
1521         }
1522         if (bar_pid) {
1523                 kill(bar_pid, SIGTERM);
1524                 bar_pid = 0;
1525         }
1526         strlcpy((char *)bar_ext, "", sizeof bar_ext);
1527         bar_extra = 0;
1528 }
1529
1530 void
1531 bar_class_name(char *s, size_t sz, struct swm_region *r)
1532 {
1533         if (r == NULL || r->ws == NULL || r->ws->focus == NULL)
1534                 return;
1535         if (r->ws->focus->ch.class_name != NULL)
1536                 strlcat(s, r->ws->focus->ch.class_name, sz);
1537 }
1538
1539 void
1540 bar_title_name(char *s, size_t sz, struct swm_region *r)
1541 {
1542         if (r == NULL || r->ws == NULL || r->ws->focus == NULL)
1543                 return;
1544         if (r->ws->focus->ch.instance_name != NULL)
1545                 strlcat(s, r->ws->focus->ch.instance_name, sz);
1546 }
1547
1548 void
1549 bar_class_title_name(char *s, size_t sz, struct swm_region *r)
1550 {
1551         if (r == NULL || r->ws == NULL || r->ws->focus == NULL)
1552                 return;
1553
1554         bar_class_name(s, sz, r);
1555         strlcat(s, ":", sz);
1556         bar_title_name(s, sz, r);
1557 }
1558
1559 void
1560 bar_window_float(char *s, size_t sz, struct swm_region *r)
1561 {
1562         if (r == NULL || r ->ws == NULL || r->ws->focus == NULL)
1563                 return;
1564         if (r->ws->focus->floating)
1565                 strlcat(s, "(f)", sz);
1566 }
1567
1568 void
1569 bar_window_name(char *s, size_t sz, struct swm_region *r)
1570 {
1571         char            *title;
1572
1573         if (r == NULL || r->ws == NULL || r->ws->focus == NULL)
1574                 return;
1575         if ((title = get_win_name(r->ws->focus->id)) == NULL)
1576                 return;
1577
1578         strlcat(s, title, sz);
1579         free(title);
1580 }
1581
1582 int             urgent[SWM_WS_MAX];
1583 void
1584 bar_urgent(char *s, size_t sz)
1585 {
1586         struct ws_win           *win;
1587         int                     i, j, num_screens;
1588         char                    b[8];
1589         xcb_get_property_cookie_t       c;
1590         xcb_icccm_wm_hints_t    hints;
1591
1592         for (i = 0; i < workspace_limit; i++)
1593                 urgent[i] = 0;
1594
1595         num_screens = xcb_setup_roots_length(xcb_get_setup(conn));
1596         for (i = 0; i < num_screens; i++)
1597                 for (j = 0; j < workspace_limit; j++)
1598                         TAILQ_FOREACH(win, &screens[i].ws[j].winlist, entry) {
1599                                 c = xcb_icccm_get_wm_hints(conn, win->id);
1600                                 if (xcb_icccm_get_wm_hints_reply(conn, c,
1601                                                 &hints, NULL) == 0)
1602                                         continue;
1603                                 if (hints.flags & XCB_ICCCM_WM_HINT_X_URGENCY)
1604                                         urgent[j] = 1;
1605                         }
1606
1607         for (i = 0; i < workspace_limit; i++) {
1608                 if (urgent[i])
1609                         snprintf(b, sizeof b, "%d ", i + 1);
1610                 else
1611                         snprintf(b, sizeof b, "- ");
1612                 strlcat(s, b, sz);
1613         }
1614 }
1615
1616 void
1617 bar_workspace_name(char *s, size_t sz, struct swm_region *r)
1618 {
1619         if (r == NULL || r->ws == NULL)
1620                 return;
1621         if (r->ws->name != NULL)
1622                 strlcat(s, r->ws->name, sz);
1623 }
1624
1625 /* build the default bar format according to the defined enabled options */
1626 void
1627 bar_fmt(const char *fmtexp, char *fmtnew, struct swm_region *r, size_t sz)
1628 {
1629         /* if format provided, just copy the buffers */
1630         if (bar_format != NULL) {
1631                 strlcpy(fmtnew, fmtexp, sz);
1632                 return;
1633         }
1634
1635         /* reset the output buffer */
1636         *fmtnew = '\0';
1637
1638         strlcat(fmtnew, "+N:+I ", sz);
1639         if (stack_enabled)
1640                 strlcat(fmtnew, "+S", sz);
1641         strlcat(fmtnew, " ", sz);
1642
1643         /* only show the workspace name if there's actually one */
1644         if (r != NULL && r->ws != NULL && r->ws->name != NULL)
1645                 strlcat(fmtnew, "<+D>", sz);
1646         strlcat(fmtnew, "+3<", sz);
1647
1648         if (clock_enabled) {
1649                 strlcat(fmtnew, fmtexp, sz);
1650                 strlcat(fmtnew, "+4<", sz);
1651         }
1652
1653         /* bar_urgent already adds the space before the last asterisk */
1654         if (urgent_enabled)
1655                 strlcat(fmtnew, "* +U*+4<", sz);
1656
1657         if (title_class_enabled) {
1658                 strlcat(fmtnew, "+C", sz);
1659                 if (title_name_enabled == 0)
1660                         strlcat(fmtnew, "+4<", sz);
1661         }
1662
1663         /* checks needed by the colon and floating strlcat(3) calls below */
1664         if (r != NULL && r->ws != NULL && r->ws->focus != NULL) {
1665                 if (title_name_enabled) {
1666                         if (title_class_enabled)
1667                                 strlcat(fmtnew, ":", sz);
1668                         strlcat(fmtnew, "+T+4<", sz);
1669                 }
1670                 if (window_name_enabled) {
1671                         if (r->ws->focus->floating)
1672                                 strlcat(fmtnew, "+F ", sz);
1673                         strlcat(fmtnew, "+64W ", sz);
1674                 }
1675         }
1676
1677         /* finally add the action script output and the version */
1678         strlcat(fmtnew, "+4<+A+4<+V", sz);
1679 }
1680
1681 void
1682 bar_replace_pad(char *tmp, int *limit, size_t sz)
1683 {
1684         /* special case; no limit given, pad one space, instead */
1685         if (*limit == sz - 1)
1686                 *limit = 1;
1687         snprintf(tmp, sz, "%*s", *limit, " ");
1688 }
1689
1690 /* replaces the bar format character sequences (like in tmux(1)) */
1691 char *
1692 bar_replace_seq(char *fmt, char *fmtrep, struct swm_region *r, size_t *offrep,
1693     size_t sz)
1694 {
1695         char                    *ptr;
1696         char                    tmp[SWM_BAR_MAX];
1697         int                     limit, size;
1698         size_t                  len;
1699
1700         /* reset strlcat(3) buffer */
1701         *tmp = '\0';
1702
1703         /* get number, if any */
1704         fmt++;
1705         size = 0;
1706         if (sscanf(fmt, "%d%n", &limit, &size) != 1)
1707                 limit = sizeof tmp - 1;
1708         if (limit <= 0 || limit >= sizeof tmp)
1709                 limit = sizeof tmp - 1;
1710
1711         /* there is nothing to replace (ie EOL) */
1712         fmt += size;
1713         if (*fmt == '\0')
1714                 return (fmt);
1715
1716         switch (*fmt) {
1717         case '<':
1718                 bar_replace_pad(tmp, &limit, sizeof tmp);
1719                 break;
1720         case 'A':
1721                 snprintf(tmp, sizeof tmp, "%s", bar_ext);
1722                 break;
1723         case 'C':
1724                 bar_class_name(tmp, sizeof tmp, r);
1725                 break;
1726         case 'D':
1727                 bar_workspace_name(tmp, sizeof tmp, r);
1728                 break;
1729         case 'F':
1730                 bar_window_float(tmp, sizeof tmp, r);
1731                 break;
1732         case 'I':
1733                 snprintf(tmp, sizeof tmp, "%d", r->ws->idx + 1);
1734                 break;
1735         case 'N':
1736                 snprintf(tmp, sizeof tmp, "%d", r->s->idx + 1);
1737                 break;
1738         case 'P':
1739                 bar_class_title_name(tmp, sizeof tmp, r);
1740                 break;
1741         case 'S':
1742                 snprintf(tmp, sizeof tmp, "%s", r->ws->stacker);
1743                 break;
1744         case 'T':
1745                 bar_title_name(tmp, sizeof tmp, r);
1746                 break;
1747         case 'U':
1748                 bar_urgent(tmp, sizeof tmp);
1749                 break;
1750         case 'V':
1751                 snprintf(tmp, sizeof tmp, "%s", bar_vertext);
1752                 break;
1753         case 'W':
1754                 bar_window_name(tmp, sizeof tmp, r);
1755                 break;
1756         default:
1757                 /* unknown character sequence; copy as-is */
1758                 snprintf(tmp, sizeof tmp, "+%c", *fmt);
1759                 break;
1760         }
1761
1762         len = strlen(tmp);
1763         ptr = tmp;
1764         if (len < limit)
1765                 limit = len;
1766         while (limit-- > 0) {
1767                 if (*offrep >= sz - 1)
1768                         break;
1769                 fmtrep[(*offrep)++] = *ptr++;
1770         }
1771
1772         fmt++;
1773         return (fmt);
1774 }
1775
1776 void
1777 bar_replace(char *fmt, char *fmtrep, struct swm_region *r, size_t sz)
1778 {
1779         size_t                  off;
1780
1781         off = 0;
1782         while (*fmt != '\0') {
1783                 if (*fmt != '+') {
1784                         /* skip ordinary characters */
1785                         if (off >= sz - 1)
1786                                 break;
1787                         fmtrep[off++] = *fmt++;
1788                         continue;
1789                 }
1790
1791                 /* character sequence found; replace it */
1792                 fmt = bar_replace_seq(fmt, fmtrep, r, &off, sz);
1793                 if (off >= sz - 1)
1794                         break;
1795         }
1796
1797         fmtrep[off] = '\0';
1798 }
1799
1800 void
1801 bar_fmt_expand(char *fmtexp, size_t sz)
1802 {
1803         char                    *fmt = NULL;
1804         size_t                  len;
1805         struct tm               tm;
1806         time_t                  tmt;
1807
1808         /* start by grabbing the current time and date */
1809         time(&tmt);
1810         localtime_r(&tmt, &tm);
1811
1812         /* figure out what to expand */
1813         if (bar_format != NULL)
1814                 fmt = bar_format;
1815         else if (bar_format == NULL && clock_enabled)
1816                 fmt = clock_format;
1817         /* if nothing to expand bail out */
1818         if (fmt == NULL) {
1819                 *fmtexp = '\0';
1820                 return;
1821         }
1822
1823         /* copy as-is, just in case the format shouldn't be expanded below */
1824         strlcpy(fmtexp, fmt, sz);
1825         /* finally pass the string through strftime(3) */
1826 #ifndef SWM_DENY_CLOCK_FORMAT
1827         if ((len = strftime(fmtexp, sz, fmt, &tm)) == 0)
1828                 warnx("format too long");
1829         fmtexp[len] = '\0';
1830 #endif
1831 }
1832
1833 void
1834 bar_fmt_print(void)
1835 {
1836         char                    fmtexp[SWM_BAR_MAX], fmtnew[SWM_BAR_MAX];
1837         char                    fmtrep[SWM_BAR_MAX];
1838         int                     i, num_screens;
1839         struct swm_region       *r;
1840
1841         /* expand the format by first passing it through strftime(3) */
1842         bar_fmt_expand(fmtexp, sizeof fmtexp);
1843
1844         num_screens = xcb_setup_roots_length(xcb_get_setup(conn));
1845         for (i = 0; i < num_screens; i++) {
1846                 TAILQ_FOREACH(r, &screens[i].rl, entry) {
1847                         if (r->bar == NULL)
1848                                 continue;
1849                         bar_fmt(fmtexp, fmtnew, r, sizeof fmtnew);
1850                         bar_replace(fmtnew, fmtrep, r, sizeof fmtrep);
1851                         bar_print(r, fmtrep);
1852                 }
1853         }
1854 }
1855
1856 void
1857 bar_update(void)
1858 {
1859         size_t                  len;
1860         char                    *b;
1861
1862         if (bar_enabled == 0)
1863                 return;
1864         if (bar_extra && bar_extra_running) {
1865                 /* ignore short reads; it'll correct itself */
1866                 while ((b = fgetln(stdin, &len)) != NULL)
1867                         if (b && b[len - 1] == '\n') {
1868                                 b[len - 1] = '\0';
1869                                 strlcpy((char *)bar_ext, b, sizeof bar_ext);
1870                         }
1871                 if (b == NULL && errno != EAGAIN) {
1872                         warn("bar_update: bar_extra failed");
1873                         bar_extra_stop();
1874                 }
1875         } else
1876                 strlcpy((char *)bar_ext, "", sizeof bar_ext);
1877
1878         bar_fmt_print();
1879         alarm(bar_delay);
1880 }
1881
1882 void
1883 bar_signal(int sig)
1884 {
1885         bar_alarm = 1;
1886 }
1887
1888 void
1889 bar_toggle(struct swm_region *r, union arg *args)
1890 {
1891         struct swm_region       *tmpr;
1892         int                     i, num_screens;
1893
1894         DNPRINTF(SWM_D_BAR, "bar_toggle\n");
1895
1896         num_screens = xcb_setup_roots_length(xcb_get_setup(conn));
1897         if (bar_enabled) {
1898                 for (i = 0; i < num_screens; i++)
1899                         TAILQ_FOREACH(tmpr, &screens[i].rl, entry)
1900                                 if (tmpr->bar)
1901                                         xcb_unmap_window(conn, tmpr->bar->id);
1902         } else {
1903                 for (i = 0; i < num_screens; i++)
1904                         TAILQ_FOREACH(tmpr, &screens[i].rl, entry)
1905                                 if (tmpr->bar)
1906                                         map_window_raised(tmpr->bar->id);
1907         }
1908
1909         bar_enabled = !bar_enabled;
1910
1911         stack();
1912         /* must be after stack */
1913         bar_update();
1914 }
1915
1916 void
1917 bar_refresh(void)
1918 {
1919         struct swm_region       *r;
1920         uint32_t                wa[2];
1921         int                     i, num_screens;
1922
1923         /* do this here because the conf file is in memory */
1924         if (bar_extra && bar_extra_running == 0 && bar_argv[0]) {
1925                 /* launch external status app */
1926                 bar_extra_running = 1;
1927                 if (pipe(bar_pipe) == -1)
1928                         err(1, "pipe error");
1929                 socket_setnonblock(bar_pipe[0]);
1930                 socket_setnonblock(bar_pipe[1]); /* XXX hmmm, really? */
1931                 if (dup2(bar_pipe[0], 0) == -1)
1932                         err(1, "dup2");
1933                 if (dup2(bar_pipe[1], 1) == -1)
1934                         err(1, "dup2");
1935                 if (signal(SIGPIPE, SIG_IGN) == SIG_ERR)
1936                         err(1, "could not disable SIGPIPE");
1937                 switch (bar_pid = fork()) {
1938                 case -1:
1939                         err(1, "cannot fork");
1940                         break;
1941                 case 0: /* child */
1942                         close(bar_pipe[0]);
1943                         execvp(bar_argv[0], bar_argv);
1944                         err(1, "%s external app failed", bar_argv[0]);
1945                         break;
1946                 default: /* parent */
1947                         close(bar_pipe[1]);
1948                         break;
1949                 }
1950         }
1951
1952         num_screens = xcb_setup_roots_length(xcb_get_setup(conn));
1953         for (i = 0; i < num_screens; i++)
1954                 TAILQ_FOREACH(r, &screens[i].rl, entry) {
1955                         if (r->bar == NULL)
1956                                 continue;
1957                         wa[0] = screens[i].c[SWM_S_COLOR_BAR].color;
1958                         wa[1] = screens[i].c[SWM_S_COLOR_BAR_BORDER].color;
1959                         xcb_change_window_attributes(conn, r->bar->id,
1960                                 XCB_CW_BACK_PIXEL | XCB_CW_BORDER_PIXEL, wa);
1961                 }
1962         bar_update();
1963 }
1964
1965 void
1966 bar_setup(struct swm_region *r)
1967 {
1968         char                    *default_string;
1969         char                    **missing_charsets;
1970         int                     num_missing_charsets = 0;
1971         int                     i;
1972         xcb_screen_t            *screen = get_screen(r->s->idx);
1973         uint32_t                wa[2];
1974         
1975         if (bar_fs) {
1976                 XFreeFontSet(display, bar_fs);
1977                 bar_fs = NULL;
1978         }
1979
1980         if ((r->bar = calloc(1, sizeof(struct swm_bar))) == NULL)
1981                 err(1, "bar_setup: calloc: failed to allocate memory.");
1982
1983         DNPRINTF(SWM_D_BAR, "bar_setup: loading bar_fonts: %s\n", bar_fonts);
1984
1985         bar_fs = XCreateFontSet(display, bar_fonts, &missing_charsets,
1986             &num_missing_charsets, &default_string);
1987
1988         if (num_missing_charsets > 0) {
1989                 warnx("Unable to load charset(s):");
1990
1991                 for (i = 0; i < num_missing_charsets; ++i)
1992                         warnx("%s", missing_charsets[i]);
1993
1994                 XFreeStringList(missing_charsets);
1995
1996                 if (strcmp(default_string, ""))
1997                         warnx("Glyphs from those sets will be replaced "
1998                             "by '%s'.", default_string);
1999                 else
2000                         warnx("Glyphs from those sets won't be drawn.");
2001         }
2002
2003         if (bar_fs == NULL)
2004                 errx(1, "Error creating font set structure.");
2005
2006         bar_fs_extents = XExtentsOfFontSet(bar_fs);
2007
2008         bar_height = bar_fs_extents->max_logical_extent.height +
2009             2 * bar_border_width;
2010
2011         if (bar_height < 1)
2012                 bar_height = 1;
2013
2014         X(r->bar) = X(r);
2015         Y(r->bar) = bar_at_bottom ? (Y(r) + HEIGHT(r) - bar_height) : Y(r);
2016         WIDTH(r->bar) = WIDTH(r) - 2 * bar_border_width;
2017         HEIGHT(r->bar) = bar_height - 2 * bar_border_width;
2018
2019         r->bar->id = xcb_generate_id(conn);
2020         wa[0] = r->s->c[SWM_S_COLOR_BAR].color;
2021         wa[1] = r->s->c[SWM_S_COLOR_BAR_BORDER].color;
2022         xcb_create_window(conn, screen->root_depth, r->bar->id, r->s->root,
2023                 X(r->bar), Y(r->bar), WIDTH(r->bar), HEIGHT(r->bar),
2024                 bar_border_width, XCB_WINDOW_CLASS_INPUT_OUTPUT,
2025                 screen->root_visual, XCB_CW_BACK_PIXEL | XCB_CW_BORDER_PIXEL,
2026                 wa);
2027
2028         r->bar->buffer = xcb_generate_id(conn);
2029         xcb_create_pixmap(conn, screen->root_depth, r->bar->buffer, r->bar->id,
2030                 WIDTH(r->bar), HEIGHT(r->bar)); 
2031
2032         xcb_randr_select_input(conn, r->bar->id,
2033                 XCB_RANDR_NOTIFY_MASK_OUTPUT_CHANGE);
2034
2035         if (bar_enabled)
2036                 map_window_raised(r->bar->id);
2037
2038         DNPRINTF(SWM_D_BAR, "bar_setup: window: 0x%x, (x,y) w x h: (%d,%d) "
2039             "%d x %d\n", WINID(r->bar), X(r->bar), Y(r->bar), WIDTH(r->bar),
2040             HEIGHT(r->bar));
2041
2042         if (signal(SIGALRM, bar_signal) == SIG_ERR)
2043                 err(1, "could not install bar_signal");
2044         bar_refresh();
2045 }
2046
2047 void
2048 bar_cleanup(struct swm_region *r)
2049 {
2050         if (r->bar == NULL)
2051                 return;
2052         xcb_destroy_window(conn, r->bar->id);
2053         xcb_free_pixmap(conn, r->bar->buffer);
2054         free(r->bar);
2055         r->bar = NULL;
2056 }
2057
2058 void
2059 drain_enter_notify(void)
2060 {
2061         int                     i = 0;
2062         XEvent                  cne;
2063
2064         while (XCheckMaskEvent(display, EnterWindowMask, &cne))
2065                 i++;
2066
2067         DNPRINTF(SWM_D_EVENT, "drain_enter_notify: drained: %d\n", i);
2068 }
2069
2070 void
2071 set_win_state(struct ws_win *win, uint16_t state)
2072 {
2073         uint16_t                data[2] = { state, XCB_ATOM_NONE };
2074
2075         DNPRINTF(SWM_D_EVENT, "set_win_state: window: 0x%x\n", win->id);
2076
2077         if (win == NULL)
2078                 return;
2079
2080         xcb_change_property(conn, XCB_PROP_MODE_REPLACE, win->id, astate,
2081                 astate, 32, 2, data);
2082 }
2083
2084 uint16_t
2085 getstate(xcb_window_t w)
2086 {
2087         uint16_t                        result = 0;
2088         xcb_get_property_cookie_t       c;
2089         xcb_get_property_reply_t        *r;
2090
2091         c = xcb_get_property(conn, False, w, astate, astate, 0L, 2L);
2092         r = xcb_get_property_reply(conn, c, NULL);
2093
2094         if (r) {
2095                 result = *((uint16_t *)xcb_get_property_value(r));
2096                 free(r);
2097         }
2098
2099         return (result);
2100 }
2101
2102 void
2103 version(struct swm_region *r, union arg *args)
2104 {
2105         bar_version = !bar_version;
2106         if (bar_version)
2107                 snprintf(bar_vertext, sizeof bar_vertext,
2108                     "Version: %s Build: %s", SPECTRWM_VERSION, buildstr);
2109         else
2110                 strlcpy(bar_vertext, "", sizeof bar_vertext);
2111         bar_update();
2112 }
2113
2114 void
2115 client_msg(struct ws_win *win, xcb_atom_t a)
2116 {
2117         xcb_client_message_event_t      ev;
2118
2119         if (win == NULL)
2120                 return;
2121
2122         bzero(&ev, sizeof ev);
2123         ev.response_type = XCB_CLIENT_MESSAGE;
2124         ev.window = win->id;
2125         ev.type = aprot;
2126         ev.format = 32;
2127         ev.data.data32[0] = a;
2128         ev.data.data32[1] = XCB_CURRENT_TIME;
2129
2130         xcb_send_event(conn, False, win->id,
2131                 XCB_EVENT_MASK_NO_EVENT, (const char *)&ev);
2132         xcb_flush(conn);
2133 }
2134
2135 /* synthetic response to a ConfigureRequest when not making a change */
2136 void
2137 config_win(struct ws_win *win, XConfigureRequestEvent  *ev)
2138 {
2139         xcb_configure_notify_event_t ce;
2140
2141         if (win == NULL)
2142                 return;
2143
2144         /* send notification of unchanged state. */
2145         bzero(&ce, sizeof(ce));
2146         ce.response_type = XCB_CONFIGURE_NOTIFY;
2147         ce.x = X(win);
2148         ce.y = Y(win);
2149         ce.width = WIDTH(win);
2150         ce.height = HEIGHT(win);
2151         ce.override_redirect = False;
2152
2153         if (ev == NULL) {
2154                 /* EWMH */
2155                 ce.event = win->id;
2156                 ce.window = win->id;
2157                 ce.border_width = BORDER(win);
2158                 ce.above_sibling = XCB_WINDOW_NONE;
2159         } else {
2160                 /* normal */
2161                 ce.event = ev->window;
2162                 ce.window = ev->window;
2163
2164                 /* make response appear more WM_SIZE_HINTS-compliant */
2165                 if (win->sh.flags)
2166                         DNPRINTF(SWM_D_MISC, "config_win: hints: window: 0x%x,"
2167                             " sh.flags: %u, min: %d x %d, max: %d x %d, inc: "
2168                             "%d x %d\n", win->id, win->sh.flags, SH_MIN_W(win),
2169                             SH_MIN_H(win), SH_MAX_W(win), SH_MAX_H(win),
2170                             SH_INC_W(win), SH_INC_H(win));
2171
2172                 /* min size */
2173                 if (SH_MIN(win)) {
2174                         /* the hint may be set... to 0! */
2175                         if (SH_MIN_W(win) > 0 && ce.width < SH_MIN_W(win))
2176                                 ce.width = SH_MIN_W(win);
2177                         if (SH_MIN_H(win) > 0 && ce.height < SH_MIN_H(win))
2178                                 ce.height = SH_MIN_H(win);
2179                 }
2180
2181                 /* max size */
2182                 if (SH_MAX(win)) {
2183                         /* may also be advertized as 0 */
2184                         if (SH_MAX_W(win) > 0 && ce.width > SH_MAX_W(win))
2185                                 ce.width = SH_MAX_W(win);
2186                         if (SH_MAX_H(win) > 0 && ce.height > SH_MAX_H(win))
2187                                 ce.height = SH_MAX_H(win);
2188                 }
2189
2190                 /* resize increment. */
2191                 if (SH_INC(win)) {
2192                         if (SH_INC_W(win) > 1 && ce.width > SH_INC_W(win))
2193                                 ce.width -= (ce.width - SH_MIN_W(win)) %
2194                                     SH_INC_W(win);
2195                         if (SH_INC_H(win) > 1 && ce.height > SH_INC_H(win))
2196                                 ce.height -= (ce.height - SH_MIN_H(win)) %
2197                                     SH_INC_H(win);
2198                 }
2199
2200                 /* adjust x and y for requested border_width. */
2201                 ce.x += BORDER(win) - ev->border_width;
2202                 ce.y += BORDER(win) - ev->border_width;
2203                 ce.border_width = ev->border_width;
2204                 ce.above_sibling = ev->above;
2205         }
2206
2207         DNPRINTF(SWM_D_MISC, "config_win: ewmh: %s, window: 0x%x, (x,y) w x h: "
2208             "(%d,%d) %d x %d, border: %d\n", YESNO(ev == NULL), win->id, ce.x,
2209             ce.y, ce.width, ce.height, ce.border_width);
2210
2211         xcb_send_event(conn, False, win->id, XCB_EVENT_MASK_STRUCTURE_NOTIFY,
2212                 (char *)&ce);
2213         xcb_flush(conn);
2214 }
2215
2216 int
2217 count_win(struct workspace *ws, int count_transient)
2218 {
2219         struct ws_win           *win;
2220         int                     count = 0;
2221
2222         TAILQ_FOREACH(win, &ws->winlist, entry) {
2223                 if (count_transient == 0 && win->floating)
2224                         continue;
2225                 if (count_transient == 0 && win->transient)
2226                         continue;
2227                 if (win->iconic)
2228                         continue;
2229                 count++;
2230         }
2231         DNPRINTF(SWM_D_MISC, "count_win: %d\n", count);
2232
2233         return (count);
2234 }
2235
2236 void
2237 quit(struct swm_region *r, union arg *args)
2238 {
2239         DNPRINTF(SWM_D_MISC, "quit\n");
2240         running = 0;
2241 }
2242
2243 void
2244 unmap_window(struct ws_win *win)
2245 {
2246         if (win == NULL)
2247                 return;
2248
2249         /* don't unmap again */
2250         if (getstate(win->id) == XCB_ICCCM_WM_STATE_ICONIC)
2251                 return;
2252
2253         set_win_state(win, XCB_ICCCM_WM_STATE_ICONIC);
2254
2255         xcb_unmap_window(conn, win->id);
2256         xcb_change_window_attributes(conn, win->id,
2257                 XCB_CW_BORDER_PIXEL, &win->s->c[SWM_S_COLOR_UNFOCUS].color);
2258 }
2259
2260 void
2261 unmap_all(void)
2262 {
2263         struct ws_win           *win;
2264         int                     i, j, num_screens;
2265
2266         num_screens = xcb_setup_roots_length(xcb_get_setup(conn));
2267         for (i = 0; i < num_screens; i++)
2268                 for (j = 0; j < workspace_limit; j++)
2269                         TAILQ_FOREACH(win, &screens[i].ws[j].winlist, entry)
2270                                 unmap_window(win);
2271 }
2272
2273 void
2274 fake_keypress(struct ws_win *win, xcb_keysym_t keysym, uint16_t modifiers)
2275 {
2276         xcb_key_press_event_t   event;
2277         xcb_keycode_t           *keycode;
2278
2279         if (win == NULL)
2280                 return;
2281
2282         keycode = xcb_key_symbols_get_keycode(syms, keysym);
2283
2284         event.event = win->id;
2285         event.root = win->s->root;
2286         event.child = XCB_WINDOW_NONE;
2287         event.time = XCB_CURRENT_TIME;
2288         event.event_x = X(win);
2289         event.event_y = Y(win);
2290         event.root_x = 1;
2291         event.root_y = 1;
2292         event.same_screen = True;
2293         event.detail = *keycode;
2294         event.state = modifiers;
2295
2296         event.response_type = XCB_KEY_PRESS;
2297         xcb_send_event(conn, True, win->id,
2298                  XCB_EVENT_MASK_KEY_PRESS, (char *)&event);
2299
2300         event.response_type = XCB_KEY_RELEASE;
2301         xcb_send_event(conn, True, win->id,
2302                 XCB_EVENT_MASK_KEY_RELEASE, (char *)&event);
2303         xcb_flush(conn);
2304 }
2305
2306 void
2307 restart(struct swm_region *r, union arg *args)
2308 {
2309         DNPRINTF(SWM_D_MISC, "restart: %s\n", start_argv[0]);
2310
2311         /* disable alarm because the following code may not be interrupted */
2312         alarm(0);
2313         if (signal(SIGALRM, SIG_IGN) == SIG_ERR)
2314                 err(1, "can't disable alarm");
2315
2316         bar_extra_stop();
2317         bar_extra = 1;
2318         unmap_all();
2319
2320         xcb_key_symbols_free(syms);
2321         xcb_flush(conn);
2322         xcb_disconnect(conn);
2323
2324         execvp(start_argv[0], start_argv);
2325         warn("execvp failed");
2326         quit(NULL, NULL);
2327 }
2328
2329 struct swm_region *
2330 root_to_region(xcb_window_t root)
2331 {
2332         struct swm_region       *r = NULL;
2333         int                     i, num_screens;
2334         xcb_query_pointer_reply_t       *qpr;
2335
2336         DNPRINTF(SWM_D_MISC, "root_to_region: window: 0x%x\n", root);
2337
2338         num_screens = xcb_setup_roots_length(xcb_get_setup(conn));
2339         for (i = 0; i < num_screens; i++)
2340                 if (screens[i].root == root)
2341                         break;
2342
2343         qpr = xcb_query_pointer_reply(conn, xcb_query_pointer(conn,
2344                 screens[i].root), NULL);
2345
2346         if (qpr) {
2347                 DNPRINTF(SWM_D_MISC, "root_to_region: pointer: (%d,%d)\n",
2348                     qpr->root_x, qpr->root_y);
2349                 /* choose a region based on pointer location */
2350                 TAILQ_FOREACH(r, &screens[i].rl, entry)
2351                         if (X(r) <= qpr->root_x && qpr->root_x < MAX_X(r) &&
2352                             Y(r) <= qpr->root_y && qpr->root_y < MAX_Y(r))
2353                                 break;
2354                 free(qpr);
2355         }
2356
2357         if (r == NULL)
2358                 r = TAILQ_FIRST(&screens[i].rl);
2359
2360         return (r);
2361 }
2362
2363 struct ws_win *
2364 find_unmanaged_window(xcb_window_t id)
2365 {
2366         struct ws_win           *win;
2367         int                     i, j, num_screens;
2368
2369         num_screens = xcb_setup_roots_length(xcb_get_setup(conn));
2370         for (i = 0; i < num_screens; i++)
2371                 for (j = 0; j < workspace_limit; j++)
2372                         TAILQ_FOREACH(win, &screens[i].ws[j].unmanagedlist,
2373                             entry)
2374                                 if (id == win->id)
2375                                         return (win);
2376         return (NULL);
2377 }
2378
2379 struct ws_win *
2380 find_window(xcb_window_t id)
2381 {
2382         struct ws_win           *win;
2383         int                     i, j, num_screens;
2384         xcb_query_tree_cookie_t c;
2385         xcb_query_tree_reply_t  *r;
2386
2387         num_screens = xcb_setup_roots_length(xcb_get_setup(conn));
2388         for (i = 0; i < num_screens; i++)
2389                 for (j = 0; j < workspace_limit; j++)
2390                         TAILQ_FOREACH(win, &screens[i].ws[j].winlist, entry)
2391                                 if (id == win->id)
2392                                         return (win);
2393
2394         c = xcb_query_tree(conn, id);
2395         r = xcb_query_tree_reply(conn, c, NULL);
2396         if (!r)
2397                 return (NULL);
2398
2399         /* if we were looking for the parent return that window instead */
2400         if (r->parent == 0 || r->root == r->parent)
2401                 return (NULL);
2402
2403         /* look for parent */
2404         for (i = 0; i < num_screens; i++)
2405                 for (j = 0; j < workspace_limit; j++)
2406                         TAILQ_FOREACH(win, &screens[i].ws[j].winlist, entry)
2407                                 if (r->parent == win->id) {
2408                                         free(r);
2409                                         return (win);
2410                                 }
2411
2412         free(r);
2413         return (NULL);
2414 }
2415
2416 void
2417 spawn(int ws_idx, union arg *args, int close_fd)
2418 {
2419         int                     fd;
2420         char                    *ret = NULL;
2421
2422         DNPRINTF(SWM_D_MISC, "spawn: %s\n", args->argv[0]);
2423
2424         if (display)
2425                 close(xcb_get_file_descriptor(conn));
2426
2427         setenv("LD_PRELOAD", SWM_LIB, 1);
2428
2429         if (asprintf(&ret, "%d", ws_idx) == -1) {
2430                 warn("spawn: asprintf SWM_WS");
2431                 _exit(1);
2432         }
2433         setenv("_SWM_WS", ret, 1);
2434         free(ret);
2435         ret = NULL;
2436
2437         if (asprintf(&ret, "%d", getpid()) == -1) {
2438                 warn("spawn: asprintf _SWM_PID");
2439                 _exit(1);
2440         }
2441         setenv("_SWM_PID", ret, 1);
2442         free(ret);
2443         ret = NULL;
2444
2445         if (setsid() == -1) {
2446                 warn("spawn: setsid");
2447                 _exit(1);
2448         }
2449
2450         if (close_fd) {
2451                 /*
2452                  * close stdin and stdout to prevent interaction between apps
2453                  * and the baraction script
2454                  * leave stderr open to record errors
2455                 */
2456                 if ((fd = open(_PATH_DEVNULL, O_RDWR, 0)) == -1) {
2457                         warn("spawn: open");
2458                         _exit(1);
2459                 }
2460                 dup2(fd, STDIN_FILENO);
2461                 dup2(fd, STDOUT_FILENO);
2462                 if (fd > 2)
2463                         close(fd);
2464         }
2465
2466         execvp(args->argv[0], args->argv);
2467
2468         warn("spawn: execvp");
2469         _exit(1);
2470 }
2471
2472 void
2473 kill_refs(struct ws_win *win)
2474 {
2475         int                     i, x, num_screens;
2476         struct swm_region       *r;
2477         struct workspace        *ws;
2478
2479         if (win == NULL)
2480                 return;
2481
2482         num_screens = xcb_setup_roots_length(xcb_get_setup(conn));
2483         for (i = 0; i < num_screens; i++)
2484                 TAILQ_FOREACH(r, &screens[i].rl, entry)
2485                         for (x = 0; x < workspace_limit; x++) {
2486                                 ws = &r->s->ws[x];
2487                                 if (win == ws->focus)
2488                                         ws->focus = NULL;
2489                                 if (win == ws->focus_prev)
2490                                         ws->focus_prev = NULL;
2491                         }
2492 }
2493
2494 int
2495 validate_win(struct ws_win *testwin)
2496 {
2497         struct ws_win           *win;
2498         struct workspace        *ws;
2499         struct swm_region       *r;
2500         int                     i, x, num_screens;
2501
2502         if (testwin == NULL)
2503                 return (0);
2504
2505         num_screens = xcb_setup_roots_length(xcb_get_setup(conn));
2506         for (i = 0; i < num_screens; i++)
2507                 TAILQ_FOREACH(r, &screens[i].rl, entry)
2508                         for (x = 0; x < workspace_limit; x++) {
2509                                 ws = &r->s->ws[x];
2510                                 TAILQ_FOREACH(win, &ws->winlist, entry)
2511                                         if (win == testwin)
2512                                                 return (0);
2513                         }
2514         return (1);
2515 }
2516
2517 int
2518 validate_ws(struct workspace *testws)
2519 {
2520         struct swm_region       *r;
2521         struct workspace        *ws;
2522         int                     i, x, num_screens;
2523
2524         /* validate all ws */
2525         num_screens = xcb_setup_roots_length(xcb_get_setup(conn));
2526         for (i = 0; i < num_screens; i++)
2527                 TAILQ_FOREACH(r, &screens[i].rl, entry)
2528                         for (x = 0; x < workspace_limit; x++) {
2529                                 ws = &r->s->ws[x];
2530                                 if (ws == testws)
2531                                         return (0);
2532                         }
2533         return (1);
2534 }
2535
2536 void
2537 unfocus_win(struct ws_win *win)
2538 {
2539         XEvent                  cne;
2540         xcb_window_t            none = XCB_WINDOW_NONE;
2541
2542         DNPRINTF(SWM_D_FOCUS, "unfocus_win: window: 0x%x\n", WINID(win));
2543
2544         if (win == NULL)
2545                 return;
2546         if (win->ws == NULL)
2547                 return;
2548
2549         if (validate_ws(win->ws))
2550                 return; /* XXX this gets hit with thunderbird, needs fixing */
2551
2552         if (win->ws->r == NULL)
2553                 return;
2554
2555         if (validate_win(win)) {
2556                 kill_refs(win);
2557                 return;
2558         }
2559
2560         if (win->ws->focus == win) {
2561                 win->ws->focus = NULL;
2562                 win->ws->focus_prev = win;
2563         }
2564
2565         if (validate_win(win->ws->focus)) {
2566                 kill_refs(win->ws->focus);
2567                 win->ws->focus = NULL;
2568         }
2569         if (validate_win(win->ws->focus_prev)) {
2570                 kill_refs(win->ws->focus_prev);
2571                 win->ws->focus_prev = NULL;
2572         }
2573
2574         /* drain all previous unfocus events */
2575         while (XCheckTypedEvent(display, FocusOut, &cne) == True)
2576                 ;
2577
2578         grabbuttons(win, 0);
2579         xcb_change_window_attributes(conn, win->id, XCB_CW_BORDER_PIXEL,
2580                 &win->ws->r->s->c[SWM_S_COLOR_UNFOCUS].color);
2581         xcb_change_property(conn, XCB_PROP_MODE_REPLACE, win->s->root,
2582                 ewmh[_NET_ACTIVE_WINDOW].atom, XCB_ATOM_WINDOW, 32, 1,
2583                 &none);
2584 }
2585
2586 void
2587 unfocus_all(void)
2588 {
2589         struct ws_win           *win;
2590         int                     i, j, num_screens;
2591
2592         DNPRINTF(SWM_D_FOCUS, "unfocus_all\n");
2593
2594         num_screens = xcb_setup_roots_length(xcb_get_setup(conn));
2595         for (i = 0; i < num_screens; i++)
2596                 for (j = 0; j < workspace_limit; j++)
2597                         TAILQ_FOREACH(win, &screens[i].ws[j].winlist, entry)
2598                                 unfocus_win(win);
2599 }
2600
2601 void
2602 focus_win(struct ws_win *win)
2603 {
2604         XEvent                  cne;
2605         struct ws_win           *cfw = NULL;
2606         xcb_get_input_focus_cookie_t    c;
2607         xcb_get_input_focus_reply_t     *r;
2608         xcb_window_t                    cur_focus = XCB_WINDOW_NONE;
2609
2610         DNPRINTF(SWM_D_FOCUS, "focus_win: window: 0x%x\n", WINID(win));
2611
2612         if (win == NULL)
2613                 return;
2614         if (win->ws == NULL)
2615                 return;
2616
2617         if (validate_ws(win->ws))
2618                 return; /* XXX this gets hit with thunderbird, needs fixing */
2619
2620         if (validate_win(win)) {
2621                 kill_refs(win);
2622                 return;
2623         }
2624
2625         if (validate_win(win)) {
2626                 kill_refs(win);
2627                 return;
2628         }
2629
2630         c = xcb_get_input_focus(conn);
2631         r = xcb_get_input_focus_reply(conn, c, NULL);
2632         if (r) {
2633                 cur_focus = r->focus;
2634                 free(r);
2635         }
2636         if ((cfw = find_window(cur_focus)) != NULL)
2637                 unfocus_win(cfw);
2638         else {
2639                 /* use larger hammer since the window was killed somehow */
2640                 TAILQ_FOREACH(cfw, &win->ws->winlist, entry)
2641                         if (cfw->ws && cfw->ws->r && cfw->ws->r->s)
2642                                 xcb_change_window_attributes(conn, cfw->id,
2643                                         XCB_CW_BORDER_PIXEL,
2644                                         &cfw->ws->r->s->c[SWM_S_COLOR_UNFOCUS].color);
2645         }
2646
2647         win->ws->focus = win;
2648
2649         if (win->ws->r != NULL) {
2650                 /* drain all previous focus events */
2651                 while (XCheckTypedEvent(display, FocusIn, &cne) == True)
2652                         ;
2653
2654                 if (win->java == 0)
2655                         xcb_set_input_focus(conn, XCB_INPUT_FOCUS_PARENT,
2656                                 win->id, XCB_CURRENT_TIME);
2657                 grabbuttons(win, 1);
2658                 xcb_change_window_attributes(conn, win->id,
2659                         XCB_CW_BORDER_PIXEL,
2660                         &win->ws->r->s->c[SWM_S_COLOR_FOCUS].color);
2661                 if (win->ws->cur_layout->flags & SWM_L_MAPONFOCUS ||
2662                     win->ws->always_raise)
2663                         map_window_raised(win->id);
2664
2665                 xcb_change_property(conn, XCB_PROP_MODE_REPLACE, win->s->root,
2666                         ewmh[_NET_ACTIVE_WINDOW].atom, XCB_ATOM_WINDOW, 32, 1,
2667                         &win->id);
2668         }
2669
2670         bar_update();
2671 }
2672
2673 void
2674 switchws(struct swm_region *r, union arg *args)
2675 {
2676         int                     wsid = args->id, unmap_old = 0;
2677         struct swm_region       *this_r, *other_r;
2678         struct ws_win           *win;
2679         struct workspace        *new_ws, *old_ws;
2680         union arg               a;
2681
2682         if (!(r && r->s))
2683                 return;
2684
2685         if (wsid >= workspace_limit)
2686                 return;
2687
2688         this_r = r;
2689         old_ws = this_r->ws;
2690         new_ws = &this_r->s->ws[wsid];
2691
2692         DNPRINTF(SWM_D_WS, "switchws: screen[%d]:%dx%d+%d+%d: %d -> %d\n",
2693             r->s->idx, WIDTH(r), HEIGHT(r), X(r), Y(r), old_ws->idx, wsid);
2694
2695         if (new_ws == NULL || old_ws == NULL)
2696                 return;
2697         if (new_ws == old_ws)
2698                 return;
2699
2700         other_r = new_ws->r;
2701         if (other_r == NULL) {
2702                 /* the other workspace is hidden, hide this one */
2703                 old_ws->r = NULL;
2704                 unmap_old = 1;
2705         } else {
2706                 /* the other ws is visible in another region, exchange them */
2707                 other_r->ws_prior = new_ws;
2708                 other_r->ws = old_ws;
2709                 old_ws->r = other_r;
2710         }
2711         this_r->ws_prior = old_ws;
2712         this_r->ws = new_ws;
2713         new_ws->r = this_r;
2714
2715         /* this is needed so that we can click on a window after a restart */
2716         unfocus_all();
2717
2718         stack();
2719         a.id = SWM_ARG_ID_FOCUSCUR;
2720         focus(new_ws->r, &a);
2721
2722         /* unmap old windows */
2723         if (unmap_old)
2724                 TAILQ_FOREACH(win, &old_ws->winlist, entry)
2725                         unmap_window(win);
2726
2727         if (focus_mode == SWM_FOCUS_DEFAULT)
2728                 drain_enter_notify();
2729 }
2730
2731 void
2732 cyclews(struct swm_region *r, union arg *args)
2733 {
2734         union                   arg a;
2735         struct swm_screen       *s = r->s;
2736         int                     cycle_all = 0;
2737
2738         DNPRINTF(SWM_D_WS, "cyclews: id: %d, screen[%d]:%dx%d+%d+%d, ws: %d\n",
2739             args->id, r->s->idx, WIDTH(r), HEIGHT(r), X(r), Y(r), r->ws->idx);
2740
2741         a.id = r->ws->idx;
2742         do {
2743                 switch (args->id) {
2744                 case SWM_ARG_ID_CYCLEWS_UP_ALL:
2745                         cycle_all = 1;
2746                         /* FALLTHROUGH */
2747                 case SWM_ARG_ID_CYCLEWS_UP:
2748                         if (a.id < workspace_limit - 1)
2749                                 a.id++;
2750                         else
2751                                 a.id = 0;
2752                         break;
2753                 case SWM_ARG_ID_CYCLEWS_DOWN_ALL:
2754                         cycle_all = 1;
2755                         /* FALLTHROUGH */
2756                 case SWM_ARG_ID_CYCLEWS_DOWN:
2757                         if (a.id > 0)
2758                                 a.id--;
2759                         else
2760                                 a.id = workspace_limit - 1;
2761                         break;
2762                 default:
2763                         return;
2764                 };
2765
2766                 if (!cycle_all &&
2767                     (cycle_empty == 0 && TAILQ_EMPTY(&s->ws[a.id].winlist)))
2768                         continue;
2769                 if (cycle_visible == 0 && s->ws[a.id].r != NULL)
2770                         continue;
2771
2772                 switchws(r, &a);
2773         } while (a.id != r->ws->idx);
2774 }
2775
2776 void
2777 priorws(struct swm_region *r, union arg *args)
2778 {
2779         union arg               a;
2780
2781         DNPRINTF(SWM_D_WS, "priorws: id: %d, screen[%d]:%dx%d+%d+%d, ws: %d\n",
2782             args->id, r->s->idx, WIDTH(r), HEIGHT(r), X(r), Y(r), r->ws->idx);
2783
2784         if (r->ws_prior == NULL)
2785                 return;
2786
2787         a.id = r->ws_prior->idx;
2788         switchws(r, &a);
2789 }
2790
2791 void
2792 cyclescr(struct swm_region *r, union arg *args)
2793 {
2794         struct swm_region       *rr = NULL;
2795         union arg               a;
2796         int                     i, x, y, num_screens;
2797
2798         num_screens = xcb_setup_roots_length(xcb_get_setup(conn));
2799         /* do nothing if we don't have more than one screen */
2800         if (!(num_screens > 1 || outputs > 1))
2801                 return;
2802
2803         i = r->s->idx;
2804         switch (args->id) {
2805         case SWM_ARG_ID_CYCLESC_UP:
2806                 rr = TAILQ_NEXT(r, entry);
2807                 if (rr == NULL)
2808                         rr = TAILQ_FIRST(&screens[i].rl);
2809                 break;
2810         case SWM_ARG_ID_CYCLESC_DOWN:
2811                 rr = TAILQ_PREV(r, swm_region_list, entry);
2812                 if (rr == NULL)
2813                         rr = TAILQ_LAST(&screens[i].rl, swm_region_list);
2814                 break;
2815         default:
2816                 return;
2817         };
2818         if (rr == NULL)
2819                 return;
2820
2821         /* move mouse to region */
2822         x = X(rr) + 1;
2823         y = Y(rr) + 1 + (bar_enabled ? bar_height : 0);
2824         xcb_warp_pointer(conn, XCB_WINDOW_NONE, rr->s[i].root, 0, 0, 0, 0,
2825                 x, y);
2826
2827         a.id = SWM_ARG_ID_FOCUSCUR;
2828         focus(rr, &a);
2829
2830         if (rr->ws->focus) {
2831                 /* move to focus window */
2832                 x = X(rr->ws->focus) + 1;
2833                 y = Y(rr->ws->focus) + 1;
2834                 xcb_warp_pointer(conn, XCB_WINDOW_NONE, rr->s[i].root, 0, 0, 0,
2835                         0, x, y);
2836         }
2837 }
2838
2839 void
2840 sort_windows(struct ws_win_list *wl)
2841 {
2842         struct ws_win           *win, *parent, *nxt;
2843
2844         if (wl == NULL)
2845                 return;
2846
2847         for (win = TAILQ_FIRST(wl); win != TAILQ_END(wl); win = nxt) {
2848                 nxt = TAILQ_NEXT(win, entry);
2849                 if (win->transient) {
2850                         parent = find_window(win->transient);
2851                         if (parent == NULL) {
2852                                 warnx("not possible bug");
2853                                 continue;
2854                         }
2855                         TAILQ_REMOVE(wl, win, entry);
2856                         TAILQ_INSERT_AFTER(wl, parent, win, entry);
2857                 }
2858         }
2859
2860 }
2861
2862 void
2863 swapwin(struct swm_region *r, union arg *args)
2864 {
2865         struct ws_win           *target, *source;
2866         struct ws_win           *cur_focus;
2867         struct ws_win_list      *wl;
2868
2869
2870         DNPRINTF(SWM_D_WS, "swapwin: id: %d, screen[%d]:%dx%d+%d+%d, ws: %d\n",
2871             args->id, r->s->idx, WIDTH(r), HEIGHT(r), X(r), Y(r), r->ws->idx);
2872
2873         cur_focus = r->ws->focus;
2874         if (cur_focus == NULL)
2875                 return;
2876
2877         source = cur_focus;
2878         wl = &source->ws->winlist;
2879
2880         switch (args->id) {
2881         case SWM_ARG_ID_SWAPPREV:
2882                 if (source->transient)
2883                         source = find_window(source->transient);
2884                 target = TAILQ_PREV(source, ws_win_list, entry);
2885                 if (target && target->transient)
2886                         target = find_window(target->transient);
2887                 TAILQ_REMOVE(wl, source, entry);
2888                 if (target == NULL)
2889                         TAILQ_INSERT_TAIL(wl, source, entry);
2890                 else
2891                         TAILQ_INSERT_BEFORE(target, source, entry);
2892                 break;
2893         case SWM_ARG_ID_SWAPNEXT:
2894                 target = TAILQ_NEXT(source, entry);
2895                 /* move the parent and let the sort handle the move */
2896                 if (source->transient)
2897                         source = find_window(source->transient);
2898                 TAILQ_REMOVE(wl, source, entry);
2899                 if (target == NULL)
2900                         TAILQ_INSERT_HEAD(wl, source, entry);
2901                 else
2902                         TAILQ_INSERT_AFTER(wl, target, source, entry);
2903                 break;
2904         case SWM_ARG_ID_SWAPMAIN:
2905                 target = TAILQ_FIRST(wl);
2906                 if (target == source) {
2907                         if (source->ws->focus_prev != NULL &&
2908                             source->ws->focus_prev != target)
2909                                 source = source->ws->focus_prev;
2910                         else
2911                                 return;
2912                 }
2913                 if (target == NULL || source == NULL)
2914                         return;
2915                 source->ws->focus_prev = target;
2916                 TAILQ_REMOVE(wl, target, entry);
2917                 TAILQ_INSERT_BEFORE(source, target, entry);
2918                 TAILQ_REMOVE(wl, source, entry);
2919                 TAILQ_INSERT_HEAD(wl, source, entry);
2920                 break;
2921         case SWM_ARG_ID_MOVELAST:
2922                 TAILQ_REMOVE(wl, source, entry);
2923                 TAILQ_INSERT_TAIL(wl, source, entry);
2924                 break;
2925         default:
2926                 DNPRINTF(SWM_D_MOVE, "swapwin: invalid id: %d\n", args->id);
2927                 return;
2928         }
2929
2930         sort_windows(wl);
2931
2932         stack();
2933 }
2934
2935 void
2936 focus_prev(struct ws_win *win)
2937 {
2938         struct ws_win           *winfocus = NULL;
2939         struct ws_win           *cur_focus = NULL;
2940         struct ws_win_list      *wl = NULL;
2941         struct workspace        *ws = NULL;
2942
2943         if (!(win && win->ws))
2944                 return;
2945
2946         ws = win->ws;
2947         wl = &ws->winlist;
2948         cur_focus = ws->focus;
2949
2950         DNPRINTF(SWM_D_FOCUS, "focus_prev: window: 0x%x, cur_focus: 0x%x\n",
2951             WINID(win), WINID(cur_focus));
2952
2953         /* pickle, just focus on whatever */
2954         if (cur_focus == NULL) {
2955                 /* use prev_focus if valid */
2956                 if (ws->focus_prev && ws->focus_prev != cur_focus &&
2957                     find_window(WINID(ws->focus_prev)))
2958                         winfocus = ws->focus_prev;
2959                 goto done;
2960         }
2961
2962         /* if transient focus on parent */
2963         if (cur_focus->transient) {
2964                 winfocus = find_window(cur_focus->transient);
2965                 goto done;
2966         }
2967
2968         /* if in max_stack try harder */
2969         if ((win->quirks & SWM_Q_FOCUSPREV) ||
2970             (ws->cur_layout->flags & SWM_L_FOCUSPREV)) {
2971                 if (cur_focus != ws->focus_prev)
2972                         winfocus = ws->focus_prev;
2973                 else
2974                         winfocus = TAILQ_PREV(win, ws_win_list, entry);
2975                 if (winfocus)
2976                         goto done;
2977         }
2978
2979         DNPRINTF(SWM_D_FOCUS, "focus_prev: focus_close: %d\n", focus_close);
2980
2981         if (winfocus == NULL || winfocus == win) {
2982                 switch (focus_close) {
2983                 case SWM_STACK_BOTTOM:
2984                         winfocus = TAILQ_FIRST(wl);
2985                         break;
2986                 case SWM_STACK_TOP:
2987                         winfocus = TAILQ_LAST(wl, ws_win_list);
2988                         break;
2989                 case SWM_STACK_ABOVE:
2990                         if ((winfocus = TAILQ_NEXT(cur_focus, entry)) == NULL) {
2991                                 if (focus_close_wrap)
2992                                         winfocus = TAILQ_FIRST(wl);
2993                                 else
2994                                         winfocus = TAILQ_PREV(cur_focus,
2995                                             ws_win_list, entry);
2996                         }
2997                         break;
2998                 case SWM_STACK_BELOW:
2999                         if ((winfocus = TAILQ_PREV(cur_focus, ws_win_list,
3000                             entry)) == NULL) {
3001                                 if (focus_close_wrap)
3002                                         winfocus = TAILQ_LAST(wl, ws_win_list);
3003                                 else
3004                                         winfocus = TAILQ_NEXT(cur_focus, entry);
3005                         }
3006                         break;
3007                 }
3008         }
3009 done:
3010         if (winfocus == NULL) {
3011                 if (focus_default == SWM_STACK_TOP)
3012                         winfocus = TAILQ_LAST(wl, ws_win_list);
3013                 else
3014                         winfocus = TAILQ_FIRST(wl);
3015         }
3016
3017         kill_refs(win);
3018         focus_magic(winfocus);
3019 }
3020
3021 void
3022 focus(struct swm_region *r, union arg *args)
3023 {
3024         struct ws_win           *winfocus = NULL, *head;
3025         struct ws_win           *cur_focus = NULL;
3026         struct ws_win_list      *wl = NULL;
3027         struct workspace        *ws = NULL;
3028         int                     all_iconics;
3029
3030         if (!(r && r->ws))
3031                 return;
3032
3033         DNPRINTF(SWM_D_FOCUS, "focus: id: %d\n", args->id);
3034
3035         /* treat FOCUS_CUR special */
3036         if (args->id == SWM_ARG_ID_FOCUSCUR) {
3037                 if (r->ws->focus && r->ws->focus->iconic == 0)
3038                         winfocus = r->ws->focus;
3039                 else if (r->ws->focus_prev && r->ws->focus_prev->iconic == 0)
3040                         winfocus = r->ws->focus_prev;
3041                 else
3042                         TAILQ_FOREACH(winfocus, &r->ws->winlist, entry)
3043                                 if (winfocus->iconic == 0)
3044                                         break;
3045
3046                 focus_magic(winfocus);
3047                 return;
3048         }
3049
3050         if ((cur_focus = r->ws->focus) == NULL)
3051                 return;
3052         ws = r->ws;
3053         wl = &ws->winlist;
3054         if (TAILQ_EMPTY(wl))
3055                 return;
3056         /* make sure there is at least one uniconified window */
3057         all_iconics = 1;
3058         TAILQ_FOREACH(winfocus, wl, entry)
3059                 if (winfocus->iconic == 0) {
3060                         all_iconics = 0;
3061                         break;
3062                 }
3063         if (all_iconics)
3064                 return;
3065
3066         switch (args->id) {
3067         case SWM_ARG_ID_FOCUSPREV:
3068                 head = TAILQ_PREV(cur_focus, ws_win_list, entry);
3069                 if (head == NULL)
3070                         head = TAILQ_LAST(wl, ws_win_list);
3071                 winfocus = head;
3072                 if (WINID(winfocus) == cur_focus->transient) {
3073                         head = TAILQ_PREV(winfocus, ws_win_list, entry);
3074                         if (head == NULL)
3075                                 head = TAILQ_LAST(wl, ws_win_list);
3076                         winfocus = head;
3077                 }
3078
3079                 /* skip iconics */
3080                 if (winfocus && winfocus->iconic) {
3081                         while (winfocus != cur_focus) {
3082                                 if (winfocus == NULL)
3083                                         winfocus = TAILQ_LAST(wl, ws_win_list);
3084                                 if (winfocus->iconic == 0)
3085                                         break;
3086                                 winfocus = TAILQ_PREV(winfocus, ws_win_list,
3087                                     entry);
3088                         }
3089                 }
3090                 break;
3091
3092         case SWM_ARG_ID_FOCUSNEXT:
3093                 head = TAILQ_NEXT(cur_focus, entry);
3094                 if (head == NULL)
3095                         head = TAILQ_FIRST(wl);
3096                 winfocus = head;
3097
3098                 /* skip iconics */
3099                 if (winfocus && winfocus->iconic) {
3100                         while (winfocus != cur_focus) {
3101                                 if (winfocus == NULL)
3102                                         winfocus = TAILQ_FIRST(wl);
3103                                 if (winfocus->iconic == 0)
3104                                         break;
3105                                 winfocus = TAILQ_NEXT(winfocus, entry);
3106                         }
3107                 }
3108                 break;
3109
3110         case SWM_ARG_ID_FOCUSMAIN:
3111                 winfocus = TAILQ_FIRST(wl);
3112                 if (winfocus == cur_focus)
3113                         winfocus = cur_focus->ws->focus_prev;
3114                 break;
3115
3116         default:
3117                 return;
3118         }
3119
3120         focus_magic(winfocus);
3121 }
3122
3123 void
3124 cycle_layout(struct swm_region *r, union arg *args)
3125 {
3126         struct workspace        *ws = r->ws;
3127         union arg               a;
3128
3129         DNPRINTF(SWM_D_EVENT, "cycle_layout: workspace: %d\n", ws->idx);
3130
3131         ws->cur_layout++;
3132         if (ws->cur_layout->l_stack == NULL)
3133                 ws->cur_layout = &layouts[0];
3134
3135         stack();
3136         if (focus_mode == SWM_FOCUS_DEFAULT)
3137                 drain_enter_notify();
3138         a.id = SWM_ARG_ID_FOCUSCUR;
3139         focus(r, &a);
3140 }
3141
3142 void
3143 stack_config(struct swm_region *r, union arg *args)
3144 {
3145         struct workspace        *ws = r->ws;
3146
3147         DNPRINTF(SWM_D_STACK, "stack_config: id: %d workspace: %d\n",
3148             args->id, ws->idx);
3149
3150         if (ws->cur_layout->l_config != NULL)
3151                 ws->cur_layout->l_config(ws, args->id);
3152
3153         if (args->id != SWM_ARG_ID_STACKINIT)
3154                 stack();
3155         bar_update();
3156 }
3157
3158 void
3159 stack(void) {
3160         struct swm_geometry     g;
3161         struct swm_region       *r;
3162         int                     i, num_screens;
3163 #ifdef SWM_DEBUG
3164         int j;
3165 #endif
3166
3167         DNPRINTF(SWM_D_STACK, "stack: begin\n");
3168
3169         num_screens = xcb_setup_roots_length(xcb_get_setup(conn));
3170         for (i = 0; i < num_screens; i++) {
3171 #ifdef SWM_DEBUG
3172                 j = 0;
3173 #endif
3174                 TAILQ_FOREACH(r, &screens[i].rl, entry) {
3175                         DNPRINTF(SWM_D_STACK, "stack: workspace: %d "
3176                             "(screen: %d, region: %d)\n", r->ws->idx, i, j++);
3177
3178                         /* start with screen geometry, adjust for bar */
3179                         g = r->g;
3180                         g.w -= 2 * border_width;
3181                         g.h -= 2 * border_width;
3182                         if (bar_enabled) {
3183                                 if (!bar_at_bottom)
3184                                         g.y += bar_height;
3185                                 g.h -= bar_height;
3186                         }
3187                         r->ws->cur_layout->l_stack(r->ws, &g);
3188                         r->ws->cur_layout->l_string(r->ws);
3189                         /* save r so we can track region changes */
3190                         r->ws->old_r = r;
3191                 }
3192         }
3193         if (font_adjusted)
3194                 font_adjusted--;
3195
3196         if (focus_mode == SWM_FOCUS_DEFAULT)
3197                 drain_enter_notify();
3198
3199         DNPRINTF(SWM_D_STACK, "stack: end\n");
3200 }
3201
3202 void
3203 store_float_geom(struct ws_win *win, struct swm_region *r)
3204 {
3205         /* retain window geom and region geom */
3206         win->g_float = win->g;
3207         win->g_float.x -= X(r);
3208         win->g_float.y -= Y(r);
3209         win->g_floatvalid = 1;
3210         DNPRINTF(SWM_D_MISC, "store_float_geom: window: 0x%x, g: (%d,%d)"
3211             " %d x %d, g_float: (%d,%d) %d x %d\n", win->id, X(win), Y(win),
3212             WIDTH(win), HEIGHT(win), win->g_float.x, win->g_float.y,
3213             win->g_float.w, win->g_float.h);
3214 }
3215
3216 void
3217 stack_floater(struct ws_win *win, struct swm_region *r)
3218 {
3219         if (win == NULL)
3220                 return;
3221
3222         DNPRINTF(SWM_D_MISC, "stack_floater: window: 0x%x\n", win->id);
3223
3224         /*
3225          * to allow windows to change their size (e.g. mplayer fs) only retrieve
3226          * geom on ws switches or return from max mode
3227          */
3228         if (win->g_floatvalid && (win->floatmaxed || (r != r->ws->old_r &&
3229             !(win->ewmh_flags & EWMH_F_FULLSCREEN)))) {
3230                 /* refloat at last floating relative position */
3231                 win->g = win->g_float;
3232                 X(win) += X(r);
3233                 Y(win) += Y(r);
3234         }
3235
3236         win->floatmaxed = 0;
3237
3238         /*
3239          * if set to fullscreen mode, configure window to maximum size.
3240          */
3241         if (win->ewmh_flags & EWMH_F_FULLSCREEN) {
3242                 if (!win->g_floatvalid)
3243                         store_float_geom(win, win->ws->r);
3244
3245                 win->g = win->ws->r->g;
3246         }
3247
3248         /*
3249          * remove border on fullscreen floater when in fullscreen mode or when
3250          * the quirk is present.
3251          */
3252         if ((win->ewmh_flags & EWMH_F_FULLSCREEN) ||
3253             ((win->quirks & SWM_Q_FULLSCREEN) &&
3254              (WIDTH(win) >= WIDTH(r)) && (HEIGHT(win) >= HEIGHT(r)))) {
3255                 if (win->bordered) {
3256                         win->bordered = 0;
3257                         X(win) += border_width;
3258                         Y(win) += border_width;
3259                 }
3260         } else if (!win->bordered) {
3261                 win->bordered = 1;
3262                 X(win) -= border_width;
3263                 Y(win) -= border_width;
3264         }
3265
3266         if (win->transient && (win->quirks & SWM_Q_TRANSSZ)) {
3267                 WIDTH(win) = (double)WIDTH(r) * dialog_ratio;
3268                 HEIGHT(win) = (double)HEIGHT(r) * dialog_ratio;
3269         }
3270
3271         if (!win->manual) {
3272                 /*
3273                  * floaters and transients are auto-centred unless moved
3274                  * or resized
3275                  */
3276                 X(win) = X(r) + (WIDTH(r) - WIDTH(win)) /  2 - BORDER(win);
3277                 Y(win) = Y(r) + (HEIGHT(r) - HEIGHT(win)) / 2 - BORDER(win);
3278         }
3279
3280         /* keep window within region bounds */
3281         constrain_window(win, r, 0);
3282
3283         update_window(win);
3284 }
3285
3286 /*
3287  * Send keystrokes to terminal to decrease/increase the font size as the
3288  * window size changes.
3289  */
3290 void
3291 adjust_font(struct ws_win *win)
3292 {
3293         if (!(win->quirks & SWM_Q_XTERM_FONTADJ) ||
3294             win->floating || win->transient)
3295                 return;
3296
3297         if (win->sh.width_inc && win->last_inc != win->sh.width_inc &&
3298             WIDTH(win) / win->sh.width_inc < term_width &&
3299             win->font_steps < SWM_MAX_FONT_STEPS) {
3300                 win->font_size_boundary[win->font_steps] =
3301                     (win->sh.width_inc * term_width) + win->sh.base_width;
3302                 win->font_steps++;
3303                 font_adjusted++;
3304                 win->last_inc = win->sh.width_inc;
3305                 fake_keypress(win, XK_KP_Subtract, ShiftMask);
3306         } else if (win->font_steps && win->last_inc != win->sh.width_inc &&
3307             WIDTH(win) > win->font_size_boundary[win->font_steps - 1]) {
3308                 win->font_steps--;
3309                 font_adjusted++;
3310                 win->last_inc = win->sh.width_inc;
3311                 fake_keypress(win, XK_KP_Add, ShiftMask);
3312         }
3313 }
3314
3315 #define SWAPXY(g)       do {                            \
3316         int tmp;                                        \
3317         tmp = (g)->y; (g)->y = (g)->x; (g)->x = tmp;    \
3318         tmp = (g)->h; (g)->h = (g)->w; (g)->w = tmp;    \
3319 } while (0)
3320 void
3321 stack_master(struct workspace *ws, struct swm_geometry *g, int rot, int flip)
3322 {
3323         struct swm_geometry     win_g, r_g = *g;
3324         struct ws_win           *win, *fs_win = NULL;
3325         int                     i, j, s, stacks;
3326         int                     w_inc = 1, h_inc, w_base = 1, h_base;
3327         int                     hrh, extra = 0, h_slice, last_h = 0;
3328         int                     split, colno, winno, mwin, msize, mscale;
3329         int                     remain, missing, v_slice, reconfigure;
3330         int                     bordered = 1;
3331         xcb_get_window_attributes_reply_t *war;
3332
3333         DNPRINTF(SWM_D_STACK, "stack_master: workspace: %d, rot: %s, "
3334             "flip: %s\n", ws->idx, YESNO(rot), YESNO(flip));
3335
3336         winno = count_win(ws, 0);
3337         if (winno == 0 && count_win(ws, 1) == 0)
3338                 return;
3339
3340         TAILQ_FOREACH(win, &ws->winlist, entry)
3341                 if (win->transient == 0 && win->floating == 0
3342                     && win->iconic == 0)
3343                         break;
3344
3345         if (win == NULL)
3346                 goto notiles;
3347
3348         if (rot) {
3349                 w_inc = win->sh.width_inc;
3350                 w_base = win->sh.base_width;
3351                 mwin = ws->l_state.horizontal_mwin;
3352                 mscale = ws->l_state.horizontal_msize;
3353                 stacks = ws->l_state.horizontal_stacks;
3354                 SWAPXY(&r_g);
3355         } else {
3356                 w_inc = win->sh.height_inc;
3357                 w_base = win->sh.base_height;
3358                 mwin = ws->l_state.vertical_mwin;
3359                 mscale = ws->l_state.vertical_msize;
3360                 stacks = ws->l_state.vertical_stacks;
3361         }
3362         win_g = r_g;
3363
3364         if (stacks > winno - mwin)
3365                 stacks = winno - mwin;
3366         if (stacks < 1)
3367                 stacks = 1;
3368
3369         h_slice = r_g.h / SWM_H_SLICE;
3370         if (mwin && winno > mwin) {
3371                 v_slice = r_g.w / SWM_V_SLICE;
3372
3373                 split = mwin;
3374                 colno = split;
3375                 win_g.w = v_slice * mscale;
3376
3377                 if (w_inc > 1 && w_inc < v_slice) {
3378                         /* adjust for window's requested size increment */
3379                         remain = (win_g.w - w_base) % w_inc;
3380                         win_g.w -= remain;
3381                 }
3382
3383                 msize = win_g.w;
3384                 if (flip)
3385                         win_g.x += r_g.w - msize;
3386         } else {
3387                 msize = -2;
3388                 colno = split = winno / stacks;
3389                 win_g.w = ((r_g.w - (stacks * 2 * border_width) +
3390                     2 * border_width) / stacks);
3391         }
3392         hrh = r_g.h / colno;
3393         extra = r_g.h - (colno * hrh);
3394         win_g.h = hrh - 2 * border_width;
3395
3396         /*  stack all the tiled windows */
3397         i = j = 0, s = stacks;
3398         TAILQ_FOREACH(win, &ws->winlist, entry) {
3399                 if (win->transient != 0 || win->floating != 0)
3400                         continue;
3401                 if (win->iconic != 0)
3402                         continue;
3403
3404                 if (win->ewmh_flags & EWMH_F_FULLSCREEN) {
3405                         fs_win = win;
3406                         continue;
3407                 }
3408
3409                 if (split && i == split) {
3410                         colno = (winno - mwin) / stacks;
3411                         if (s <= (winno - mwin) % stacks)
3412                                 colno++;
3413                         split = split + colno;
3414                         hrh = (r_g.h / colno);
3415                         extra = r_g.h - (colno * hrh);
3416                         if (flip)
3417                                 win_g.x = r_g.x;
3418                         else
3419                                 win_g.x += win_g.w + 2 * border_width;
3420                         win_g.w = (r_g.w - msize -
3421                             (stacks * 2 * border_width)) / stacks;
3422                         if (s == 1)
3423                                 win_g.w += (r_g.w - msize -
3424                                     (stacks * 2 * border_width)) % stacks;
3425                         s--;
3426                         j = 0;
3427                 }
3428                 win_g.h = hrh - 2 * border_width;
3429                 if (rot) {
3430                         h_inc = win->sh.width_inc;
3431                         h_base = win->sh.base_width;
3432                 } else {
3433                         h_inc = win->sh.height_inc;
3434                         h_base = win->sh.base_height;
3435                 }
3436                 if (j == colno - 1) {
3437                         win_g.h = hrh + extra;
3438                 } else if (h_inc > 1 && h_inc < h_slice) {
3439                         /* adjust for window's requested size increment */
3440                         remain = (win_g.h - h_base) % h_inc;
3441                         missing = h_inc - remain;
3442
3443                         if (missing <= extra || j == 0) {
3444                                 extra -= missing;
3445                                 win_g.h += missing;
3446                         } else {
3447                                 win_g.h -= remain;
3448                                 extra += remain;
3449                         }
3450                 }
3451
3452                 if (j == 0)
3453                         win_g.y = r_g.y;
3454                 else
3455                         win_g.y += last_h + 2 * border_width;
3456
3457                 if (disable_border && bar_enabled == 0 && winno == 1){
3458                         bordered = 0;
3459                         win_g.w += 2 * border_width;
3460                         win_g.h += 2 * border_width;
3461                 } else {
3462                         bordered = 1;
3463                 }
3464                 if (rot) {
3465                         if (X(win) != win_g.y || Y(win) != win_g.x ||
3466                             WIDTH(win) != win_g.h || HEIGHT(win) != win_g.w) {
3467                                 reconfigure = 1;
3468                                 X(win) = win_g.y;
3469                                 Y(win) = win_g.x;
3470                                 WIDTH(win) = win_g.h;
3471                                 HEIGHT(win) = win_g.w;
3472                         }
3473                 } else {
3474                         if (X(win) != win_g.x || Y(win) != win_g.y ||
3475                             WIDTH(win) != win_g.w || HEIGHT(win) != win_g.h) {
3476                                 reconfigure = 1;
3477                                 X(win) = win_g.x;
3478                                 Y(win) = win_g.y;
3479                                 WIDTH(win) = win_g.w;
3480                                 HEIGHT(win) = win_g.h;
3481                         }
3482                 }
3483
3484                 if (bordered != win->bordered) {
3485                         reconfigure = 1;
3486                         win->bordered = bordered;
3487                 }
3488
3489                 if (reconfigure) {
3490                         adjust_font(win);
3491                         update_window(win);
3492                 }
3493
3494                 war = xcb_get_window_attributes_reply(conn,
3495                         xcb_get_window_attributes(conn, win->id),
3496                         NULL);
3497                 if (war) {
3498                         if (war->map_state == XCB_MAP_STATE_UNMAPPED)
3499                                 map_window_raised(win->id);
3500                         free(war);
3501                 }
3502
3503                 last_h = win_g.h;
3504                 i++;
3505                 j++;
3506         }
3507
3508 notiles:
3509         /* now, stack all the floaters and transients */
3510         TAILQ_FOREACH(win, &ws->winlist, entry) {
3511                 if (win->transient == 0 && win->floating == 0)
3512                         continue;
3513                 if (win->iconic == 1)
3514                         continue;
3515                 if (win->ewmh_flags & EWMH_F_FULLSCREEN) {
3516                         fs_win = win;
3517                         continue;
3518                 }
3519
3520                 stack_floater(win, ws->r);
3521                 map_window_raised(win->id);
3522         }
3523
3524         if (fs_win) {
3525                 stack_floater(fs_win, ws->r);
3526                 map_window_raised(fs_win->id);
3527         }
3528 }
3529
3530 void
3531 vertical_config(struct workspace *ws, int id)
3532 {
3533         DNPRINTF(SWM_D_STACK, "vertical_config: id: %d, workspace: %d\n",
3534             id, ws->idx);
3535
3536         switch (id) {
3537         case SWM_ARG_ID_STACKRESET:
3538         case SWM_ARG_ID_STACKINIT:
3539                 ws->l_state.vertical_msize = SWM_V_SLICE / 2;
3540                 ws->l_state.vertical_mwin = 1;
3541                 ws->l_state.vertical_stacks = 1;
3542                 break;
3543         case SWM_ARG_ID_MASTERSHRINK:
3544                 if (ws->l_state.vertical_msize > 1)
3545                         ws->l_state.vertical_msize--;
3546                 break;
3547         case SWM_ARG_ID_MASTERGROW:
3548                 if (ws->l_state.vertical_msize < SWM_V_SLICE - 1)
3549                         ws->l_state.vertical_msize++;
3550                 break;
3551         case SWM_ARG_ID_MASTERADD:
3552                 ws->l_state.vertical_mwin++;
3553                 break;
3554         case SWM_ARG_ID_MASTERDEL:
3555                 if (ws->l_state.vertical_mwin > 0)
3556                         ws->l_state.vertical_mwin--;
3557                 break;
3558         case SWM_ARG_ID_STACKINC:
3559                 ws->l_state.vertical_stacks++;
3560                 break;
3561         case SWM_ARG_ID_STACKDEC:
3562                 if (ws->l_state.vertical_stacks > 1)
3563                         ws->l_state.vertical_stacks--;
3564                 break;
3565         case SWM_ARG_ID_FLIPLAYOUT:
3566                 ws->l_state.vertical_flip = !ws->l_state.vertical_flip;
3567                 break;
3568         default:
3569                 return;
3570         }
3571 }
3572
3573 void
3574 vertical_stack(struct workspace *ws, struct swm_geometry *g)
3575 {
3576         DNPRINTF(SWM_D_STACK, "vertical_stack: workspace: %d\n", ws->idx);
3577
3578         stack_master(ws, g, 0, ws->l_state.vertical_flip);
3579 }
3580
3581 void
3582 horizontal_config(struct workspace *ws, int id)
3583 {
3584         DNPRINTF(SWM_D_STACK, "horizontal_config: workspace: %d\n", ws->idx);
3585
3586         switch (id) {
3587         case SWM_ARG_ID_STACKRESET:
3588         case SWM_ARG_ID_STACKINIT:
3589                 ws->l_state.horizontal_mwin = 1;
3590                 ws->l_state.horizontal_msize = SWM_H_SLICE / 2;
3591                 ws->l_state.horizontal_stacks = 1;
3592                 break;
3593         case SWM_ARG_ID_MASTERSHRINK:
3594                 if (ws->l_state.horizontal_msize > 1)
3595                         ws->l_state.horizontal_msize--;
3596                 break;
3597         case SWM_ARG_ID_MASTERGROW:
3598                 if (ws->l_state.horizontal_msize < SWM_H_SLICE - 1)
3599                         ws->l_state.horizontal_msize++;
3600                 break;
3601         case SWM_ARG_ID_MASTERADD:
3602                 ws->l_state.horizontal_mwin++;
3603                 break;
3604         case SWM_ARG_ID_MASTERDEL:
3605                 if (ws->l_state.horizontal_mwin > 0)
3606                         ws->l_state.horizontal_mwin--;
3607                 break;
3608         case SWM_ARG_ID_STACKINC:
3609                 ws->l_state.horizontal_stacks++;
3610                 break;
3611         case SWM_ARG_ID_STACKDEC:
3612                 if (ws->l_state.horizontal_stacks > 1)
3613                         ws->l_state.horizontal_stacks--;
3614                 break;
3615         case SWM_ARG_ID_FLIPLAYOUT:
3616                 ws->l_state.horizontal_flip = !ws->l_state.horizontal_flip;
3617                 break;
3618         default:
3619                 return;
3620         }
3621 }
3622
3623 void
3624 horizontal_stack(struct workspace *ws, struct swm_geometry *g)
3625 {
3626         DNPRINTF(SWM_D_STACK, "horizontal_stack: workspace: %d\n", ws->idx);
3627
3628         stack_master(ws, g, 1, ws->l_state.horizontal_flip);
3629 }
3630
3631 /* fullscreen view */
3632 void
3633 max_stack(struct workspace *ws, struct swm_geometry *g)
3634 {
3635         struct swm_geometry     gg = *g;
3636         struct ws_win           *win, *wintrans = NULL, *parent = NULL;
3637         int                     winno, num_screens;
3638
3639         DNPRINTF(SWM_D_STACK, "max_stack: workspace: %d\n", ws->idx);
3640
3641         if (ws == NULL)
3642                 return;
3643
3644         winno = count_win(ws, 0);
3645         if (winno == 0 && count_win(ws, 1) == 0)
3646                 return;
3647
3648         num_screens = xcb_setup_roots_length(xcb_get_setup(conn));
3649         TAILQ_FOREACH(win, &ws->winlist, entry) {
3650                 if (win->transient) {
3651                         wintrans = win;
3652                         parent = find_window(win->transient);
3653                         continue;
3654                 }
3655
3656                 if (win->floating && win->floatmaxed == 0 ) {
3657                         /*
3658                          * retain geometry for retrieval on exit from
3659                          * max_stack mode
3660                          */
3661                         store_float_geom(win, ws->r);
3662                         win->floatmaxed = 1;
3663                 }
3664
3665                 /* only reconfigure if necessary */
3666                 if (X(win) != gg.x || Y(win) != gg.y || WIDTH(win) != gg.w ||
3667                     HEIGHT(win) != gg.h) {
3668                         win->g = gg;
3669                         if (bar_enabled){
3670                                 win->bordered = 1;
3671                         } else {
3672                                 win->bordered = 0;
3673                                 WIDTH(win) += 2 * border_width;
3674                                 HEIGHT(win) += 2 * border_width;
3675                         }
3676
3677                         update_window(win);
3678                 }
3679                 /* unmap only if we don't have multi screen */
3680                 if (win != ws->focus)
3681                         if (!(num_screens > 1 || outputs > 1))
3682                                 unmap_window(win);
3683         }
3684
3685         /* put the last transient on top */
3686         if (wintrans) {
3687                 if (parent)
3688                         map_window_raised(parent->id);
3689                 stack_floater(wintrans, ws->r);
3690                 focus_magic(wintrans);
3691         }
3692 }
3693
3694 void
3695 send_to_ws(struct swm_region *r, union arg *args)
3696 {
3697         int                     wsid = args->id;
3698         struct ws_win           *win = NULL, *parent;
3699         struct workspace        *ws, *nws;
3700         xcb_atom_t              ws_idx_atom = XCB_ATOM_NONE;
3701         char                    ws_idx_str[SWM_PROPLEN];
3702         union arg               a;
3703
3704         if (wsid >= workspace_limit)
3705                 return;
3706
3707         if (r && r->ws && r->ws->focus)
3708                 win = r->ws->focus;
3709         else
3710                 return;
3711         if (win == NULL)
3712                 return;
3713         if (win->ws->idx == wsid)
3714                 return;
3715
3716         DNPRINTF(SWM_D_MOVE, "send_to_ws: window: 0x%x\n", win->id);
3717
3718         ws = win->ws;
3719         nws = &win->s->ws[wsid];
3720
3721         a.id = SWM_ARG_ID_FOCUSPREV;
3722         focus(r, &a);
3723         if (win->transient) {
3724                 parent = find_window(win->transient);
3725                 if (parent) {
3726                         unmap_window(parent);
3727                         TAILQ_REMOVE(&ws->winlist, parent, entry);
3728                         TAILQ_INSERT_TAIL(&nws->winlist, parent, entry);
3729                         parent->ws = nws;
3730                 }
3731         }
3732         unmap_window(win);
3733         TAILQ_REMOVE(&ws->winlist, win, entry);
3734         TAILQ_INSERT_TAIL(&nws->winlist, win, entry);
3735         if (TAILQ_EMPTY(&ws->winlist))
3736                 r->ws->focus = NULL;
3737         win->ws = nws;
3738
3739         /* Try to update the window's workspace property */
3740         ws_idx_atom = get_atom_from_string("_SWM_WS");
3741         if (ws_idx_atom &&
3742             snprintf(ws_idx_str, SWM_PROPLEN, "%d", nws->idx) <
3743                 SWM_PROPLEN) {
3744                 DNPRINTF(SWM_D_PROP, "send_to_ws: set property: _SWM_WS: %s\n",
3745                     ws_idx_str);
3746                 xcb_change_property(conn, XCB_PROP_MODE_REPLACE, win->id,
3747                         ws_idx_atom, XCB_ATOM_STRING, 8, strlen(ws_idx_str),
3748                         ws_idx_str);
3749         }
3750
3751         stack();
3752         bar_update();
3753 }
3754
3755 void
3756 pressbutton(struct swm_region *r, union arg *args)
3757 {
3758         XTestFakeButtonEvent(display, args->id, True, CurrentTime);
3759         XTestFakeButtonEvent(display, args->id, False, CurrentTime);
3760 }
3761
3762 void
3763 raise_toggle(struct swm_region *r, union arg *args)
3764 {
3765         if (r == NULL || r->ws == NULL)
3766                 return;
3767
3768         r->ws->always_raise = !r->ws->always_raise;
3769
3770         /* bring floaters back to top */
3771         if (r->ws->always_raise == 0)
3772                 stack();
3773 }
3774
3775 void
3776 iconify(struct swm_region *r, union arg *args)
3777 {
3778         union arg a;
3779
3780         if (r->ws->focus == NULL)
3781                 return;
3782         unmap_window(r->ws->focus);
3783         update_iconic(r->ws->focus, 1);
3784         stack();
3785         if (focus_mode == SWM_FOCUS_DEFAULT)
3786                 drain_enter_notify();
3787         r->ws->focus = NULL;
3788         a.id = SWM_ARG_ID_FOCUSCUR;
3789         focus(r, &a);
3790 }
3791
3792 char *
3793 get_win_name(xcb_window_t win)
3794 {
3795         char                            *name = NULL;
3796         xcb_get_property_cookie_t       c;
3797         xcb_icccm_get_text_property_reply_t     r;
3798
3799         c = xcb_icccm_get_wm_name(conn, win);
3800         if (xcb_icccm_get_wm_name_reply(conn, c, &r, NULL)) {
3801                 if (r.name_len > 0) {
3802                         name = malloc(r.name_len + 1);
3803                         if (!name) {
3804                                 xcb_icccm_get_text_property_reply_wipe(&r);
3805                                 return (NULL);
3806                         }
3807                         memcpy(name, r.name, r.name_len);
3808                         name[r.name_len] = '\0';
3809                 }
3810                 xcb_icccm_get_text_property_reply_wipe(&r);
3811         }
3812
3813         return (name);
3814 }
3815
3816 void
3817 uniconify(struct swm_region *r, union arg *args)
3818 {
3819         struct ws_win           *win;
3820         FILE                    *lfile;
3821         char                    *name;
3822         int                     count = 0;
3823
3824         DNPRINTF(SWM_D_MISC, "uniconify\n");
3825
3826         if (r == NULL || r->ws == NULL)
3827                 return;
3828
3829         /* make sure we have anything to uniconify */
3830         TAILQ_FOREACH(win, &r->ws->winlist, entry) {
3831                 if (win->ws == NULL)
3832                         continue; /* should never happen */
3833                 if (win->iconic == 0)
3834                         continue;
3835                 count++;
3836         }
3837         if (count == 0)
3838                 return;
3839
3840         search_r = r;
3841         search_resp_action = SWM_SEARCH_UNICONIFY;
3842
3843         spawn_select(r, args, "search", &searchpid);
3844
3845         if ((lfile = fdopen(select_list_pipe[1], "w")) == NULL)
3846                 return;
3847
3848         TAILQ_FOREACH(win, &r->ws->winlist, entry) {
3849                 if (win->ws == NULL)
3850                         continue; /* should never happen */
3851                 if (win->iconic == 0)
3852                         continue;
3853
3854                 name = get_win_name(win->id);
3855                 if (name == NULL)
3856                         continue;
3857                 fprintf(lfile, "%s.%u\n", name, win->id);
3858                 free(name);
3859         }
3860
3861         fclose(lfile);
3862 }
3863
3864 void
3865 name_workspace(struct swm_region *r, union arg *args)
3866 {
3867         FILE                    *lfile;
3868
3869         DNPRINTF(SWM_D_MISC, "name_workspace\n");
3870
3871         if (r == NULL)
3872                 return;
3873
3874         search_r = r;
3875         search_resp_action = SWM_SEARCH_NAME_WORKSPACE;
3876
3877         spawn_select(r, args, "name_workspace", &searchpid);
3878
3879         if ((lfile = fdopen(select_list_pipe[1], "w")) == NULL)
3880                 return;
3881
3882         fprintf(lfile, "%s", "");
3883         fclose(lfile);
3884 }
3885
3886 void
3887 search_workspace(struct swm_region *r, union arg *args)
3888 {
3889         int                     i;
3890         struct workspace        *ws;
3891         FILE                    *lfile;
3892
3893         DNPRINTF(SWM_D_MISC, "search_workspace\n");
3894
3895         if (r == NULL)
3896                 return;
3897
3898         search_r = r;
3899         search_resp_action = SWM_SEARCH_SEARCH_WORKSPACE;
3900
3901         spawn_select(r, args, "search", &searchpid);
3902
3903         if ((lfile = fdopen(select_list_pipe[1], "w")) == NULL)
3904                 return;
3905
3906         for (i = 0; i < workspace_limit; i++) {
3907                 ws = &r->s->ws[i];
3908                 if (ws == NULL)
3909                         continue;
3910                 fprintf(lfile, "%d%s%s\n", ws->idx + 1,
3911                     (ws->name ? ":" : ""), (ws->name ? ws->name : ""));
3912         }
3913
3914         fclose(lfile);
3915 }
3916
3917 void
3918 search_win_cleanup(void)
3919 {
3920         struct search_window    *sw = NULL;
3921
3922         while ((sw = TAILQ_FIRST(&search_wl)) != NULL) {
3923                 xcb_destroy_window(conn, sw->indicator);
3924                 xcb_free_gc(conn, sw->gc);
3925                 TAILQ_REMOVE(&search_wl, sw, entry);
3926                 free(sw);
3927         }
3928 }
3929
3930 void
3931 search_win(struct swm_region *r, union arg *args)
3932 {
3933         struct ws_win           *win = NULL;
3934         struct search_window    *sw = NULL;
3935         xcb_window_t            w;
3936         uint32_t                gcv[1], wa[2];
3937         int                     i;
3938         char                    s[8];
3939         FILE                    *lfile;
3940         size_t                  len;
3941         XRectangle              ibox, lbox;
3942         xcb_screen_t            *screen; 
3943         DNPRINTF(SWM_D_MISC, "search_win\n");
3944
3945         search_r = r;
3946         search_resp_action = SWM_SEARCH_SEARCH_WINDOW;
3947
3948         spawn_select(r, args, "search", &searchpid);
3949
3950         if ((lfile = fdopen(select_list_pipe[1], "w")) == NULL)
3951                 return;
3952
3953         TAILQ_INIT(&search_wl);
3954
3955         i = 1;
3956         TAILQ_FOREACH(win, &r->ws->winlist, entry) {
3957                 if (win->iconic == 1)
3958                         continue;
3959
3960                 sw = calloc(1, sizeof(struct search_window));
3961                 if (sw == NULL) {
3962                         warn("search_win: calloc");
3963                         fclose(lfile);
3964                         search_win_cleanup();
3965                         return;
3966                 }
3967                 sw->idx = i;
3968                 sw->win = win;
3969                 screen = get_screen(sw->idx);
3970
3971                 snprintf(s, sizeof s, "%d", i);
3972                 len = strlen(s);
3973
3974                 XmbTextExtents(bar_fs, s, len, &ibox, &lbox);
3975
3976                 w = xcb_generate_id(conn);
3977                 wa[0] = r->s->c[SWM_S_COLOR_FOCUS].color;
3978                 wa[1] = r->s->c[SWM_S_COLOR_UNFOCUS].color;
3979                 xcb_create_window(conn, screen->root_depth, w, win->id,
3980                         0, 0, lbox.width + 4,
3981                         bar_fs_extents->max_logical_extent.height, 1,
3982                         XCB_WINDOW_CLASS_INPUT_OUTPUT, screen->root_visual,
3983                         XCB_CW_BACK_PIXEL | XCB_CW_BORDER_PIXEL, wa);   
3984                 
3985                 sw->indicator = w;
3986                 TAILQ_INSERT_TAIL(&search_wl, sw, entry);
3987
3988                 sw->gc = xcb_generate_id(conn);
3989                 gcv[0] = r->s->c[SWM_S_COLOR_BAR].color;                
3990                 xcb_create_gc(conn, sw->gc, w, XCB_GC_FOREGROUND, gcv); 
3991                 map_window_raised(w);
3992
3993                 xcb_image_text_8(conn, len, w, sw->gc, 2,
3994                     (bar_fs_extents->max_logical_extent.height -
3995                     lbox.height) / 2 - lbox.y, s);
3996
3997                 fprintf(lfile, "%d\n", i);
3998                 i++;
3999         }
4000
4001         fclose(lfile);
4002 }
4003
4004 void
4005 search_resp_uniconify(char *resp, unsigned long len)
4006 {
4007         char                    *name;
4008         struct ws_win           *win;
4009         char                    *s;
4010
4011         DNPRINTF(SWM_D_MISC, "search_resp_uniconify: resp: %s\n", resp);
4012
4013         TAILQ_FOREACH(win, &search_r->ws->winlist, entry) {
4014                 if (win->iconic == 0)
4015                         continue;
4016                 name = get_win_name(win->id);
4017                 if (name == NULL)
4018                         continue;
4019                 if (asprintf(&s, "%s.%u", name, win->id) == -1) {
4020                         free(name);
4021                         continue;
4022                 }
4023                 free(name);
4024                 if (strncmp(s, resp, len) == 0) {
4025                         /* XXX this should be a callback to generalize */
4026                         update_iconic(win, 0);
4027                         free(s);
4028                         break;
4029                 }
4030                 free(s);
4031         }
4032 }
4033
4034 void
4035 search_resp_name_workspace(char *resp, unsigned long len)
4036 {
4037         struct workspace        *ws;
4038
4039         DNPRINTF(SWM_D_MISC, "search_resp_name_workspace: resp: %s\n", resp);
4040
4041         if (search_r->ws == NULL)
4042                 return;
4043         ws = search_r->ws;
4044
4045         if (ws->name) {
4046                 free(search_r->ws->name);
4047                 search_r->ws->name = NULL;
4048         }
4049
4050         if (len > 1) {
4051                 ws->name = strdup(resp);
4052                 if (ws->name == NULL) {
4053                         DNPRINTF(SWM_D_MISC, "search_resp_name_workspace: "
4054                             "strdup: %s", strerror(errno));
4055                         return;
4056                 }
4057         }
4058 }
4059
4060 void
4061 search_resp_search_workspace(char *resp, unsigned long len)
4062 {
4063         char                    *p, *q;
4064         int                     ws_idx;
4065         const char              *errstr;
4066         union arg               a;
4067
4068         DNPRINTF(SWM_D_MISC, "search_resp_search_workspace: resp: %s\n", resp);
4069
4070         q = strdup(resp);
4071         if (!q) {
4072                 DNPRINTF(SWM_D_MISC, "search_resp_search_workspace: strdup: %s",
4073                     strerror(errno));
4074                 return;
4075         }
4076         p = strchr(q, ':');
4077         if (p != NULL)
4078                 *p = '\0';
4079         ws_idx = strtonum(q, 1, workspace_limit, &errstr);
4080         if (errstr) {
4081                 DNPRINTF(SWM_D_MISC, "workspace idx is %s: %s",
4082                     errstr, q);
4083                 free(q);
4084                 return;
4085         }
4086         free(q);
4087         a.id = ws_idx - 1;
4088         switchws(search_r, &a);
4089 }
4090
4091 void
4092 search_resp_search_window(char *resp, unsigned long len)
4093 {
4094         char                    *s;
4095         int                     idx;
4096         const char              *errstr;
4097         struct search_window    *sw;
4098
4099         DNPRINTF(SWM_D_MISC, "search_resp_search_window: resp: %s\n", resp);
4100
4101         s = strdup(resp);
4102         if (!s) {
4103                 DNPRINTF(SWM_D_MISC, "search_resp_search_window: strdup: %s",
4104                     strerror(errno));
4105                 return;
4106         }
4107
4108         idx = strtonum(s, 1, INT_MAX, &errstr);
4109         if (errstr) {
4110                 DNPRINTF(SWM_D_MISC, "window idx is %s: %s",
4111                     errstr, s);
4112                 free(s);
4113                 return;
4114         }
4115         free(s);
4116
4117         TAILQ_FOREACH(sw, &search_wl, entry)
4118                 if (idx == sw->idx) {
4119                         focus_win(sw->win);
4120                         break;
4121                 }
4122 }
4123
4124 #define MAX_RESP_LEN    1024
4125
4126 void
4127 search_do_resp(void)
4128 {
4129         ssize_t                 rbytes;
4130         char                    *resp;
4131         unsigned long           len;
4132
4133         DNPRINTF(SWM_D_MISC, "search_do_resp:\n");
4134
4135         search_resp = 0;
4136         searchpid = 0;
4137
4138         if ((resp = calloc(1, MAX_RESP_LEN + 1)) == NULL) {
4139                 warn("search: calloc");
4140                 goto done;
4141         }
4142
4143         rbytes = read(select_resp_pipe[0], resp, MAX_RESP_LEN);
4144         if (rbytes <= 0) {
4145                 warn("search: read error");
4146                 goto done;
4147         }
4148         resp[rbytes] = '\0';
4149
4150         /* XXX:
4151          * Older versions of dmenu (Atleast pre 4.4.1) do not send a
4152          * newline, so work around that by sanitizing the resp now.
4153          */
4154         resp[strcspn(resp, "\n")] = '\0';
4155         len = strlen(resp);
4156
4157         switch (search_resp_action) {
4158         case SWM_SEARCH_UNICONIFY:
4159                 search_resp_uniconify(resp, len);
4160                 break;
4161         case SWM_SEARCH_NAME_WORKSPACE:
4162                 search_resp_name_workspace(resp, len);
4163                 break;
4164         case SWM_SEARCH_SEARCH_WORKSPACE:
4165                 search_resp_search_workspace(resp, len);
4166                 break;
4167         case SWM_SEARCH_SEARCH_WINDOW:
4168                 search_resp_search_window(resp, len);
4169                 break;
4170         }
4171
4172 done:
4173         if (search_resp_action == SWM_SEARCH_SEARCH_WINDOW)
4174                 search_win_cleanup();
4175
4176         search_resp_action = SWM_SEARCH_NONE;
4177         close(select_resp_pipe[0]);
4178         free(resp);
4179 }
4180
4181 void
4182 wkill(struct swm_region *r, union arg *args)
4183 {
4184         DNPRINTF(SWM_D_MISC, "wkill: id: %d\n", args->id);
4185
4186         if (r->ws->focus == NULL)
4187                 return;
4188
4189         if (args->id == SWM_ARG_ID_KILLWINDOW)
4190                 xcb_kill_client(conn, r->ws->focus->id);
4191         else
4192                 if (r->ws->focus->can_delete)
4193                         client_msg(r->ws->focus, adelete);
4194 }
4195
4196
4197 int
4198 floating_toggle_win(struct ws_win *win)
4199 {
4200         struct swm_region       *r;
4201
4202         if (win == NULL)
4203                 return (0);
4204
4205         if (!win->ws->r)
4206                 return (0);
4207
4208         r = win->ws->r;
4209
4210         /* reject floating toggles in max stack mode */
4211         if (win->ws->cur_layout == &layouts[SWM_MAX_STACK])
4212                 return (0);
4213
4214         if (win->floating) {
4215                 if (!win->floatmaxed) {
4216                         /* retain position for refloat */
4217                         store_float_geom(win, r);
4218                 }
4219                 win->floating = 0;
4220         } else {
4221                 if (win->g_floatvalid) {
4222                         /* refloat at last floating relative position */
4223                         X(win) = win->g_float.x + X(r);
4224                         Y(win) = win->g_float.y + Y(r);
4225                         WIDTH(win) = win->g_float.w;
4226                         HEIGHT(win) = win->g_float.h;
4227                 }
4228                 win->floating = 1;
4229         }
4230
4231         ewmh_update_actions(win);
4232
4233         return (1);
4234 }
4235
4236 void
4237 floating_toggle(struct swm_region *r, union arg *args)
4238 {
4239         struct ws_win           *win = r->ws->focus;
4240         union arg               a;
4241
4242         if (win == NULL)
4243                 return;
4244
4245         ewmh_update_win_state(win, ewmh[_NET_WM_STATE_ABOVE].atom,
4246             _NET_WM_STATE_TOGGLE);
4247
4248         stack();
4249         if (focus_mode == SWM_FOCUS_DEFAULT)
4250                 drain_enter_notify();
4251
4252         if (win == win->ws->focus) {
4253                 a.id = SWM_ARG_ID_FOCUSCUR;
4254                 focus(win->ws->r, &a);
4255         }
4256 }
4257
4258 void
4259 constrain_window(struct ws_win *win, struct swm_region *r, int resizable)
4260 {
4261         if (MAX_X(win) + BORDER(win) > MAX_X(r)) {
4262                 if (resizable)
4263                         WIDTH(win) = MAX_X(r) - X(win) - BORDER(win);
4264                 else
4265                         X(win) = MAX_X(r)- WIDTH(win) - BORDER(win);
4266         }
4267
4268         if (X(win) + BORDER(win) < X(r)) {
4269                 if (resizable)
4270                         WIDTH(win) -= X(r) - X(win) - BORDER(win);
4271
4272                 X(win) = X(r) - BORDER(win);
4273         }
4274
4275         if (MAX_Y(win) + BORDER(win) > MAX_Y(r)) {
4276                 if (resizable)
4277                         HEIGHT(win) = MAX_Y(r) - Y(win) - BORDER(win);
4278                 else
4279                         Y(win) = MAX_Y(r) - HEIGHT(win) - BORDER(win);
4280         }
4281
4282         if (Y(win) + BORDER(win) < Y(r)) {
4283                 if (resizable)
4284                         HEIGHT(win) -= Y(r) - Y(win) - BORDER(win);
4285
4286                 Y(win) = Y(r) - BORDER(win);
4287         }
4288
4289         if (resizable) {
4290                 if (WIDTH(win) < 1)
4291                         WIDTH(win) = 1;
4292                 if (HEIGHT(win) < 1)
4293                         HEIGHT(win) = 1;
4294         }
4295 }
4296
4297 void
4298 update_window(struct ws_win *win)
4299 {
4300         uint16_t        mask;
4301         uint32_t        wc[5];
4302
4303         mask = XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y |
4304                 XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT |
4305                 XCB_CONFIG_WINDOW_BORDER_WIDTH;
4306         wc[0] = X(win);
4307         wc[1] = Y(win);
4308         wc[2] = WIDTH(win);
4309         wc[3] = HEIGHT(win);
4310         wc[4] = BORDER(win);
4311
4312         DNPRINTF(SWM_D_EVENT, "update_window: window: 0x%x, (x,y) w x h: "
4313             "(%d,%d) %d x %d, bordered: %s\n", win->id, wc[0], wc[1], wc[2],
4314             wc[3], YESNO(win->bordered));
4315
4316         xcb_configure_window(conn, win->id, mask, wc);
4317 }
4318
4319 #define SWM_RESIZE_STEPS        (50)
4320
4321 void
4322 resize(struct ws_win *win, union arg *args)
4323 {
4324         XEvent                  ev;
4325         Time                    time = 0;
4326         struct swm_region       *r = NULL;
4327         int                     resize_step = 0;
4328         struct swm_geometry     g;
4329         int                     top = 0, left = 0;
4330         int                     dx, dy;
4331         unsigned int            shape; /* cursor style */
4332         xcb_cursor_t            cursor;
4333         xcb_font_t              cursor_font;
4334         xcb_grab_pointer_reply_t        *gpr;
4335         xcb_query_pointer_reply_t       *xpr;
4336
4337         if (win == NULL)
4338                 return;
4339         r = win->ws->r;
4340
4341         if (win->ewmh_flags & EWMH_F_FULLSCREEN)
4342                 return;
4343
4344         DNPRINTF(SWM_D_MOUSE, "resize: window: 0x%x, floating: %s, "
4345             "transient: 0x%x\n", win->id, YESNO(win->floating),
4346             win->transient);
4347
4348         if (!(win->transient != 0 || win->floating != 0))
4349                 return;
4350
4351         /* reject resizes in max mode for floaters (transient ok) */
4352         if (win->floatmaxed)
4353                 return;
4354
4355         win->manual = 1;
4356         ewmh_update_win_state(win, ewmh[_SWM_WM_STATE_MANUAL].atom,
4357             _NET_WM_STATE_ADD);
4358
4359         stack();
4360
4361         switch (args->id) {
4362         case SWM_ARG_ID_WIDTHSHRINK:
4363                 WIDTH(win) -= SWM_RESIZE_STEPS;
4364                 resize_step = 1;
4365                 break;
4366         case SWM_ARG_ID_WIDTHGROW:
4367                 WIDTH(win) += SWM_RESIZE_STEPS;
4368                 resize_step = 1;
4369                 break;
4370         case SWM_ARG_ID_HEIGHTSHRINK:
4371                 HEIGHT(win) -= SWM_RESIZE_STEPS;
4372                 resize_step = 1;
4373                 break;
4374         case SWM_ARG_ID_HEIGHTGROW:
4375                 HEIGHT(win) += SWM_RESIZE_STEPS;
4376                 resize_step = 1;
4377                 break;
4378         default:
4379                 break;
4380         }
4381         if (resize_step) {
4382                 constrain_window(win, r, 1);
4383                 update_window(win);
4384                 store_float_geom(win,r);
4385                 return;
4386         }
4387
4388         if (focus_mode == SWM_FOCUS_DEFAULT)
4389                 drain_enter_notify();
4390
4391         /* get cursor offset from window root */
4392         xpr = xcb_query_pointer_reply(conn, xcb_query_pointer(conn, win->id),
4393                 NULL);
4394         if (!xpr)
4395                 return;
4396         
4397         g = win->g;
4398
4399         if (xpr->win_x < WIDTH(win) / 2)
4400                 left = 1;
4401
4402         if (xpr->win_y < HEIGHT(win) / 2)
4403                 top = 1;
4404
4405         if (args->id == SWM_ARG_ID_CENTER)
4406                 shape = XC_sizing;
4407         else if (top)
4408                 shape = (left) ? XC_top_left_corner : XC_top_right_corner;
4409         else
4410                 shape = (left) ? XC_bottom_left_corner : XC_bottom_right_corner;
4411
4412         cursor_font = xcb_generate_id(conn);
4413         xcb_open_font(conn, cursor_font, strlen("cursor"), "cursor");
4414
4415         cursor = xcb_generate_id(conn);
4416         xcb_create_glyph_cursor(conn, cursor, cursor_font, cursor_font,
4417                 shape, shape + 1, 0, 0, 0, 0xffff, 0xffff, 0xffff);
4418
4419         gpr = xcb_grab_pointer_reply(conn,
4420                 xcb_grab_pointer(conn, False, win->id, MOUSEMASK,
4421                 XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC, XCB_WINDOW_NONE,
4422                 cursor, XCB_CURRENT_TIME),
4423                 NULL);
4424         if (!gpr) {
4425                 xcb_free_cursor(conn, cursor);
4426                 xcb_close_font(conn, cursor_font);
4427                 free(xpr);
4428                 return;
4429         }
4430
4431         do {
4432                 XMaskEvent(display, MOUSEMASK | ExposureMask |
4433                     SubstructureRedirectMask, &ev);
4434                 switch (ev.type) {
4435                 case ConfigureRequest:
4436                 case Expose:
4437                 case MapRequest:
4438                         handler[ev.type](&ev);
4439                         break;
4440                 case MotionNotify:
4441                         /* cursor offset/delta from start of the operation */
4442                         dx = ev.xmotion.x_root - xpr->root_x;
4443                         dy = ev.xmotion.y_root - xpr->root_y;
4444
4445                         /* vertical */
4446                         if (top)
4447                                 dy = -dy;
4448
4449                         if (args->id == SWM_ARG_ID_CENTER) {
4450                                 if (g.h / 2 + dy < 1)
4451                                         dy = 1 - g.h / 2;
4452
4453                                 Y(win) = g.y - dy;
4454                                 HEIGHT(win) = g.h + 2 * dy;
4455                         } else {
4456                                 if (g.h + dy < 1)
4457                                         dy = 1 - g.h;
4458
4459                                 if (top)
4460                                         Y(win) = g.y - dy;
4461
4462                                 HEIGHT(win) = g.h + dy;
4463                         }
4464
4465                         /* horizontal */
4466                         if (left)
4467                                 dx = -dx;
4468
4469                         if (args->id == SWM_ARG_ID_CENTER) {
4470                                 if (g.w / 2 + dx < 1)
4471                                         dx = 1 - g.w / 2;
4472
4473                                 X(win) = g.x - dx;
4474                                 WIDTH(win) = g.w + 2 * dx;
4475                         } else {
4476                                 if (g.w + dx < 1)
4477                                         dx = 1 - g.w;
4478
4479                                 if (left)
4480                                         X(win) = g.x - dx;
4481
4482                                 WIDTH(win) = g.w + dx;
4483                         }
4484
4485                         constrain_window(win, r, 1);
4486
4487                         /* not free, don't sync more than 120 times / second */
4488                         if ((ev.xmotion.time - time) > (1000 / 120) ) {
4489                                 time = ev.xmotion.time;
4490                                 do_sync();
4491                                 update_window(win);
4492                         }
4493                         break;
4494                 }
4495         } while (ev.type != ButtonRelease);
4496         if (time) {
4497                 do_sync();
4498                 update_window(win);
4499         }
4500         store_float_geom(win,r);
4501
4502         xcb_ungrab_pointer(conn, XCB_CURRENT_TIME);
4503         xcb_free_cursor(conn, cursor);
4504         xcb_close_font(conn, cursor_font);
4505         free(gpr);
4506         free(xpr);
4507
4508         /* drain events */
4509         drain_enter_notify();
4510 }
4511
4512 void
4513 resize_step(struct swm_region *r, union arg *args)
4514 {
4515         struct ws_win           *win = NULL;
4516
4517         if (r && r->ws && r->ws->focus)
4518                 win = r->ws->focus;
4519         else
4520                 return;
4521
4522         resize(win, args);
4523 }
4524
4525 #define SWM_MOVE_STEPS  (50)
4526
4527 void
4528 move(struct ws_win *win, union arg *args)
4529 {
4530         XEvent                  ev;
4531         Time                    time = 0;
4532         int                     move_step = 0;
4533         struct swm_region       *r = NULL;
4534         xcb_font_t                      cursor_font;
4535         xcb_cursor_t                    cursor;
4536         xcb_grab_pointer_reply_t        *gpr;
4537         xcb_query_pointer_reply_t       *qpr;
4538
4539         if (win == NULL)
4540                 return;
4541         r = win->ws->r;
4542
4543         if (win->ewmh_flags & EWMH_F_FULLSCREEN)
4544                 return;
4545
4546         DNPRINTF(SWM_D_MOUSE, "move: window: 0x%x, floating: %s, transient: "
4547             "0x%x\n", win->id, YESNO(win->floating), win->transient);
4548
4549         /* in max_stack mode should only move transients */
4550         if (win->ws->cur_layout == &layouts[SWM_MAX_STACK] && !win->transient)
4551                 return;
4552
4553         win->manual = 1;
4554         if (win->floating == 0 && !win->transient) {
4555                 store_float_geom(win, r);
4556                 ewmh_update_win_state(win, ewmh[_NET_WM_STATE_ABOVE].atom,
4557                     _NET_WM_STATE_ADD);
4558         }
4559         ewmh_update_win_state(win, ewmh[_SWM_WM_STATE_MANUAL].atom,
4560             _NET_WM_STATE_ADD);
4561
4562         stack();
4563
4564         move_step = 0;
4565         switch (args->id) {
4566         case SWM_ARG_ID_MOVELEFT:
4567                 X(win) -= (SWM_MOVE_STEPS - border_width);
4568                 move_step = 1;
4569                 break;
4570         case SWM_ARG_ID_MOVERIGHT:
4571                 X(win) += (SWM_MOVE_STEPS - border_width);
4572                 move_step = 1;
4573                 break;
4574         case SWM_ARG_ID_MOVEUP:
4575                 Y(win) -= (SWM_MOVE_STEPS - border_width);
4576                 move_step = 1;
4577                 break;
4578         case SWM_ARG_ID_MOVEDOWN:
4579                 Y(win) += (SWM_MOVE_STEPS - border_width);
4580                 move_step = 1;
4581                 break;
4582         default:
4583                 break;
4584         }
4585         if (move_step) {
4586                 constrain_window(win, r, 0);
4587                 update_window(win);
4588                 store_float_geom(win, r);
4589                 return;
4590         }
4591
4592         cursor_font = xcb_generate_id(conn);
4593         xcb_open_font(conn, cursor_font, strlen("cursor"), "cursor");
4594         
4595         cursor = xcb_generate_id(conn);
4596         xcb_create_glyph_cursor(conn, cursor, cursor_font, cursor_font,
4597                 XC_fleur, XC_fleur + 1, 0, 0, 0, 0xffff, 0xffff, 0xffff);
4598
4599         gpr = xcb_grab_pointer_reply(conn,
4600                 xcb_grab_pointer(conn, False, win->id, MOUSEMASK,
4601                         XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC,
4602                         XCB_WINDOW_NONE, cursor, XCB_CURRENT_TIME),
4603                 NULL);
4604         if (!gpr) {
4605                 xcb_free_cursor(conn, cursor);
4606                 xcb_close_font(conn, cursor_font);
4607                 return;
4608         }
4609
4610         /* get cursor offset from window root */
4611         qpr = xcb_query_pointer_reply(conn, xcb_query_pointer(conn, win->id),
4612                 NULL);
4613         if (!qpr) {
4614                 xcb_ungrab_pointer(conn, XCB_CURRENT_TIME);
4615                 xcb_free_cursor(conn, cursor);
4616                 xcb_close_font(conn, cursor_font);
4617                 return;
4618         }
4619         do {
4620                 XMaskEvent(display, MOUSEMASK | ExposureMask |
4621                     SubstructureRedirectMask, &ev);
4622                 switch (ev.type) {
4623                 case ConfigureRequest:
4624                 case Expose:
4625                 case MapRequest:
4626                         handler[ev.type](&ev);
4627                         break;
4628                 case MotionNotify:
4629                         X(win) = ev.xmotion.x_root - qpr->win_x - border_width;
4630                         Y(win) = ev.xmotion.y_root - qpr->win_y - border_width;
4631
4632                         constrain_window(win, r, 0);
4633
4634                         /* not free, don't sync more than 120 times / second */
4635                         if ((ev.xmotion.time - time) > (1000 / 120) ) {
4636                                 time = ev.xmotion.time;
4637                                 do_sync();
4638                                 update_window(win);
4639                         }
4640                         break;
4641                 }
4642         } while (ev.type != ButtonRelease);
4643         if (time) {
4644                 do_sync();
4645                 update_window(win);
4646         }
4647         store_float_geom(win, r);
4648         free(qpr);
4649         xcb_free_cursor(conn, cursor);
4650         xcb_close_font(conn, cursor_font);
4651         xcb_ungrab_pointer(conn, XCB_CURRENT_TIME);
4652
4653         /* drain events */
4654         drain_enter_notify();
4655 }
4656
4657 void
4658 move_step(struct swm_region *r, union arg *args)
4659 {
4660         struct ws_win           *win = NULL;
4661
4662         if (r && r->ws && r->ws->focus)
4663                 win = r->ws->focus;
4664         else
4665                 return;
4666
4667         if (!(win->transient != 0 || win->floating != 0))
4668                 return;
4669
4670         move(win, args);
4671 }
4672
4673
4674 /* user/key callable function IDs */
4675 enum keyfuncid {
4676         kf_cycle_layout,
4677         kf_flip_layout,
4678         kf_stack_reset,
4679         kf_master_shrink,
4680         kf_master_grow,
4681         kf_master_add,
4682         kf_master_del,
4683         kf_stack_inc,
4684         kf_stack_dec,
4685         kf_swap_main,
4686         kf_focus_next,
4687         kf_focus_prev,
4688         kf_swap_next,
4689         kf_swap_prev,
4690         kf_quit,
4691         kf_restart,
4692         kf_focus_main,
4693         kf_ws_1,
4694         kf_ws_2,
4695         kf_ws_3,
4696         kf_ws_4,
4697         kf_ws_5,
4698         kf_ws_6,
4699         kf_ws_7,
4700         kf_ws_8,
4701         kf_ws_9,
4702         kf_ws_10,
4703         kf_ws_11,
4704         kf_ws_12,
4705         kf_ws_13,
4706         kf_ws_14,
4707         kf_ws_15,
4708         kf_ws_16,
4709         kf_ws_17,
4710         kf_ws_18,
4711         kf_ws_19,
4712         kf_ws_20,
4713         kf_ws_21,
4714         kf_ws_22,
4715         kf_ws_next,
4716         kf_ws_prev,
4717         kf_ws_next_all,
4718         kf_ws_prev_all,
4719         kf_ws_prior,
4720         kf_screen_next,
4721         kf_screen_prev,
4722         kf_mvws_1,
4723         kf_mvws_2,
4724         kf_mvws_3,
4725         kf_mvws_4,
4726         kf_mvws_5,
4727         kf_mvws_6,
4728         kf_mvws_7,
4729         kf_mvws_8,
4730         kf_mvws_9,
4731         kf_mvws_10,
4732         kf_mvws_11,
4733         kf_mvws_12,
4734         kf_mvws_13,
4735         kf_mvws_14,
4736         kf_mvws_15,
4737         kf_mvws_16,
4738         kf_mvws_17,
4739         kf_mvws_18,
4740         kf_mvws_19,
4741         kf_mvws_20,
4742         kf_mvws_21,
4743         kf_mvws_22,
4744         kf_bar_toggle,
4745         kf_wind_kill,
4746         kf_wind_del,
4747         kf_float_toggle,
4748         kf_version,
4749         kf_spawn_custom,
4750         kf_iconify,
4751         kf_uniconify,
4752         kf_raise_toggle,
4753         kf_button2,
4754         kf_width_shrink,
4755         kf_width_grow,
4756         kf_height_shrink,
4757         kf_height_grow,
4758         kf_move_left,
4759         kf_move_right,
4760         kf_move_up,
4761         kf_move_down,
4762         kf_name_workspace,
4763         kf_search_workspace,
4764         kf_search_win,
4765         kf_dumpwins, /* MUST BE LAST */
4766         kf_invalid
4767 };
4768
4769 /* key definitions */
4770 void
4771 dummykeyfunc(struct swm_region *r, union arg *args)
4772 {
4773 };
4774
4775 struct keyfunc {
4776         char                    name[SWM_FUNCNAME_LEN];
4777         void                    (*func)(struct swm_region *r, union arg *);
4778         union arg               args;
4779 } keyfuncs[kf_invalid + 1] = {
4780         /* name                 function        argument */
4781         { "cycle_layout",       cycle_layout,   {0} },
4782         { "flip_layout",        stack_config,   {.id = SWM_ARG_ID_FLIPLAYOUT} },
4783         { "stack_reset",        stack_config,   {.id = SWM_ARG_ID_STACKRESET} },
4784         { "master_shrink",      stack_config,   {.id = SWM_ARG_ID_MASTERSHRINK} },
4785         { "master_grow",        stack_config,   {.id = SWM_ARG_ID_MASTERGROW} },
4786         { "master_add",         stack_config,   {.id = SWM_ARG_ID_MASTERADD} },
4787         { "master_del",         stack_config,   {.id = SWM_ARG_ID_MASTERDEL} },
4788         { "stack_inc",          stack_config,   {.id = SWM_ARG_ID_STACKINC} },
4789         { "stack_dec",          stack_config,   {.id = SWM_ARG_ID_STACKDEC} },
4790         { "swap_main",          swapwin,        {.id = SWM_ARG_ID_SWAPMAIN} },
4791         { "focus_next",         focus,          {.id = SWM_ARG_ID_FOCUSNEXT} },
4792         { "focus_prev",         focus,          {.id = SWM_ARG_ID_FOCUSPREV} },
4793         { "swap_next",          swapwin,        {.id = SWM_ARG_ID_SWAPNEXT} },
4794         { "swap_prev",          swapwin,        {.id = SWM_ARG_ID_SWAPPREV} },
4795         { "quit",               quit,           {0} },
4796         { "restart",            restart,        {0} },
4797         { "focus_main",         focus,          {.id = SWM_ARG_ID_FOCUSMAIN} },
4798         { "ws_1",               switchws,       {.id = 0} },
4799         { "ws_2",               switchws,       {.id = 1} },
4800         { "ws_3",               switchws,       {.id = 2} },
4801         { "ws_4",               switchws,       {.id = 3} },
4802         { "ws_5",               switchws,       {.id = 4} },
4803         { "ws_6",               switchws,       {.id = 5} },
4804         { "ws_7",               switchws,       {.id = 6} },
4805         { "ws_8",               switchws,       {.id = 7} },
4806         { "ws_9",               switchws,       {.id = 8} },
4807         { "ws_10",              switchws,       {.id = 9} },
4808         { "ws_11",              switchws,       {.id = 10} },
4809         { "ws_12",              switchws,       {.id = 11} },
4810         { "ws_13",              switchws,       {.id = 12} },
4811         { "ws_14",              switchws,       {.id = 13} },
4812         { "ws_15",              switchws,       {.id = 14} },
4813         { "ws_16",              switchws,       {.id = 15} },
4814         { "ws_17",              switchws,       {.id = 16} },
4815         { "ws_18",              switchws,       {.id = 17} },
4816         { "ws_19",              switchws,       {.id = 18} },
4817         { "ws_20",              switchws,       {.id = 19} },
4818         { "ws_21",              switchws,       {.id = 20} },
4819         { "ws_22",              switchws,       {.id = 21} },
4820         { "ws_next",            cyclews,        {.id = SWM_ARG_ID_CYCLEWS_UP} },
4821         { "ws_prev",            cyclews,        {.id = SWM_ARG_ID_CYCLEWS_DOWN} },
4822         { "ws_next_all",        cyclews,        {.id = SWM_ARG_ID_CYCLEWS_UP_ALL} },
4823         { "ws_prev_all",        cyclews,        {.id = SWM_ARG_ID_CYCLEWS_DOWN_ALL} },
4824         { "ws_prior",           priorws,        {0} },
4825         { "screen_next",        cyclescr,       {.id = SWM_ARG_ID_CYCLESC_UP} },
4826         { "screen_prev",        cyclescr,       {.id = SWM_ARG_ID_CYCLESC_DOWN} },
4827         { "mvws_1",             send_to_ws,     {.id = 0} },
4828         { "mvws_2",             send_to_ws,     {.id = 1} },
4829         { "mvws_3",             send_to_ws,     {.id = 2} },
4830         { "mvws_4",             send_to_ws,     {.id = 3} },
4831         { "mvws_5",             send_to_ws,     {.id = 4} },
4832         { "mvws_6",             send_to_ws,     {.id = 5} },
4833         { "mvws_7",             send_to_ws,     {.id = 6} },
4834         { "mvws_8",             send_to_ws,     {.id = 7} },
4835         { "mvws_9",             send_to_ws,     {.id = 8} },
4836         { "mvws_10",            send_to_ws,     {.id = 9} },
4837         { "mvws_11",            send_to_ws,     {.id = 10} },
4838         { "mvws_12",            send_to_ws,     {.id = 11} },
4839         { "mvws_13",            send_to_ws,     {.id = 12} },
4840         { "mvws_14",            send_to_ws,     {.id = 13} },
4841         { "mvws_15",            send_to_ws,     {.id = 14} },
4842         { "mvws_16",            send_to_ws,     {.id = 15} },
4843         { "mvws_17",            send_to_ws,     {.id = 16} },
4844         { "mvws_18",            send_to_ws,     {.id = 17} },
4845         { "mvws_19",            send_to_ws,     {.id = 18} },
4846         { "mvws_20",            send_to_ws,     {.id = 19} },
4847         { "mvws_21",            send_to_ws,     {.id = 20} },
4848         { "mvws_22",            send_to_ws,     {.id = 21} },
4849         { "bar_toggle",         bar_toggle,     {0} },
4850         { "wind_kill",          wkill,          {.id = SWM_ARG_ID_KILLWINDOW} },
4851         { "wind_del",           wkill,          {.id = SWM_ARG_ID_DELETEWINDOW} },
4852         { "float_toggle",       floating_toggle,{0} },
4853         { "version",            version,        {0} },
4854         { "spawn_custom",       dummykeyfunc,   {0} },
4855         { "iconify",            iconify,        {0} },
4856         { "uniconify",          uniconify,      {0} },
4857         { "raise_toggle",       raise_toggle,   {0} },
4858         { "button2",            pressbutton,    {2} },
4859         { "width_shrink",       resize_step,    {.id = SWM_ARG_ID_WIDTHSHRINK} },
4860         { "width_grow",         resize_step,    {.id = SWM_ARG_ID_WIDTHGROW} },
4861         { "height_shrink",      resize_step,    {.id = SWM_ARG_ID_HEIGHTSHRINK} },
4862         { "height_grow",        resize_step,    {.id = SWM_ARG_ID_HEIGHTGROW} },
4863         { "move_left",          move_step,      {.id = SWM_ARG_ID_MOVELEFT} },
4864         { "move_right",         move_step,      {.id = SWM_ARG_ID_MOVERIGHT} },
4865         { "move_up",            move_step,      {.id = SWM_ARG_ID_MOVEUP} },
4866         { "move_down",          move_step,      {.id = SWM_ARG_ID_MOVEDOWN} },
4867         { "name_workspace",     name_workspace, {0} },
4868         { "search_workspace",   search_workspace,       {0} },
4869         { "search_win",         search_win,     {0} },
4870         { "dumpwins",           dumpwins,       {0} }, /* MUST BE LAST */
4871         { "invalid key func",   NULL,           {0} },
4872 };
4873 struct key {
4874         RB_ENTRY(key)           entry;
4875         unsigned int            mod;
4876         KeySym                  keysym;
4877         enum keyfuncid          funcid;
4878         char                    *spawn_name;
4879 };
4880 RB_HEAD(key_tree, key);
4881
4882 int
4883 key_cmp(struct key *kp1, struct key *kp2)
4884 {
4885         if (kp1->keysym < kp2->keysym)
4886                 return (-1);
4887         if (kp1->keysym > kp2->keysym)
4888                 return (1);
4889
4890         if (kp1->mod < kp2->mod)
4891                 return (-1);
4892         if (kp1->mod > kp2->mod)
4893                 return (1);
4894
4895         return (0);
4896 }
4897
4898 RB_GENERATE(key_tree, key, entry, key_cmp);
4899 struct key_tree                 keys;
4900
4901 /* mouse */
4902 enum { client_click, root_click };
4903 struct button {
4904         unsigned int            action;
4905         unsigned int            mask;
4906         unsigned int            button;
4907         void                    (*func)(struct ws_win *, union arg *);
4908         union arg               args;
4909 } buttons[] = {
4910           /* action     key             mouse button    func    args */
4911         { client_click, MODKEY,         Button3,        resize, {.id = SWM_ARG_ID_DONTCENTER} },
4912         { client_click, MODKEY | ShiftMask, Button3,    resize, {.id = SWM_ARG_ID_CENTER} },
4913         { client_click, MODKEY,         Button1,        move,   {0} },
4914 };
4915
4916 void
4917 update_modkey(unsigned int mod)
4918 {
4919         int                     i;
4920         struct key              *kp;
4921
4922         mod_key = mod;
4923         RB_FOREACH(kp, key_tree, &keys)
4924                 if (kp->mod & ShiftMask)
4925                         kp->mod = mod | ShiftMask;
4926                 else
4927                         kp->mod = mod;
4928
4929         for (i = 0; i < LENGTH(buttons); i++)
4930                 if (buttons[i].mask & ShiftMask)
4931                         buttons[i].mask = mod | ShiftMask;
4932                 else
4933                         buttons[i].mask = mod;
4934 }
4935
4936 /* spawn */
4937 struct spawn_prog {
4938         TAILQ_ENTRY(spawn_prog) entry;
4939         char                    *name;
4940         int                     argc;
4941         char                    **argv;
4942 };
4943 TAILQ_HEAD(spawn_list, spawn_prog);
4944 struct spawn_list               spawns = TAILQ_HEAD_INITIALIZER(spawns);
4945
4946 int
4947 spawn_expand(struct swm_region *r, union arg *args, char *spawn_name,
4948     char ***ret_args)
4949 {
4950         struct spawn_prog       *prog = NULL;
4951         int                     i;
4952         char                    *ap, **real_args;
4953
4954         DNPRINTF(SWM_D_SPAWN, "spawn_expand: %s\n", spawn_name);
4955
4956         /* find program */
4957         TAILQ_FOREACH(prog, &spawns, entry) {
4958                 if (!strcasecmp(spawn_name, prog->name))
4959                         break;
4960         }
4961         if (prog == NULL) {
4962                 warnx("spawn_custom: program %s not found", spawn_name);
4963                 return (-1);
4964         }
4965
4966         /* make room for expanded args */
4967         if ((real_args = calloc(prog->argc + 1, sizeof(char *))) == NULL)
4968                 err(1, "spawn_custom: calloc real_args");
4969
4970         /* expand spawn_args into real_args */
4971         for (i = 0; i < prog->argc; i++) {
4972                 ap = prog->argv[i];
4973                 DNPRINTF(SWM_D_SPAWN, "spawn_custom: raw arg: %s\n", ap);
4974                 if (!strcasecmp(ap, "$bar_border")) {
4975                         if ((real_args[i] =
4976                             strdup(r->s->c[SWM_S_COLOR_BAR_BORDER].name))
4977                             == NULL)
4978                                 err(1,  "spawn_custom border color");
4979                 } else if (!strcasecmp(ap, "$bar_color")) {
4980                         if ((real_args[i] =
4981                             strdup(r->s->c[SWM_S_COLOR_BAR].name))
4982                             == NULL)
4983                                 err(1, "spawn_custom bar color");
4984                 } else if (!strcasecmp(ap, "$bar_font")) {
4985                         if ((real_args[i] = strdup(bar_fonts))
4986                             == NULL)
4987                                 err(1, "spawn_custom bar fonts");
4988                 } else if (!strcasecmp(ap, "$bar_font_color")) {
4989                         if ((real_args[i] =
4990                             strdup(r->s->c[SWM_S_COLOR_BAR_FONT].name))
4991                             == NULL)
4992                                 err(1, "spawn_custom color font");
4993                 } else if (!strcasecmp(ap, "$color_focus")) {
4994                         if ((real_args[i] =
4995                             strdup(r->s->c[SWM_S_COLOR_FOCUS].name))
4996                             == NULL)
4997                                 err(1, "spawn_custom color focus");
4998                 } else if (!strcasecmp(ap, "$color_unfocus")) {
4999                         if ((real_args[i] =
5000                             strdup(r->s->c[SWM_S_COLOR_UNFOCUS].name))
5001                             == NULL)
5002                                 err(1, "spawn_custom color unfocus");
5003                 } else {
5004                         /* no match --> copy as is */
5005                         if ((real_args[i] = strdup(ap)) == NULL)
5006                                 err(1, "spawn_custom strdup(ap)");
5007                 }
5008                 DNPRINTF(SWM_D_SPAWN, "spawn_custom: cooked arg: %s\n",
5009                     real_args[i]);
5010         }
5011
5012 #ifdef SWM_DEBUG
5013         DNPRINTF(SWM_D_SPAWN, "spawn_custom: result: ");
5014         for (i = 0; i < prog->argc; i++)
5015                 DNPRINTF(SWM_D_SPAWN, "\"%s\" ", real_args[i]);
5016         DNPRINTF(SWM_D_SPAWN, "\n");
5017 #endif
5018         *ret_args = real_args;
5019         return (prog->argc);
5020 }
5021
5022 void
5023 spawn_custom(struct swm_region *r, union arg *args, char *spawn_name)
5024 {
5025         union arg               a;
5026         char                    **real_args;
5027         int                     spawn_argc, i;
5028
5029         if ((spawn_argc = spawn_expand(r, args, spawn_name, &real_args)) < 0)
5030                 return;
5031         a.argv = real_args;
5032         if (fork() == 0)
5033                 spawn(r->ws->idx, &a, 1);
5034
5035         for (i = 0; i < spawn_argc; i++)
5036                 free(real_args[i]);
5037         free(real_args);
5038 }
5039
5040 void
5041 spawn_select(struct swm_region *r, union arg *args, char *spawn_name, int *pid)
5042 {
5043         union arg               a;
5044         char                    **real_args;
5045         int                     i, spawn_argc;
5046
5047         if ((spawn_argc = spawn_expand(r, args, spawn_name, &real_args)) < 0)
5048                 return;
5049         a.argv = real_args;
5050
5051         if (pipe(select_list_pipe) == -1)
5052                 err(1, "pipe error");
5053         if (pipe(select_resp_pipe) == -1)
5054                 err(1, "pipe error");
5055
5056         if (signal(SIGPIPE, SIG_IGN) == SIG_ERR)
5057                 err(1, "could not disable SIGPIPE");
5058         switch (*pid = fork()) {
5059         case -1:
5060                 err(1, "cannot fork");
5061                 break;
5062         case 0: /* child */
5063                 if (dup2(select_list_pipe[0], 0) == -1)
5064                         err(1, "dup2");
5065                 if (dup2(select_resp_pipe[1], 1) == -1)
5066                         err(1, "dup2");
5067                 close(select_list_pipe[1]);
5068                 close(select_resp_pipe[0]);
5069                 spawn(r->ws->idx, &a, 0);
5070                 break;
5071         default: /* parent */
5072                 close(select_list_pipe[0]);
5073                 close(select_resp_pipe[1]);
5074                 break;
5075         }
5076
5077         for (i = 0; i < spawn_argc; i++)
5078                 free(real_args[i]);
5079         free(real_args);
5080 }
5081
5082 void
5083 spawn_insert(char *name, char *args)
5084 {
5085         char                    *arg, *cp, *ptr;
5086         struct spawn_prog       *sp;
5087
5088         DNPRINTF(SWM_D_SPAWN, "spawn_insert: %s\n", name);
5089
5090         if ((sp = calloc(1, sizeof *sp)) == NULL)
5091                 err(1, "spawn_insert: malloc");
5092         if ((sp->name = strdup(name)) == NULL)
5093                 err(1, "spawn_insert: strdup");
5094
5095         /* convert the arguments to an argument list */
5096         if ((ptr = cp = strdup(args)) == NULL)
5097                 err(1, "spawn_insert: strdup");
5098         while ((arg = strsep(&ptr, " \t")) != NULL) {
5099                 /* empty field; skip it */
5100                 if (*arg == '\0')
5101                         continue;
5102
5103                 sp->argc++;
5104                 if ((sp->argv = realloc(sp->argv, sp->argc *
5105                     sizeof *sp->argv)) == NULL)
5106                         err(1, "spawn_insert: realloc");
5107                 if ((sp->argv[sp->argc - 1] = strdup(arg)) == NULL)
5108                         err(1, "spawn_insert: strdup");
5109         }
5110         free(cp);
5111
5112         TAILQ_INSERT_TAIL(&spawns, sp, entry);
5113         DNPRINTF(SWM_D_SPAWN, "spawn_insert: leave\n");
5114 }
5115
5116 void
5117 spawn_remove(struct spawn_prog *sp)
5118 {
5119         int                     i;
5120
5121         DNPRINTF(SWM_D_SPAWN, "spawn_remove: %s\n", sp->name);
5122
5123         TAILQ_REMOVE(&spawns, sp, entry);
5124         for (i = 0; i < sp->argc; i++)
5125                 free(sp->argv[i]);
5126         free(sp->argv);
5127         free(sp->name);
5128         free(sp);
5129
5130         DNPRINTF(SWM_D_SPAWN, "spawn_remove: leave\n");
5131 }
5132
5133 void
5134 spawn_replace(struct spawn_prog *sp, char *name, char *args)
5135 {
5136         DNPRINTF(SWM_D_SPAWN, "spawn_replace: %s [%s]\n", sp->name, name);
5137
5138         spawn_remove(sp);
5139         spawn_insert(name, args);
5140
5141         DNPRINTF(SWM_D_SPAWN, "spawn_replace: leave\n");
5142 }
5143
5144 void
5145 setspawn(char *name, char *args)
5146 {
5147         struct spawn_prog       *sp;
5148
5149         DNPRINTF(SWM_D_SPAWN, "setspawn: %s\n", name);
5150
5151         if (name == NULL)
5152                 return;
5153
5154         TAILQ_FOREACH(sp, &spawns, entry) {
5155                 if (!strcmp(sp->name, name)) {
5156                         if (*args == '\0')
5157                                 spawn_remove(sp);
5158                         else
5159                                 spawn_replace(sp, name, args);
5160                         DNPRINTF(SWM_D_SPAWN, "setspawn: leave\n");
5161                         return;
5162                 }
5163         }
5164         if (*args == '\0') {
5165                 warnx("error: setspawn: cannot find program: %s", name);
5166                 return;
5167         }
5168
5169         spawn_insert(name, args);
5170         DNPRINTF(SWM_D_SPAWN, "setspawn: leave\n");
5171 }
5172
5173 int
5174 setconfspawn(char *selector, char *value, int flags)
5175 {
5176         DNPRINTF(SWM_D_SPAWN, "setconfspawn: [%s] [%s]\n", selector, value);
5177
5178         setspawn(selector, value);
5179
5180         DNPRINTF(SWM_D_SPAWN, "setconfspawn: done\n");
5181         return (0);
5182 }
5183
5184 void
5185 setup_spawn(void)
5186 {
5187         setconfspawn("term",            "xterm",                0);
5188         setconfspawn("spawn_term",      "xterm",                0);
5189         setconfspawn("screenshot_all",  "screenshot.sh full",   0);
5190         setconfspawn("screenshot_wind", "screenshot.sh window", 0);
5191         setconfspawn("lock",            "xlock",                0);
5192         setconfspawn("initscr",         "initscreen.sh",        0);
5193         setconfspawn("menu",            "dmenu_run"
5194                                         " -fn $bar_font"
5195                                         " -nb $bar_color"
5196                                         " -nf $bar_font_color"
5197                                         " -sb $bar_border"
5198                                         " -sf $bar_color",      0);
5199         setconfspawn("search",          "dmenu"
5200                                         " -i"
5201                                         " -fn $bar_font"
5202                                         " -nb $bar_color"
5203                                         " -nf $bar_font_color"
5204                                         " -sb $bar_border"
5205                                         " -sf $bar_color",      0);
5206         setconfspawn("name_workspace",  "dmenu"
5207                                         " -p Workspace"
5208                                         " -fn $bar_font"
5209                                         " -nb $bar_color"
5210                                         " -nf $bar_font_color"
5211                                         " -sb $bar_border"
5212                                         " -sf $bar_color",      0);
5213 }
5214
5215 /* key bindings */
5216 #define SWM_MODNAME_SIZE        32
5217 #define SWM_KEY_WS              "\n+ \t"
5218 int
5219 parsekeys(char *keystr, unsigned int currmod, unsigned int *mod, KeySym *ks)
5220 {
5221         char                    *cp, *name;
5222         KeySym                  uks;
5223         DNPRINTF(SWM_D_KEY, "parsekeys: enter [%s]\n", keystr);
5224         if (mod == NULL || ks == NULL) {
5225                 DNPRINTF(SWM_D_KEY, "parsekeys: no mod or key vars\n");
5226                 return (1);
5227         }
5228         if (keystr == NULL || strlen(keystr) == 0) {
5229                 DNPRINTF(SWM_D_KEY, "parsekeys: no keystr\n");
5230                 return (1);
5231         }
5232         cp = keystr;
5233         *ks = NoSymbol;
5234         *mod = 0;
5235         while ((name = strsep(&cp, SWM_KEY_WS)) != NULL) {
5236                 DNPRINTF(SWM_D_KEY, "parsekeys: key [%s]\n", name);
5237                 if (cp)
5238                         cp += (long)strspn(cp, SWM_KEY_WS);
5239                 if (strncasecmp(name, "MOD", SWM_MODNAME_SIZE) == 0)
5240                         *mod |= currmod;
5241                 else if (!strncasecmp(name, "Mod1", SWM_MODNAME_SIZE))
5242                         *mod |= Mod1Mask;
5243                 else if (!strncasecmp(name, "Mod2", SWM_MODNAME_SIZE))
5244                         *mod += Mod2Mask;
5245                 else if (!strncmp(name, "Mod3", SWM_MODNAME_SIZE))
5246                         *mod |= Mod3Mask;
5247                 else if (!strncmp(name, "Mod4", SWM_MODNAME_SIZE))
5248                         *mod |= Mod4Mask;
5249                 else if (strncasecmp(name, "SHIFT", SWM_MODNAME_SIZE) == 0)
5250                         *mod |= ShiftMask;
5251                 else if (strncasecmp(name, "CONTROL", SWM_MODNAME_SIZE) == 0)
5252                         *mod |= ControlMask;
5253                 else {
5254                         *ks = XStringToKeysym(name);
5255                         XConvertCase(*ks, ks, &uks);
5256                         if (ks == NoSymbol) {
5257                                 DNPRINTF(SWM_D_KEY,
5258                                     "parsekeys: invalid key %s\n",
5259                                     name);
5260                                 return (1);
5261                         }
5262                 }
5263         }
5264         DNPRINTF(SWM_D_KEY, "parsekeys: leave ok\n");
5265         return (0);
5266 }
5267
5268 char *
5269 strdupsafe(char *str)
5270 {
5271         if (str == NULL)
5272                 return (NULL);
5273         else
5274                 return (strdup(str));
5275 }
5276
5277 void
5278 key_insert(unsigned int mod, KeySym ks, enum keyfuncid kfid, char *spawn_name)
5279 {
5280         struct key              *kp;
5281
5282         DNPRINTF(SWM_D_KEY, "key_insert: enter %s [%s]\n",
5283             keyfuncs[kfid].name, spawn_name);
5284
5285         if ((kp = malloc(sizeof *kp)) == NULL)
5286                 err(1, "key_insert: malloc");
5287
5288         kp->mod = mod;
5289         kp->keysym = ks;
5290         kp->funcid = kfid;
5291         kp->spawn_name = strdupsafe(spawn_name);
5292         RB_INSERT(key_tree, &keys, kp);
5293
5294         DNPRINTF(SWM_D_KEY, "key_insert: leave\n");
5295 }
5296
5297 struct key *
5298 key_lookup(unsigned int mod, KeySym ks)
5299 {
5300         struct key              kp;
5301
5302         kp.keysym = ks;
5303         kp.mod = mod;
5304
5305         return (RB_FIND(key_tree, &keys, &kp));
5306 }
5307
5308 void
5309 key_remove(struct key *kp)
5310 {
5311         DNPRINTF(SWM_D_KEY, "key_remove: %s\n", keyfuncs[kp->funcid].name);
5312
5313         RB_REMOVE(key_tree, &keys, kp);
5314         free(kp->spawn_name);
5315         free(kp);
5316
5317         DNPRINTF(SWM_D_KEY, "key_remove: leave\n");
5318 }
5319
5320 void
5321 key_replace(struct key *kp, unsigned int mod, KeySym ks, enum keyfuncid kfid,
5322     char *spawn_name)
5323 {
5324         DNPRINTF(SWM_D_KEY, "key_replace: %s [%s]\n", keyfuncs[kp->funcid].name,
5325             spawn_name);
5326
5327         key_remove(kp);
5328         key_insert(mod, ks, kfid, spawn_name);
5329
5330         DNPRINTF(SWM_D_KEY, "key_replace: leave\n");
5331 }
5332
5333 void
5334 setkeybinding(unsigned int mod, KeySym ks, enum keyfuncid kfid,
5335     char *spawn_name)
5336 {
5337         struct key              *kp;
5338
5339         DNPRINTF(SWM_D_KEY, "setkeybinding: enter %s [%s]\n",
5340             keyfuncs[kfid].name, spawn_name);
5341
5342         if ((kp = key_lookup(mod, ks)) != NULL) {
5343                 if (kfid == kf_invalid)
5344                         key_remove(kp);
5345                 else
5346                         key_replace(kp, mod, ks, kfid, spawn_name);
5347                 DNPRINTF(SWM_D_KEY, "setkeybinding: leave\n");
5348                 return;
5349         }
5350         if (kfid == kf_invalid) {
5351                 warnx("error: setkeybinding: cannot find mod/key combination");
5352                 DNPRINTF(SWM_D_KEY, "setkeybinding: leave\n");
5353                 return;
5354         }
5355
5356         key_insert(mod, ks, kfid, spawn_name);
5357         DNPRINTF(SWM_D_KEY, "setkeybinding: leave\n");
5358 }
5359
5360 int
5361 setconfbinding(char *selector, char *value, int flags)
5362 {
5363         enum keyfuncid          kfid;
5364         unsigned int            mod;
5365         KeySym                  ks;
5366         struct spawn_prog       *sp;
5367         DNPRINTF(SWM_D_KEY, "setconfbinding: enter\n");
5368         if (selector == NULL) {
5369                 DNPRINTF(SWM_D_KEY, "setconfbinding: unbind %s\n", value);
5370                 if (parsekeys(value, mod_key, &mod, &ks) == 0) {
5371                         kfid = kf_invalid;
5372                         setkeybinding(mod, ks, kfid, NULL);
5373                         return (0);
5374                 } else
5375                         return (1);
5376         }
5377         /* search by key function name */
5378         for (kfid = 0; kfid < kf_invalid; (kfid)++) {
5379                 if (strncasecmp(selector, keyfuncs[kfid].name,
5380                     SWM_FUNCNAME_LEN) == 0) {
5381                         DNPRINTF(SWM_D_KEY, "setconfbinding: %s: match\n",
5382                             selector);
5383                         if (parsekeys(value, mod_key, &mod, &ks) == 0) {
5384                                 setkeybinding(mod, ks, kfid, NULL);
5385                                 return (0);
5386                         } else
5387                                 return (1);
5388                 }
5389         }
5390         /* search by custom spawn name */
5391         TAILQ_FOREACH(sp, &spawns, entry) {
5392                 if (strcasecmp(selector, sp->name) == 0) {
5393                         DNPRINTF(SWM_D_KEY, "setconfbinding: %s: match\n",
5394                             selector);
5395                         if (parsekeys(value, mod_key, &mod, &ks) == 0) {
5396                                 setkeybinding(mod, ks, kf_spawn_custom,
5397                                     sp->name);
5398                                 return (0);
5399                         } else
5400                                 return (1);
5401                 }
5402         }
5403         DNPRINTF(SWM_D_KEY, "setconfbinding: no match\n");
5404         return (1);
5405 }
5406
5407 void
5408 setup_keys(void)
5409 {
5410         setkeybinding(MODKEY,           XK_space,       kf_cycle_layout,NULL);
5411         setkeybinding(MODKEY|ShiftMask, XK_backslash,   kf_flip_layout, NULL);
5412         setkeybinding(MODKEY|ShiftMask, XK_space,       kf_stack_reset, NULL);
5413         setkeybinding(MODKEY,           XK_h,           kf_master_shrink,NULL);
5414         setkeybinding(MODKEY,           XK_l,           kf_master_grow, NULL);
5415         setkeybinding(MODKEY,           XK_comma,       kf_master_add,  NULL);
5416         setkeybinding(MODKEY,           XK_period,      kf_master_del,  NULL);
5417         setkeybinding(MODKEY|ShiftMask, XK_comma,       kf_stack_inc,   NULL);
5418         setkeybinding(MODKEY|ShiftMask, XK_period,      kf_stack_dec,   NULL);
5419         setkeybinding(MODKEY,           XK_Return,      kf_swap_main,   NULL);
5420         setkeybinding(MODKEY,           XK_j,           kf_focus_next,  NULL);
5421         setkeybinding(MODKEY,           XK_k,           kf_focus_prev,  NULL);
5422         setkeybinding(MODKEY|ShiftMask, XK_j,           kf_swap_next,   NULL);
5423         setkeybinding(MODKEY|ShiftMask, XK_k,           kf_swap_prev,   NULL);
5424         setkeybinding(MODKEY|ShiftMask, XK_Return,      kf_spawn_custom,"term");
5425         setkeybinding(MODKEY,           XK_p,           kf_spawn_custom,"menu");
5426         setkeybinding(MODKEY|ShiftMask, XK_q,           kf_quit,        NULL);
5427         setkeybinding(MODKEY,           XK_q,           kf_restart,     NULL);
5428         setkeybinding(MODKEY,           XK_m,           kf_focus_main,  NULL);
5429         setkeybinding(MODKEY,           XK_1,           kf_ws_1,        NULL);
5430         setkeybinding(MODKEY,           XK_2,           kf_ws_2,        NULL);
5431         setkeybinding(MODKEY,           XK_3,           kf_ws_3,        NULL);
5432         setkeybinding(MODKEY,           XK_4,           kf_ws_4,        NULL);
5433         setkeybinding(MODKEY,           XK_5,           kf_ws_5,        NULL);
5434         setkeybinding(MODKEY,           XK_6,           kf_ws_6,        NULL);
5435         setkeybinding(MODKEY,           XK_7,           kf_ws_7,        NULL);
5436         setkeybinding(MODKEY,           XK_8,           kf_ws_8,        NULL);
5437         setkeybinding(MODKEY,           XK_9,           kf_ws_9,        NULL);
5438         setkeybinding(MODKEY,           XK_0,           kf_ws_10,       NULL);
5439         setkeybinding(MODKEY,           XK_F1,          kf_ws_11,       NULL);
5440         setkeybinding(MODKEY,           XK_F2,          kf_ws_12,       NULL);
5441         setkeybinding(MODKEY,           XK_F3,          kf_ws_13,       NULL);
5442         setkeybinding(MODKEY,           XK_F4,          kf_ws_14,       NULL);
5443         setkeybinding(MODKEY,           XK_F5,          kf_ws_15,       NULL);
5444         setkeybinding(MODKEY,           XK_F6,          kf_ws_16,       NULL);
5445         setkeybinding(MODKEY,           XK_F7,          kf_ws_17,       NULL);
5446         setkeybinding(MODKEY,           XK_F8,          kf_ws_18,       NULL);
5447         setkeybinding(MODKEY,           XK_F9,          kf_ws_19,       NULL);
5448         setkeybinding(MODKEY,           XK_F10,         kf_ws_20,       NULL);
5449         setkeybinding(MODKEY,           XK_F11,         kf_ws_21,       NULL);
5450         setkeybinding(MODKEY,           XK_F12,         kf_ws_22,       NULL);
5451         setkeybinding(MODKEY,           XK_Right,       kf_ws_next,     NULL);
5452         setkeybinding(MODKEY,           XK_Left,        kf_ws_prev,     NULL);
5453         setkeybinding(MODKEY,           XK_Up,          kf_ws_next_all, NULL);
5454         setkeybinding(MODKEY,           XK_Down,        kf_ws_prev_all, NULL);
5455         setkeybinding(MODKEY,           XK_a,           kf_ws_prior,    NULL);
5456         setkeybinding(MODKEY|ShiftMask, XK_Right,       kf_screen_next, NULL);
5457         setkeybinding(MODKEY|ShiftMask, XK_Left,        kf_screen_prev, NULL);
5458         setkeybinding(MODKEY|ShiftMask, XK_1,           kf_mvws_1,      NULL);
5459         setkeybinding(MODKEY|ShiftMask, XK_2,           kf_mvws_2,      NULL);
5460         setkeybinding(MODKEY|ShiftMask, XK_3,           kf_mvws_3,      NULL);
5461         setkeybinding(MODKEY|ShiftMask, XK_4,           kf_mvws_4,      NULL);
5462         setkeybinding(MODKEY|ShiftMask, XK_5,           kf_mvws_5,      NULL);
5463         setkeybinding(MODKEY|ShiftMask, XK_6,           kf_mvws_6,      NULL);
5464         setkeybinding(MODKEY|ShiftMask, XK_7,           kf_mvws_7,      NULL);
5465         setkeybinding(MODKEY|ShiftMask, XK_8,           kf_mvws_8,      NULL);
5466         setkeybinding(MODKEY|ShiftMask, XK_9,           kf_mvws_9,      NULL);
5467         setkeybinding(MODKEY|ShiftMask, XK_0,           kf_mvws_10,     NULL);
5468         setkeybinding(MODKEY|ShiftMask, XK_F1,          kf_mvws_11,     NULL);
5469         setkeybinding(MODKEY|ShiftMask, XK_F2,          kf_mvws_12,     NULL);
5470         setkeybinding(MODKEY|ShiftMask, XK_F3,          kf_mvws_13,     NULL);
5471         setkeybinding(MODKEY|ShiftMask, XK_F4,          kf_mvws_14,     NULL);
5472         setkeybinding(MODKEY|ShiftMask, XK_F5,          kf_mvws_15,     NULL);
5473         setkeybinding(MODKEY|ShiftMask, XK_F6,          kf_mvws_16,     NULL);
5474         setkeybinding(MODKEY|ShiftMask, XK_F7,          kf_mvws_17,     NULL);
5475         setkeybinding(MODKEY|ShiftMask, XK_F8,          kf_mvws_18,     NULL);
5476         setkeybinding(MODKEY|ShiftMask, XK_F9,          kf_mvws_19,     NULL);
5477         setkeybinding(MODKEY|ShiftMask, XK_F10,         kf_mvws_20,     NULL);
5478         setkeybinding(MODKEY|ShiftMask, XK_F11,         kf_mvws_21,     NULL);
5479         setkeybinding(MODKEY|ShiftMask, XK_F12,         kf_mvws_22,     NULL);
5480         setkeybinding(MODKEY,           XK_b,           kf_bar_toggle,  NULL);
5481         setkeybinding(MODKEY,           XK_Tab,         kf_focus_next,  NULL);
5482         setkeybinding(MODKEY|ShiftMask, XK_Tab,         kf_focus_prev,  NULL);
5483         setkeybinding(MODKEY|ShiftMask, XK_x,           kf_wind_kill,   NULL);
5484         setkeybinding(MODKEY,           XK_x,           kf_wind_del,    NULL);
5485         setkeybinding(MODKEY,           XK_s,           kf_spawn_custom,"screenshot_all");
5486         setkeybinding(MODKEY|ShiftMask, XK_s,           kf_spawn_custom,"screenshot_wind");
5487         setkeybinding(MODKEY,           XK_t,           kf_float_toggle,NULL);
5488         setkeybinding(MODKEY|ShiftMask, XK_v,           kf_version,     NULL);
5489         setkeybinding(MODKEY|ShiftMask, XK_Delete,      kf_spawn_custom,"lock");
5490         setkeybinding(MODKEY|ShiftMask, XK_i,           kf_spawn_custom,"initscr");
5491         setkeybinding(MODKEY,           XK_w,           kf_iconify,     NULL);
5492         setkeybinding(MODKEY|ShiftMask, XK_w,           kf_uniconify,   NULL);
5493         setkeybinding(MODKEY|ShiftMask, XK_r,           kf_raise_toggle,NULL);
5494         setkeybinding(MODKEY,           XK_v,           kf_button2,     NULL);
5495         setkeybinding(MODKEY,           XK_equal,       kf_width_grow,  NULL);
5496         setkeybinding(MODKEY,           XK_minus,       kf_width_shrink,NULL);
5497         setkeybinding(MODKEY|ShiftMask, XK_equal,       kf_height_grow, NULL);
5498         setkeybinding(MODKEY|ShiftMask, XK_minus,       kf_height_shrink,NULL);
5499         setkeybinding(MODKEY,           XK_bracketleft, kf_move_left,   NULL);
5500         setkeybinding(MODKEY,           XK_bracketright,kf_move_right,  NULL);
5501         setkeybinding(MODKEY|ShiftMask, XK_bracketleft, kf_move_up,     NULL);
5502         setkeybinding(MODKEY|ShiftMask, XK_bracketright,kf_move_down,   NULL);
5503         setkeybinding(MODKEY|ShiftMask, XK_slash,       kf_name_workspace,NULL);
5504         setkeybinding(MODKEY,           XK_slash,       kf_search_workspace,NULL);
5505         setkeybinding(MODKEY,           XK_f,           kf_search_win,  NULL);
5506 #ifdef SWM_DEBUG
5507         setkeybinding(MODKEY|ShiftMask, XK_d,           kf_dumpwins,    NULL);
5508 #endif
5509 }
5510
5511 void
5512 clear_keys(void)
5513 {
5514         struct key              *kp;
5515
5516         while (RB_EMPTY(&keys) == 0) {
5517                 kp = RB_ROOT(&keys);
5518                 key_remove(kp);
5519         }
5520 }
5521
5522 int
5523 setkeymapping(char *selector, char *value, int flags)
5524 {
5525         char                    keymapping_file[PATH_MAX];
5526         DNPRINTF(SWM_D_KEY, "setkeymapping: enter\n");
5527         if (value[0] == '~')
5528                 snprintf(keymapping_file, sizeof keymapping_file, "%s/%s",
5529                     pwd->pw_dir, &value[1]);
5530         else
5531                 strlcpy(keymapping_file, value, sizeof keymapping_file);
5532         clear_keys();
5533         /* load new key bindings; if it fails, revert to default bindings */
5534         if (conf_load(keymapping_file, SWM_CONF_KEYMAPPING)) {
5535                 clear_keys();
5536                 setup_keys();
5537         }
5538         DNPRINTF(SWM_D_KEY, "setkeymapping: leave\n");
5539         return (0);
5540 }
5541
5542 void
5543 updatenumlockmask(void)
5544 {
5545         unsigned int                            i, j;
5546         xcb_get_modifier_mapping_reply_t        *modmap_r;
5547         xcb_keycode_t                           *modmap, kc;
5548
5549         DNPRINTF(SWM_D_MISC, "updatenumlockmask\n");
5550         numlockmask = 0;
5551
5552         modmap_r = xcb_get_modifier_mapping_reply(conn,
5553                 xcb_get_modifier_mapping(conn),
5554                 NULL);
5555         if (modmap_r) {
5556                 modmap = xcb_get_modifier_mapping_keycodes(modmap_r);
5557                 for (i = 0; i < 8; i++) {
5558                         for (j = 0; j < modmap_r->keycodes_per_modifier; j++) {
5559                                 kc = modmap[i * modmap_r->keycodes_per_modifier
5560                                         + j];
5561
5562                                 if (kc == *((xcb_keycode_t *)xcb_key_symbols_get_keycode(syms,
5563                                                 XK_Num_Lock))) 
5564                                         numlockmask = (1 << i);
5565                         }
5566                 }
5567                 free(modmap_r);
5568         }
5569 }
5570
5571 void
5572 grabkeys(void)
5573 {
5574         int                     num_screens;
5575         unsigned int            j, k;
5576         KeyCode                 code;
5577         unsigned int            modifiers[] =
5578             { 0, LockMask, numlockmask, numlockmask | LockMask };
5579         struct key              *kp;
5580
5581         DNPRINTF(SWM_D_MISC, "grabkeys\n");
5582         updatenumlockmask();
5583
5584         num_screens = xcb_setup_roots_length(xcb_get_setup(conn));
5585         for (k = 0; k < num_screens; k++) {
5586                 if (TAILQ_EMPTY(&screens[k].rl))
5587                         continue;
5588                 xcb_ungrab_key(conn, XCB_GRAB_ANY, screens[k].root,
5589                         XCB_MOD_MASK_ANY);
5590                 RB_FOREACH(kp, key_tree, &keys) {
5591                         if ((code = XKeysymToKeycode(display, kp->keysym)))
5592                                 for (j = 0; j < LENGTH(modifiers); j++)
5593                                         XGrabKey(display, code,
5594                                             kp->mod | modifiers[j],
5595                                             screens[k].root, True,
5596                                             GrabModeAsync, GrabModeAsync);
5597                 }
5598         }
5599 }
5600
5601 void
5602 grabbuttons(struct ws_win *win, int focused)
5603 {
5604         unsigned int            i, j;
5605         unsigned int            modifiers[] =
5606             { 0, LockMask, numlockmask, numlockmask|LockMask };
5607
5608         updatenumlockmask();
5609         xcb_ungrab_button(conn, XCB_BUTTON_INDEX_ANY, win->id,
5610                 XCB_BUTTON_MASK_ANY);
5611         if (focused) {
5612                 for (i = 0; i < LENGTH(buttons); i++)
5613                         if (buttons[i].action == client_click)
5614                                 for (j = 0; j < LENGTH(modifiers); j++)
5615                                         xcb_grab_button(conn, False, win->id,
5616                                                 BUTTONMASK,
5617                                                 XCB_GRAB_MODE_ASYNC,
5618                                                 XCB_GRAB_MODE_SYNC,
5619                                                 XCB_WINDOW_NONE,
5620                                                 XCB_CURSOR_NONE,
5621                                                 buttons[i].button,
5622                                                 buttons[i].mask);
5623         } else
5624                 xcb_grab_button(conn, False, win->id, BUTTONMASK,
5625                         XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_SYNC,
5626                         XCB_WINDOW_NONE, XCB_CURSOR_NONE, XCB_BUTTON_INDEX_ANY,
5627                         XCB_BUTTON_MASK_ANY);
5628 }
5629
5630 const char *quirkname[] = {
5631         "NONE",         /* config string for "no value" */
5632         "FLOAT",
5633         "TRANSSZ",
5634         "ANYWHERE",
5635         "XTERM_FONTADJ",
5636         "FULLSCREEN",
5637         "FOCUSPREV",
5638 };
5639
5640 /* SWM_Q_WS: retain '|' for back compat for now (2009-08-11) */
5641 #define SWM_Q_WS                "\n|+ \t"
5642 int
5643 parsequirks(char *qstr, unsigned long *quirk)
5644 {
5645         char                    *cp, *name;
5646         int                     i;
5647
5648         if (quirk == NULL)
5649                 return (1);
5650
5651         cp = qstr;
5652         *quirk = 0;
5653         while ((name = strsep(&cp, SWM_Q_WS)) != NULL) {
5654                 if (cp)
5655                         cp += (long)strspn(cp, SWM_Q_WS);
5656                 for (i = 0; i < LENGTH(quirkname); i++) {
5657                         if (!strncasecmp(name, quirkname[i], SWM_QUIRK_LEN)) {
5658                                 DNPRINTF(SWM_D_QUIRK,
5659                                     "parsequirks: %s\n", name);
5660                                 if (i == 0) {
5661                                         *quirk = 0;
5662                                         return (0);
5663                                 }
5664                                 *quirk |= 1 << (i-1);
5665                                 break;
5666                         }
5667                 }
5668                 if (i >= LENGTH(quirkname)) {
5669                         DNPRINTF(SWM_D_QUIRK,
5670                             "parsequirks: invalid quirk [%s]\n", name);
5671                         return (1);
5672                 }
5673         }
5674         return (0);
5675 }
5676
5677 void
5678 quirk_insert(const char *class, const char *name, unsigned long quirk)
5679 {
5680         struct quirk            *qp;
5681
5682         DNPRINTF(SWM_D_QUIRK, "quirk_insert: %s:%s [%lu]\n", class, name,
5683             quirk);
5684
5685         if ((qp = malloc(sizeof *qp)) == NULL)
5686                 err(1, "quirk_insert: malloc");
5687         if ((qp->class = strdup(class)) == NULL)
5688                 err(1, "quirk_insert: strdup");
5689         if ((qp->name = strdup(name)) == NULL)
5690                 err(1, "quirk_insert: strdup");
5691
5692         qp->quirk = quirk;
5693         TAILQ_INSERT_TAIL(&quirks, qp, entry);
5694
5695         DNPRINTF(SWM_D_QUIRK, "quirk_insert: leave\n");
5696 }
5697
5698 void
5699 quirk_remove(struct quirk *qp)
5700 {
5701         DNPRINTF(SWM_D_QUIRK, "quirk_remove: %s:%s [%lu]\n", qp->class,
5702             qp->name, qp->quirk);
5703
5704         TAILQ_REMOVE(&quirks, qp, entry);
5705         free(qp->class);
5706         free(qp->name);
5707         free(qp);
5708
5709         DNPRINTF(SWM_D_QUIRK, "quirk_remove: leave\n");
5710 }
5711
5712 void
5713 quirk_replace(struct quirk *qp, const char *class, const char *name,
5714     unsigned long quirk)
5715 {
5716         DNPRINTF(SWM_D_QUIRK, "quirk_replace: %s:%s [%lu]\n", qp->class,
5717             qp->name, qp->quirk);
5718
5719         quirk_remove(qp);
5720         quirk_insert(class, name, quirk);
5721
5722         DNPRINTF(SWM_D_QUIRK, "quirk_replace: leave\n");
5723 }
5724
5725 void
5726 setquirk(const char *class, const char *name, unsigned long quirk)
5727 {
5728         struct quirk            *qp;
5729
5730         DNPRINTF(SWM_D_QUIRK, "setquirk: enter %s:%s [%lu]\n", class, name,
5731            quirk);
5732
5733         TAILQ_FOREACH(qp, &quirks, entry) {
5734                 if (!strcmp(qp->class, class) && !strcmp(qp->name, name)) {
5735                         if (!quirk)
5736                                 quirk_remove(qp);
5737                         else
5738                                 quirk_replace(qp, class, name, quirk);
5739                         DNPRINTF(SWM_D_QUIRK, "setquirk: leave\n");
5740                         return;
5741                 }
5742         }
5743         if (!quirk) {
5744                 warnx("error: setquirk: cannot find class/name combination");
5745                 return;
5746         }
5747
5748         quirk_insert(class, name, quirk);
5749         DNPRINTF(SWM_D_QUIRK, "setquirk: leave\n");
5750 }
5751
5752 int
5753 setconfquirk(char *selector, char *value, int flags)
5754 {
5755         char                    *cp, *class, *name;
5756         int                     retval;
5757         unsigned long           quirks;
5758         if (selector == NULL)
5759                 return (0);
5760         if ((cp = strchr(selector, ':')) == NULL)
5761                 return (0);
5762         *cp = '\0';
5763         class = selector;
5764         name = cp + 1;
5765         if ((retval = parsequirks(value, &quirks)) == 0)
5766                 setquirk(class, name, quirks);
5767         return (retval);
5768 }
5769
5770 void
5771 setup_quirks(void)
5772 {
5773         setquirk("MPlayer",             "xv",           SWM_Q_FLOAT | SWM_Q_FULLSCREEN | SWM_Q_FOCUSPREV);
5774         setquirk("OpenOffice.org 3.2",  "VCLSalFrame",  SWM_Q_FLOAT);
5775         setquirk("Firefox-bin",         "firefox-bin",  SWM_Q_TRANSSZ);
5776         setquirk("Firefox",             "Dialog",       SWM_Q_FLOAT);
5777         setquirk("Gimp",                "gimp",         SWM_Q_FLOAT | SWM_Q_ANYWHERE);
5778         setquirk("XTerm",               "xterm",        SWM_Q_XTERM_FONTADJ);
5779         setquirk("xine",                "Xine Window",  SWM_Q_FLOAT | SWM_Q_ANYWHERE);
5780         setquirk("Xitk",                "Xitk Combo",   SWM_Q_FLOAT | SWM_Q_ANYWHERE);
5781         setquirk("xine",                "xine Panel",   SWM_Q_FLOAT | SWM_Q_ANYWHERE);
5782         setquirk("Xitk",                "Xine Window",  SWM_Q_FLOAT | SWM_Q_ANYWHERE);
5783         setquirk("xine",                "xine Video Fullscreen Window", SWM_Q_FULLSCREEN | SWM_Q_FLOAT);
5784         setquirk("pcb",                 "pcb",          SWM_Q_FLOAT);
5785         setquirk("SDL_App",             "SDL_App",      SWM_Q_FLOAT | SWM_Q_FULLSCREEN);
5786 }
5787
5788 /* conf file stuff */
5789 #define SWM_CONF_FILE           "spectrwm.conf"
5790 #define SWM_CONF_FILE_OLD       "scrotwm.conf"
5791
5792 enum {
5793         SWM_S_BAR_ACTION,
5794         SWM_S_BAR_AT_BOTTOM,
5795         SWM_S_BAR_BORDER_WIDTH,
5796         SWM_S_BAR_DELAY,
5797         SWM_S_BAR_ENABLED,
5798         SWM_S_BAR_FONT,
5799         SWM_S_BAR_FORMAT,
5800         SWM_S_BAR_JUSTIFY,
5801         SWM_S_BORDER_WIDTH,
5802         SWM_S_CLOCK_ENABLED,
5803         SWM_S_CLOCK_FORMAT,
5804         SWM_S_CYCLE_EMPTY,
5805         SWM_S_CYCLE_VISIBLE,
5806         SWM_S_DIALOG_RATIO,
5807         SWM_S_DISABLE_BORDER,
5808         SWM_S_FOCUS_CLOSE,
5809         SWM_S_FOCUS_CLOSE_WRAP,
5810         SWM_S_FOCUS_DEFAULT,
5811         SWM_S_FOCUS_MODE,
5812         SWM_S_SPAWN_ORDER,
5813         SWM_S_SPAWN_TERM,
5814         SWM_S_SS_APP,
5815         SWM_S_SS_ENABLED,
5816         SWM_S_STACK_ENABLED,
5817         SWM_S_TERM_WIDTH,
5818         SWM_S_TITLE_CLASS_ENABLED,
5819         SWM_S_TITLE_NAME_ENABLED,
5820         SWM_S_URGENT_ENABLED,
5821         SWM_S_VERBOSE_LAYOUT,
5822         SWM_S_WINDOW_NAME_ENABLED,
5823         SWM_S_WORKSPACE_LIMIT
5824 };
5825
5826 int
5827 setconfvalue(char *selector, char *value, int flags)
5828 {
5829         int     i;
5830         char    *b;
5831
5832         switch (flags) {
5833         case SWM_S_BAR_ACTION:
5834                 free(bar_argv[0]);
5835                 if ((bar_argv[0] = strdup(value)) == NULL)
5836                         err(1, "setconfvalue: bar_action");
5837                 break;
5838         case SWM_S_BAR_AT_BOTTOM:
5839                 bar_at_bottom = atoi(value);
5840                 break;
5841         case SWM_S_BAR_BORDER_WIDTH:
5842                 bar_border_width = atoi(value);
5843                 if (bar_border_width < 0)
5844                         bar_border_width = 0;
5845                 break;
5846         case SWM_S_BAR_DELAY:
5847                 bar_delay = atoi(value);
5848                 break;
5849         case SWM_S_BAR_ENABLED:
5850                 bar_enabled = atoi(value);
5851                 break;
5852         case SWM_S_BAR_FONT:
5853                 b = bar_fonts;
5854                 if (asprintf(&bar_fonts, "%s,%s", value, bar_fonts) == -1)
5855                         err(1, "setconfvalue: asprintf: failed to allocate "
5856                                 "memory for bar_fonts.");
5857
5858                 free(b);
5859                 break;
5860         case SWM_S_BAR_FORMAT:
5861                 free(bar_format);
5862                 if ((bar_format = strdup(value)) == NULL)
5863                         err(1, "setconfvalue: bar_format");
5864                 break;
5865         case SWM_S_BAR_JUSTIFY:
5866                 if (!strcmp(value, "left"))
5867                         bar_justify = SWM_BAR_JUSTIFY_LEFT;
5868                 else if (!strcmp(value, "center"))
5869                         bar_justify = SWM_BAR_JUSTIFY_CENTER;
5870                 else if (!strcmp(value, "right"))
5871                         bar_justify = SWM_BAR_JUSTIFY_RIGHT;
5872                 else
5873                         errx(1, "invalid bar_justify");
5874                 break;
5875         case SWM_S_BORDER_WIDTH:
5876                 border_width = atoi(value);
5877                 if (border_width < 0)
5878                         border_width = 0;
5879                 break;
5880         case SWM_S_CLOCK_ENABLED:
5881                 clock_enabled = atoi(value);
5882                 break;
5883         case SWM_S_CLOCK_FORMAT:
5884 #ifndef SWM_DENY_CLOCK_FORMAT
5885                 free(clock_format);
5886                 if ((clock_format = strdup(value)) == NULL)
5887                         err(1, "setconfvalue: clock_format");
5888 #endif
5889                 break;
5890         case SWM_S_CYCLE_EMPTY:
5891                 cycle_empty = atoi(value);
5892                 break;
5893         case SWM_S_CYCLE_VISIBLE:
5894                 cycle_visible = atoi(value);
5895                 break;
5896         case SWM_S_DIALOG_RATIO:
5897                 dialog_ratio = atof(value);
5898                 if (dialog_ratio > 1.0 || dialog_ratio <= .3)
5899                         dialog_ratio = .6;
5900                 break;
5901         case SWM_S_DISABLE_BORDER:
5902                 disable_border = atoi(value);
5903                 break;
5904         case SWM_S_FOCUS_CLOSE:
5905                 if (!strcmp(value, "first"))
5906                         focus_close = SWM_STACK_BOTTOM;
5907                 else if (!strcmp(value, "last"))
5908                         focus_close = SWM_STACK_TOP;
5909                 else if (!strcmp(value, "next"))
5910                         focus_close = SWM_STACK_ABOVE;
5911                 else if (!strcmp(value, "previous"))
5912                         focus_close = SWM_STACK_BELOW;
5913                 else
5914                         errx(1, "focus_close");
5915                 break;
5916         case SWM_S_FOCUS_CLOSE_WRAP:
5917                 focus_close_wrap = atoi(value);
5918                 break;
5919         case SWM_S_FOCUS_DEFAULT:
5920                 if (!strcmp(value, "last"))
5921                         focus_default = SWM_STACK_TOP;
5922                 else if (!strcmp(value, "first"))
5923                         focus_default = SWM_STACK_BOTTOM;
5924                 else
5925                         errx(1, "focus_default");
5926                 break;
5927         case SWM_S_FOCUS_MODE:
5928                 if (!strcmp(value, "default"))
5929                         focus_mode = SWM_FOCUS_DEFAULT;
5930                 else if (!strcmp(value, "follow_cursor"))
5931                         focus_mode = SWM_FOCUS_FOLLOW;
5932                 else if (!strcmp(value, "synergy"))
5933                         focus_mode = SWM_FOCUS_SYNERGY;
5934                 else
5935                         errx(1, "focus_mode");
5936                 break;
5937         case SWM_S_SPAWN_ORDER:
5938                 if (!strcmp(value, "first"))
5939                         spawn_position = SWM_STACK_BOTTOM;
5940                 else if (!strcmp(value, "last"))
5941                         spawn_position = SWM_STACK_TOP;
5942                 else if (!strcmp(value, "next"))
5943                         spawn_position = SWM_STACK_ABOVE;
5944                 else if (!strcmp(value, "previous"))
5945                         spawn_position = SWM_STACK_BELOW;
5946                 else
5947                         errx(1, "spawn_position");
5948                 break;
5949         case SWM_S_SPAWN_TERM:
5950                 setconfspawn("term", value, 0);
5951                 setconfspawn("spawn_term", value, 0);
5952                 break;
5953         case SWM_S_SS_APP:
5954                 break;
5955         case SWM_S_SS_ENABLED:
5956                 ss_enabled = atoi(value);
5957                 break;
5958         case SWM_S_STACK_ENABLED:
5959                 stack_enabled = atoi(value);
5960                 break;
5961         case SWM_S_TERM_WIDTH:
5962                 term_width = atoi(value);
5963                 if (term_width < 0)
5964                         term_width = 0;
5965                 break;
5966         case SWM_S_TITLE_CLASS_ENABLED:
5967                 title_class_enabled = atoi(value);
5968                 break;
5969         case SWM_S_TITLE_NAME_ENABLED:
5970                 title_name_enabled = atoi(value);
5971                 break;
5972         case SWM_S_URGENT_ENABLED:
5973                 urgent_enabled = atoi(value);
5974                 break;
5975         case SWM_S_VERBOSE_LAYOUT:
5976                 verbose_layout = atoi(value);
5977                 for (i = 0; layouts[i].l_stack != NULL; i++) {
5978                         if (verbose_layout)
5979                                 layouts[i].l_string = fancy_stacker;
5980                         else
5981                                 layouts[i].l_string = plain_stacker;
5982                 }
5983                 break;
5984         case SWM_S_WINDOW_NAME_ENABLED:
5985                 window_name_enabled = atoi(value);
5986                 break;
5987         case SWM_S_WORKSPACE_LIMIT:
5988                 workspace_limit = atoi(value);
5989                 if (workspace_limit > SWM_WS_MAX)
5990                         workspace_limit = SWM_WS_MAX;
5991                 else if (workspace_limit < 1)
5992                         workspace_limit = 1;
5993                 break;
5994         default:
5995                 return (1);
5996         }
5997         return (0);
5998 }
5999
6000 int
6001 setconfmodkey(char *selector, char *value, int flags)
6002 {
6003         if (!strncasecmp(value, "Mod1", strlen("Mod1")))
6004                 update_modkey(Mod1Mask);
6005         else if (!strncasecmp(value, "Mod2", strlen("Mod2")))
6006                 update_modkey(Mod2Mask);
6007         else if (!strncasecmp(value, "Mod3", strlen("Mod3")))
6008                 update_modkey(Mod3Mask);
6009         else if (!strncasecmp(value, "Mod4", strlen("Mod4")))
6010                 update_modkey(Mod4Mask);
6011         else
6012                 return (1);
6013         return (0);
6014 }
6015
6016 int
6017 setconfcolor(char *selector, char *value, int flags)
6018 {
6019         setscreencolor(value, ((selector == NULL)?-1:atoi(selector)), flags);
6020         return (0);
6021 }
6022
6023 int
6024 setconfregion(char *selector, char *value, int flags)
6025 {
6026         custom_region(value);
6027         return (0);
6028 }
6029
6030 int
6031 setautorun(char *selector, char *value, int flags)
6032 {
6033         int                     ws_id;
6034         char                    s[1024];
6035         char                    *ap, *sp = s;
6036         union arg               a;
6037         int                     argc = 0;
6038         long                    pid;
6039         struct pid_e            *p;
6040
6041         if (getenv("SWM_STARTED"))
6042                 return (0);
6043
6044         bzero(s, sizeof s);
6045         if (sscanf(value, "ws[%d]:%1023c", &ws_id, s) != 2)
6046                 errx(1, "invalid autorun entry, should be 'ws[<idx>]:command'");
6047         ws_id--;
6048         if (ws_id < 0 || ws_id >= workspace_limit)
6049                 errx(1, "autorun: invalid workspace %d", ws_id + 1);
6050
6051         /*
6052          * This is a little intricate
6053          *
6054          * If the pid already exists we simply reuse it because it means it was
6055          * used before AND not claimed by manage_window.  We get away with
6056          * altering it in the parent after INSERT because this can not be a race
6057          */
6058         a.argv = NULL;
6059         while ((ap = strsep(&sp, " \t")) != NULL) {
6060                 if (*ap == '\0')
6061                         continue;
6062                 DNPRINTF(SWM_D_SPAWN, "setautorun: arg [%s]\n", ap);
6063                 argc++;
6064                 if ((a.argv = realloc(a.argv, argc * sizeof(char *))) == NULL)
6065                         err(1, "setautorun: realloc");
6066                 a.argv[argc - 1] = ap;
6067         }
6068
6069         if ((a.argv = realloc(a.argv, (argc + 1) * sizeof(char *))) == NULL)
6070                 err(1, "setautorun: realloc");
6071         a.argv[argc] = NULL;
6072
6073         if ((pid = fork()) == 0) {
6074                 spawn(ws_id, &a, 1);
6075                 /* NOTREACHED */
6076                 _exit(1);
6077         }
6078         free(a.argv);
6079
6080         /* parent */
6081         p = find_pid(pid);
6082         if (p == NULL) {
6083                 p = calloc(1, sizeof *p);
6084                 if (p == NULL)
6085                         return (1);
6086                 TAILQ_INSERT_TAIL(&pidlist, p, entry);
6087         }
6088
6089         p->pid = pid;
6090         p->ws = ws_id;
6091
6092         return (0);
6093 }
6094
6095 int
6096 setlayout(char *selector, char *value, int flags)
6097 {
6098         int                     ws_id, i, x, mg, ma, si, raise, f = 0;
6099         int                     st = SWM_V_STACK, num_screens;
6100         char                    s[1024];
6101         struct workspace        *ws;
6102
6103         if (getenv("SWM_STARTED"))
6104                 return (0);
6105
6106         bzero(s, sizeof s);
6107         if (sscanf(value, "ws[%d]:%d:%d:%d:%d:%1023c",
6108             &ws_id, &mg, &ma, &si, &raise, s) != 6)
6109                 errx(1, "invalid layout entry, should be 'ws[<idx>]:"
6110                     "<master_grow>:<master_add>:<stack_inc>:<always_raise>:"
6111                     "<type>'");
6112         ws_id--;
6113         if (ws_id < 0 || ws_id >= workspace_limit)
6114                 errx(1, "layout: invalid workspace %d", ws_id + 1);
6115
6116         if (!strcasecmp(s, "vertical"))
6117                 st = SWM_V_STACK;
6118         else if (!strcasecmp(s, "vertical_flip")) {
6119                 st = SWM_V_STACK;
6120                 f = 1;
6121         } else if (!strcasecmp(s, "horizontal"))
6122                 st = SWM_H_STACK;
6123         else if (!strcasecmp(s, "horizontal_flip")) {
6124                 st = SWM_H_STACK;
6125                 f = 1;
6126         } else if (!strcasecmp(s, "fullscreen"))
6127                 st = SWM_MAX_STACK;
6128         else
6129                 errx(1, "invalid layout entry, should be 'ws[<idx>]:"
6130                     "<master_grow>:<master_add>:<stack_inc>:<always_raise>:"
6131                     "<type>'");
6132
6133         num_screens = xcb_setup_roots_length(xcb_get_setup(conn));
6134         for (i = 0; i < num_screens; i++) {
6135                 ws = (struct workspace *)&screens[i].ws;
6136                 ws[ws_id].cur_layout = &layouts[st];
6137
6138                 ws[ws_id].always_raise = raise;
6139                 if (st == SWM_MAX_STACK)
6140                         continue;
6141
6142                 /* master grow */
6143                 for (x = 0; x < abs(mg); x++) {
6144                         ws[ws_id].cur_layout->l_config(&ws[ws_id],
6145                             mg >= 0 ?  SWM_ARG_ID_MASTERGROW :
6146                             SWM_ARG_ID_MASTERSHRINK);
6147                         stack();
6148                 }
6149                 /* master add */
6150                 for (x = 0; x < abs(ma); x++) {
6151                         ws[ws_id].cur_layout->l_config(&ws[ws_id],
6152                             ma >= 0 ?  SWM_ARG_ID_MASTERADD :
6153                             SWM_ARG_ID_MASTERDEL);
6154                         stack();
6155                 }
6156                 /* stack inc */
6157                 for (x = 0; x < abs(si); x++) {
6158                         ws[ws_id].cur_layout->l_config(&ws[ws_id],
6159                             si >= 0 ?  SWM_ARG_ID_STACKINC :
6160                             SWM_ARG_ID_STACKDEC);
6161                         stack();
6162                 }
6163                 /* Apply flip */
6164                 if (f) {
6165                         ws[ws_id].cur_layout->l_config(&ws[ws_id],
6166                             SWM_ARG_ID_FLIPLAYOUT);
6167                         stack();
6168                 }
6169         }
6170
6171         return (0);
6172 }
6173
6174 /* config options */
6175 struct config_option {
6176         char                    *optname;
6177         int                     (*func)(char*, char*, int);
6178         int                     funcflags;
6179 };
6180 struct config_option configopt[] = {
6181         { "bar_enabled",                setconfvalue,   SWM_S_BAR_ENABLED },
6182         { "bar_at_bottom",              setconfvalue,   SWM_S_BAR_AT_BOTTOM },
6183         { "bar_border",                 setconfcolor,   SWM_S_COLOR_BAR_BORDER },
6184         { "bar_border_width",           setconfvalue,   SWM_S_BAR_BORDER_WIDTH },
6185         { "bar_color",                  setconfcolor,   SWM_S_COLOR_BAR },
6186         { "bar_font_color",             setconfcolor,   SWM_S_COLOR_BAR_FONT },
6187         { "bar_font",                   setconfvalue,   SWM_S_BAR_FONT },
6188         { "bar_action",                 setconfvalue,   SWM_S_BAR_ACTION },
6189         { "bar_delay",                  setconfvalue,   SWM_S_BAR_DELAY },
6190         { "bar_justify",                setconfvalue,   SWM_S_BAR_JUSTIFY },
6191         { "bar_format",                 setconfvalue,   SWM_S_BAR_FORMAT },
6192         { "keyboard_mapping",           setkeymapping,  0 },
6193         { "bind",                       setconfbinding, 0 },
6194         { "stack_enabled",              setconfvalue,   SWM_S_STACK_ENABLED },
6195         { "clock_enabled",              setconfvalue,   SWM_S_CLOCK_ENABLED },
6196         { "clock_format",               setconfvalue,   SWM_S_CLOCK_FORMAT },
6197         { "color_focus",                setconfcolor,   SWM_S_COLOR_FOCUS },
6198         { "color_unfocus",              setconfcolor,   SWM_S_COLOR_UNFOCUS },
6199         { "cycle_empty",                setconfvalue,   SWM_S_CYCLE_EMPTY },
6200         { "cycle_visible",              setconfvalue,   SWM_S_CYCLE_VISIBLE },
6201         { "workspace_limit",            setconfvalue,   SWM_S_WORKSPACE_LIMIT },
6202         { "dialog_ratio",               setconfvalue,   SWM_S_DIALOG_RATIO },
6203         { "verbose_layout",             setconfvalue,   SWM_S_VERBOSE_LAYOUT },
6204         { "modkey",                     setconfmodkey,  0 },
6205         { "program",                    setconfspawn,   0 },
6206         { "quirk",                      setconfquirk,   0 },
6207         { "region",                     setconfregion,  0 },
6208         { "spawn_term",                 setconfvalue,   SWM_S_SPAWN_TERM },
6209         { "screenshot_enabled",         setconfvalue,   SWM_S_SS_ENABLED },
6210         { "screenshot_app",             setconfvalue,   SWM_S_SS_APP },
6211         { "window_name_enabled",        setconfvalue,   SWM_S_WINDOW_NAME_ENABLED },
6212         { "urgent_enabled",             setconfvalue,   SWM_S_URGENT_ENABLED },
6213         { "term_width",                 setconfvalue,   SWM_S_TERM_WIDTH },
6214         { "title_class_enabled",        setconfvalue,   SWM_S_TITLE_CLASS_ENABLED },
6215         { "title_name_enabled",         setconfvalue,   SWM_S_TITLE_NAME_ENABLED },
6216         { "focus_mode",                 setconfvalue,   SWM_S_FOCUS_MODE },
6217         { "focus_close",                setconfvalue,   SWM_S_FOCUS_CLOSE },
6218         { "focus_close_wrap",           setconfvalue,   SWM_S_FOCUS_CLOSE_WRAP },
6219         { "focus_default",              setconfvalue,   SWM_S_FOCUS_DEFAULT },
6220         { "spawn_position",             setconfvalue,   SWM_S_SPAWN_ORDER },
6221         { "disable_border",             setconfvalue,   SWM_S_DISABLE_BORDER },
6222         { "border_width",               setconfvalue,   SWM_S_BORDER_WIDTH },
6223         { "autorun",                    setautorun,     0 },
6224         { "layout",                     setlayout,      0 },
6225 };
6226
6227
6228 int
6229 conf_load(char *filename, int keymapping)
6230 {
6231         FILE                    *config;
6232         char                    *line, *cp, *optsub, *optval;
6233         size_t                  linelen, lineno = 0;
6234         int                     wordlen, i, optind;
6235         struct config_option    *opt;
6236
6237         DNPRINTF(SWM_D_CONF, "conf_load: begin\n");
6238
6239         if (filename == NULL) {
6240                 warnx("conf_load: no filename");
6241                 return (1);
6242         }
6243         if ((config = fopen(filename, "r")) == NULL) {
6244                 warn("conf_load: fopen: %s", filename);
6245                 return (1);
6246         }
6247
6248         while (!feof(config)) {
6249                 if ((line = fparseln(config, &linelen, &lineno, NULL, 0))
6250                     == NULL) {
6251                         if (ferror(config))
6252                                 err(1, "%s", filename);
6253                         else
6254                                 continue;
6255                 }
6256                 cp = line;
6257                 cp += strspn(cp, " \t\n"); /* eat whitespace */
6258                 if (cp[0] == '\0') {
6259                         /* empty line */
6260                         free(line);
6261                         continue;
6262                 }
6263                 /* get config option */
6264                 wordlen = strcspn(cp, "=[ \t\n");
6265                 if (wordlen == 0) {
6266                         warnx("%s: line %zd: no option found",
6267                             filename, lineno);
6268                         goto out;
6269                 }
6270                 optind = -1;
6271                 for (i = 0; i < LENGTH(configopt); i++) {
6272                         opt = &configopt[i];
6273                         if (!strncasecmp(cp, opt->optname, wordlen) &&
6274                             strlen(opt->optname) == wordlen) {
6275                                 optind = i;
6276                                 break;
6277                         }
6278                 }
6279                 if (optind == -1) {
6280                         warnx("%s: line %zd: unknown option %.*s",
6281                             filename, lineno, wordlen, cp);
6282                         goto out;
6283                 }
6284                 if (keymapping && strcmp(opt->optname, "bind")) {
6285                         warnx("%s: line %zd: invalid option %.*s",
6286                             filename, lineno, wordlen, cp);
6287                         goto out;
6288                 }
6289                 cp += wordlen;
6290                 cp += strspn(cp, " \t\n"); /* eat whitespace */
6291                 /* get [selector] if any */
6292                 optsub = NULL;
6293                 if (*cp == '[') {
6294                         cp++;
6295                         wordlen = strcspn(cp, "]");
6296                         if (*cp != ']') {
6297                                 if (wordlen == 0) {
6298                                         warnx("%s: line %zd: syntax error",
6299                                             filename, lineno);
6300                                         goto out;
6301                                 }
6302
6303                                 if (asprintf(&optsub, "%.*s", wordlen, cp) ==
6304                                     -1) {
6305                                         warnx("%s: line %zd: unable to allocate"
6306                                             "memory for selector", filename,
6307                                             lineno);
6308                                         goto out;
6309                                 }
6310                         }
6311                         cp += wordlen;
6312                         cp += strspn(cp, "] \t\n"); /* eat trailing */
6313                 }
6314                 cp += strspn(cp, "= \t\n"); /* eat trailing */
6315                 /* get RHS value */
6316                 optval = strdup(cp);
6317                 /* call function to deal with it all */
6318                 if (configopt[optind].func(optsub, optval,
6319                     configopt[optind].funcflags) != 0)
6320                         errx(1, "%s: line %zd: invalid data for %s",
6321                             filename, lineno, configopt[optind].optname);
6322                 free(optval);
6323                 free(optsub);
6324                 free(line);
6325         }
6326
6327         fclose(config);
6328         DNPRINTF(SWM_D_CONF, "conf_load: end\n");
6329
6330         return (0);
6331
6332 out:
6333         free(line);
6334         fclose(config);
6335         DNPRINTF(SWM_D_CONF, "conf_load: end with error.\n");
6336
6337         return (1);
6338 }
6339
6340 void
6341 set_child_transient(struct ws_win *win, xcb_window_t *trans)
6342 {
6343         struct ws_win           *parent, *w;
6344         struct swm_region       *r;
6345         struct workspace        *ws;
6346         xcb_icccm_wm_hints_t    wmh;
6347
6348         parent = find_window(win->transient);
6349         if (parent)
6350                 parent->child_trans = win;
6351         else {
6352                 DNPRINTF(SWM_D_MISC, "set_child_transient: parent doesn't exist"
6353                     " for 0x%x trans 0x%x\n", win->id, win->transient);
6354
6355                 r = root_to_region(win->wa->root);
6356                 ws = r->ws;
6357                 /* parent doen't exist in our window list */
6358                 TAILQ_FOREACH(w, &ws->winlist, entry) {
6359                         if (xcb_icccm_get_wm_hints_reply(conn,
6360                                         xcb_icccm_get_wm_hints(conn, w->id),
6361                                         &wmh, NULL) != 1) {
6362                                 warnx("can't get hints for 0x%x", w->id);
6363                                 continue;
6364                         }
6365
6366                         if (win->hints.window_group != wmh.window_group)
6367                                 continue;
6368
6369                         w->child_trans = win;
6370                         win->transient = w->id;
6371                         *trans = w->id;
6372                         DNPRINTF(SWM_D_MISC, "set_child_transient: adjusting "
6373                             "transient to 0x%x\n", win->transient);
6374                         break;
6375                 }
6376         }
6377 }
6378
6379 long
6380 window_get_pid(xcb_window_t win)
6381 {
6382         long                            ret = 0;
6383         const char                      *errstr;
6384         xcb_atom_t                      apid;
6385         xcb_get_property_cookie_t       pc;
6386         xcb_get_property_reply_t        *pr;
6387
6388         apid = get_atom_from_string("_NET_WM_PID");
6389         if (apid == XCB_ATOM_NONE)
6390                 goto tryharder;
6391
6392         pc = xcb_get_property(conn, False, win, apid, XCB_ATOM_CARDINAL, 0, 1);
6393         pr = xcb_get_property_reply(conn, pc, NULL);
6394         if (!pr)
6395                 goto tryharder;
6396         if (pr->type != XCB_ATOM_CARDINAL)
6397                 goto tryharder;
6398
6399         ret = *(long *)xcb_get_property_value(pr);
6400         free(pr);
6401
6402         return (ret);
6403
6404 tryharder:
6405         apid = get_atom_from_string("_SWM_PID");
6406         pc = xcb_get_property(conn, False, win, apid, XCB_ATOM_STRING,
6407                 0, SWM_PROPLEN);
6408         pr = xcb_get_property_reply(conn, pc, NULL);
6409         if (!pr)
6410                 return (0);
6411         if (pr->type != XCB_ATOM_STRING)
6412                 free(pr);
6413                 return (0);
6414         ret = strtonum(xcb_get_property_value(pr), 0, UINT_MAX, &errstr);
6415         free(pr);
6416
6417         return (ret);
6418 }
6419
6420 struct ws_win *
6421 manage_window(xcb_window_t id)
6422 {
6423         xcb_window_t            trans = XCB_WINDOW_NONE;
6424         struct workspace        *ws;
6425         struct ws_win           *win, *ww;
6426         int                     i, ws_idx, border_me = 0;
6427         xcb_atom_t              ws_idx_atom = XCB_ATOM_NONE;
6428         char                    ws_idx_str[SWM_PROPLEN], *prop = NULL;
6429         size_t                  proplen;
6430         struct swm_region       *r;
6431         const char              *errstr;
6432         struct pid_e            *p;
6433         struct quirk            *qp;
6434         uint32_t                event_mask;
6435         xcb_atom_t              prot;
6436         xcb_get_property_reply_t        *gpr;
6437         xcb_icccm_get_wm_protocols_reply_t      wpr;
6438
6439         if ((win = find_window(id)) != NULL)
6440                 return (win);   /* already being managed */
6441
6442         /* see if we are on the unmanaged list */
6443         if ((win = find_unmanaged_window(id)) != NULL) {
6444                 DNPRINTF(SWM_D_MISC, "manage_window: previously unmanaged "
6445                     "window: 0x%x\n", win->id);
6446                 TAILQ_REMOVE(&win->ws->unmanagedlist, win, entry);
6447                 if (win->transient)
6448                         set_child_transient(win, &trans);
6449
6450                 if (trans && (ww = find_window(trans)))
6451                         TAILQ_INSERT_AFTER(&win->ws->winlist, ww, win, entry);
6452                 else if ((ww = win->ws->focus) &&
6453                     spawn_position == SWM_STACK_ABOVE)
6454                         TAILQ_INSERT_AFTER(&win->ws->winlist, win->ws->focus,
6455                             win, entry);
6456                 else if (ww && spawn_position == SWM_STACK_BELOW)
6457                         TAILQ_INSERT_BEFORE(win->ws->focus, win, entry);
6458                 else switch (spawn_position) {
6459                 default:
6460                 case SWM_STACK_TOP:
6461                 case SWM_STACK_ABOVE:
6462                         TAILQ_INSERT_TAIL(&win->ws->winlist, win, entry);
6463                         break;
6464                 case SWM_STACK_BOTTOM:
6465                 case SWM_STACK_BELOW:
6466                         TAILQ_INSERT_HEAD(&win->ws->winlist, win, entry);
6467                 }
6468
6469                 ewmh_update_actions(win);
6470                 return (win);
6471         }
6472
6473         if ((win = calloc(1, sizeof(struct ws_win))) == NULL)
6474                 err(1, "manage_window: calloc: failed to allocate memory for "
6475                     "new window");
6476
6477         win->id = id;
6478         win->bordered = 0;
6479
6480         /* see if we need to override the workspace */
6481         p = find_pid(window_get_pid(id));
6482
6483         /* Get all the window data in one shot */
6484         ws_idx_atom = get_atom_from_string("_SWM_WS");
6485         if (ws_idx_atom) {
6486                 gpr = xcb_get_property_reply(conn,
6487                         xcb_get_property(conn, False, id, ws_idx_atom,
6488                                 XCB_ATOM_STRING, 0, SWM_PROPLEN),
6489                         NULL);
6490                 if (gpr) {
6491                         proplen = xcb_get_property_value_length(gpr);
6492                         if (proplen > 0) {
6493                                 prop = malloc(proplen + 1);
6494                                 if (prop) {
6495                                         memcpy(prop,
6496                                             xcb_get_property_value(gpr),
6497                                             proplen);   
6498                                         prop[proplen] = '\0';
6499                                 }
6500                         }
6501                         free(gpr);
6502                 }
6503         }
6504         win->wa = xcb_get_geometry_reply(conn,
6505                 xcb_get_geometry(conn, id),
6506                 NULL);  
6507         xcb_icccm_get_wm_normal_hints_reply(conn,
6508                 xcb_icccm_get_wm_normal_hints(conn, id),
6509                 &win->sh, NULL);
6510         xcb_icccm_get_wm_hints_reply(conn,
6511                 xcb_icccm_get_wm_hints(conn, id),
6512                 &win->hints, NULL);
6513         xcb_icccm_get_wm_transient_for_reply(conn,
6514                 xcb_icccm_get_wm_transient_for(conn, id),
6515                 &trans, NULL);
6516         if (trans) {
6517                 win->transient = trans;
6518                 set_child_transient(win, &trans);
6519                 DNPRINTF(SWM_D_MISC, "manage_window: window: 0x%x, "
6520                     "transient: 0x%x\n", win->id, win->transient);
6521         }
6522
6523         prot = get_atom_from_string("WM_PROTOCOLS");
6524
6525         /* get supported protocols */
6526         if (xcb_icccm_get_wm_protocols_reply(conn,
6527                         xcb_icccm_get_wm_protocols(conn, id, prot),
6528                         &wpr, NULL)) {
6529                 for (i = 0; i < wpr.atoms_len; i++) {
6530                         if (wpr.atoms[i] == takefocus)
6531                                 win->take_focus = 1;
6532                         if (wpr.atoms[i] == adelete)
6533                                 win->can_delete = 1;
6534                 }
6535                 xcb_icccm_get_wm_protocols_reply_wipe(&wpr);
6536         }
6537
6538         win->iconic = get_iconic(win);
6539
6540         /*
6541          * Figure out where to put the window. If it was previously assigned to
6542          * a workspace (either by spawn() or manually moving), and isn't
6543          * transient, * put it in the same workspace
6544          */
6545         r = root_to_region(win->wa->root);
6546         if (p) {
6547                 ws = &r->s->ws[p->ws];
6548                 TAILQ_REMOVE(&pidlist, p, entry);
6549                 free(p);
6550                 p = NULL;
6551         } else if (prop && win->transient == 0) {
6552                 DNPRINTF(SWM_D_PROP, "manage_window: get _SWM_WS: %s\n", prop);
6553                 ws_idx = strtonum(prop, 0, workspace_limit - 1,
6554                     &errstr);
6555                 if (errstr) {
6556                         DNPRINTF(SWM_D_EVENT, "manage_window: window: #%s: %s",
6557                             errstr, prop);
6558                 }
6559                 ws = &r->s->ws[ws_idx];
6560         } else {
6561                 ws = r->ws;
6562                 /* this should launch transients in the same ws as parent */
6563                 if (id && trans)
6564                         if ((ww = find_window(trans)) != NULL)
6565                                 if (ws->r) {
6566                                         ws = ww->ws;
6567                                         if (ww->ws->r)
6568                                                 r = ww->ws->r;
6569                                         else
6570                                                 warnx("manage_window: fix this "
6571                                                     "bug mcbride");
6572                                         border_me = 1;
6573                                 }
6574         }
6575
6576         /* set up the window layout */
6577         win->id = id;
6578         win->ws = ws;
6579         win->s = r->s;  /* this never changes */
6580         if (trans && (ww = find_window(trans)))
6581                 TAILQ_INSERT_AFTER(&ws->winlist, ww, win, entry);
6582         else if (win->ws->focus && spawn_position == SWM_STACK_ABOVE)
6583                 TAILQ_INSERT_AFTER(&win->ws->winlist, win->ws->focus, win,
6584                     entry);
6585         else if (win->ws->focus && spawn_position == SWM_STACK_BELOW)
6586                 TAILQ_INSERT_BEFORE(win->ws->focus, win, entry);
6587         else switch (spawn_position) {
6588         default:
6589         case SWM_STACK_TOP:
6590         case SWM_STACK_ABOVE:
6591                 TAILQ_INSERT_TAIL(&win->ws->winlist, win, entry);
6592                 break;
6593         case SWM_STACK_BOTTOM:
6594         case SWM_STACK_BELOW:
6595                 TAILQ_INSERT_HEAD(&win->ws->winlist, win, entry);
6596         }
6597
6598         /* ignore window border if there is one. */
6599         WIDTH(win) = win->wa->width;
6600         HEIGHT(win) = win->wa->height;
6601         X(win) = win->wa->x + win->wa->border_width;
6602         Y(win) = win->wa->y + win->wa->border_width;
6603         win->bordered = 0;
6604         win->g_floatvalid = 0;
6605         win->floatmaxed = 0;
6606         win->ewmh_flags = 0;
6607
6608         DNPRINTF(SWM_D_MISC, "manage_window: window: 0x%x, (x,y) w x h: "
6609             "(%d,%d) %d x %d, ws: %d\n", win->id, X(win), Y(win), WIDTH(win),
6610             HEIGHT(win), ws->idx);
6611
6612         constrain_window(win, r, 0);
6613
6614         /* Set window properties so we can remember this after reincarnation */
6615         if (ws_idx_atom && prop == NULL &&
6616             snprintf(ws_idx_str, SWM_PROPLEN, "%d", ws->idx) <
6617                 SWM_PROPLEN) {
6618                 DNPRINTF(SWM_D_PROP, "manage_window: set _SWM_WS: %s\n",
6619                     ws_idx_str);
6620                 xcb_change_property(conn, XCB_PROP_MODE_REPLACE, win->id,
6621                         ws_idx_atom, XCB_ATOM_STRING, 8, strlen(ws_idx_str),
6622                         ws_idx_str);
6623         }
6624         if (prop)
6625                 free(prop);
6626
6627         ewmh_autoquirk(win);
6628
6629         if (xcb_icccm_get_wm_class_reply(conn,
6630                         xcb_icccm_get_wm_class(conn, win->id),
6631                         &win->ch, NULL)) {
6632                 DNPRINTF(SWM_D_CLASS, "manage_window: class: %s, name: %s\n",
6633                     win->ch.class_name, win->ch.instance_name);
6634
6635                 /* java is retarded so treat it special */
6636                 if (strstr(win->ch.instance_name, "sun-awt")) {
6637                         win->java = 1;
6638                         border_me = 1;
6639                 }
6640
6641                 TAILQ_FOREACH(qp, &quirks, entry) {
6642                         if (!strcmp(win->ch.class_name, qp->class) &&
6643                             !strcmp(win->ch.instance_name, qp->name)) {
6644                                 DNPRINTF(SWM_D_CLASS, "manage_window: found: "
6645                                     "class: %s, name: %s\n", win->ch.class_name,
6646                                     win->ch.instance_name);
6647                                 if (qp->quirk & SWM_Q_FLOAT) {
6648                                         win->floating = 1;
6649                                         border_me = 1;
6650                                 }
6651                                 win->quirks = qp->quirk;
6652                         }
6653                 }
6654         }
6655
6656         /* alter window position if quirky */
6657         if (win->quirks & SWM_Q_ANYWHERE) {
6658                 win->manual = 1; /* don't center the quirky windows */
6659                 if (bar_enabled && Y(win) < bar_height)
6660                         Y(win) = bar_height;
6661                 if (WIDTH(win) + X(win) > WIDTH(r))
6662                         X(win) = WIDTH(r) - WIDTH(win) - 2;
6663                 border_me = 1;
6664         }
6665
6666         /* Reset font sizes (the bruteforce way; no default keybinding). */
6667         if (win->quirks & SWM_Q_XTERM_FONTADJ) {
6668                 for (i = 0; i < SWM_MAX_FONT_STEPS; i++)
6669                         fake_keypress(win, XK_KP_Subtract, ShiftMask);
6670                 for (i = 0; i < SWM_MAX_FONT_STEPS; i++)
6671                         fake_keypress(win, XK_KP_Add, ShiftMask);
6672         }
6673
6674         ewmh_get_win_state(win);
6675         ewmh_update_actions(win);
6676         ewmh_update_win_state(win, None, _NET_WM_STATE_REMOVE);
6677
6678         /* border me */
6679         if (border_me) {
6680                 win->bordered = 1;
6681                 X(win) -= border_width;
6682                 Y(win) -= border_width;
6683                 update_window(win);
6684         }
6685
6686         event_mask = XCB_EVENT_MASK_ENTER_WINDOW | XCB_EVENT_MASK_FOCUS_CHANGE |
6687                         XCB_EVENT_MASK_PROPERTY_CHANGE |
6688                         XCB_EVENT_MASK_STRUCTURE_NOTIFY;
6689
6690         xcb_change_window_attributes(conn, id, XCB_CW_EVENT_MASK, &event_mask);
6691
6692         /* floaters need to be mapped if they are in the current workspace */
6693         if ((win->floating || win->transient) && (ws->idx == r->ws->idx))
6694                 map_window_raised(win->id);
6695
6696         return (win);
6697 }
6698
6699 void
6700 free_window(struct ws_win *win)
6701 {
6702         DNPRINTF(SWM_D_MISC, "free_window: window: 0x%x\n", win->id);
6703
6704         if (win == NULL)
6705                 return;
6706
6707         /* needed for restart wm */
6708         set_win_state(win, XCB_ICCCM_WM_STATE_WITHDRAWN);
6709
6710         TAILQ_REMOVE(&win->ws->unmanagedlist, win, entry);
6711
6712         if (win->wa)
6713                 free(win->wa);
6714         
6715         xcb_icccm_get_wm_class_reply_wipe(&win->ch);
6716
6717         kill_refs(win);
6718
6719         /* paint memory */
6720         memset(win, 0xff, sizeof *win); /* XXX kill later */
6721
6722         free(win);
6723 }
6724
6725 void
6726 unmanage_window(struct ws_win *win)
6727 {
6728         struct ws_win           *parent;
6729         xcb_screen_t            *screen;
6730
6731         if (win == NULL)
6732                 return;
6733
6734         DNPRINTF(SWM_D_MISC, "unmanage_window: window: 0x%x\n", win->id);
6735
6736         if (win->transient) {
6737                 parent = find_window(win->transient);
6738                 if (parent)
6739                         parent->child_trans = NULL;
6740         }
6741
6742         /* focus on root just in case */
6743         screen = xcb_setup_roots_iterator(xcb_get_setup(conn)).data;
6744         xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT,
6745                 screen->root, XCB_CURRENT_TIME);
6746
6747         focus_prev(win);
6748
6749         TAILQ_REMOVE(&win->ws->winlist, win, entry);
6750         TAILQ_INSERT_TAIL(&win->ws->unmanagedlist, win, entry);
6751 }
6752
6753 void
6754 focus_magic(struct ws_win *win)
6755 {
6756         DNPRINTF(SWM_D_FOCUS, "focus_magic: window: 0x%x\n", WINID(win));
6757
6758         if (win == NULL) {
6759                 /* if there are no windows clear the status-bar */
6760                 bar_update();
6761                 return;
6762         }
6763
6764         if (win->child_trans) {
6765                 /* win = parent & has a transient so focus on that */
6766                 if (win->java) {
6767                         focus_win(win->child_trans);
6768                         if (win->child_trans->take_focus)
6769                                 client_msg(win, takefocus);
6770                 } else {
6771                         /* make sure transient hasn't disappeared */
6772                         if (validate_win(win->child_trans) == 0) {
6773                                 focus_win(win->child_trans);
6774                                 if (win->child_trans->take_focus)
6775                                         client_msg(win->child_trans, takefocus);
6776                         } else {
6777                                 win->child_trans = NULL;
6778                                 focus_win(win);
6779                                 if (win->take_focus)
6780                                         client_msg(win, takefocus);
6781                         }
6782                 }
6783         } else {
6784                 /* regular focus */
6785                 focus_win(win);
6786                 if (win->take_focus)
6787                         client_msg(win, takefocus);
6788         }
6789 }
6790
6791 void
6792 expose(XEvent *e)
6793 {
6794         DNPRINTF(SWM_D_EVENT, "expose: window: 0x%lx\n", e->xexpose.window);
6795 }
6796
6797 void
6798 keypress(XEvent *e)
6799 {
6800         KeySym                  keysym;
6801         XKeyEvent               *ev = &e->xkey;
6802         struct key              *kp;
6803         struct swm_region       *r;
6804
6805         keysym = XkbKeycodeToKeysym(display, (KeyCode)ev->keycode, 0, 0);
6806         if ((kp = key_lookup(CLEANMASK(ev->state), keysym)) == NULL)
6807                 return;
6808         if (keyfuncs[kp->funcid].func == NULL)
6809                 return;
6810
6811         r = root_to_region(ev->root);
6812         if (kp->funcid == kf_spawn_custom)
6813                 spawn_custom(r, &(keyfuncs[kp->funcid].args), kp->spawn_name);
6814         else
6815                 keyfuncs[kp->funcid].func(r, &(keyfuncs[kp->funcid].args));
6816 }
6817
6818 void
6819 buttonpress(XEvent *e)
6820 {
6821         struct ws_win           *win;
6822         int                     i, action;
6823         XButtonPressedEvent     *ev = &e->xbutton;
6824
6825         if ((win = find_window(ev->window)) == NULL)
6826                 return;
6827
6828         focus_magic(win);
6829         action = client_click;
6830
6831         for (i = 0; i < LENGTH(buttons); i++)
6832                 if (action == buttons[i].action && buttons[i].func &&
6833                     buttons[i].button == ev->button &&
6834                     CLEANMASK(buttons[i].mask) == CLEANMASK(ev->state))
6835                         buttons[i].func(win, &buttons[i].args);
6836 }
6837
6838 void
6839 configurerequest(XEvent *e)
6840 {
6841         XConfigureRequestEvent  *ev = &e->xconfigurerequest;
6842         struct ws_win           *win;
6843         int                     new = 0, i = 0;
6844         uint16_t                mask = 0;       
6845         uint32_t                wc[7];
6846
6847         if ((win = find_window(ev->window)) == NULL)
6848                 if ((win = find_unmanaged_window(ev->window)) == NULL)
6849                         new = 1;
6850
6851         if (new) {
6852                 if (ev->value_mask & XCB_CONFIG_WINDOW_X) {
6853                         mask |= XCB_CONFIG_WINDOW_X;
6854                         wc[i++] = ev->x;
6855                 }
6856                 if (ev->value_mask & XCB_CONFIG_WINDOW_Y) {
6857                         mask |= XCB_CONFIG_WINDOW_Y;
6858                         wc[i++] = ev->y;
6859                 }
6860                 if (ev->value_mask & XCB_CONFIG_WINDOW_WIDTH) {
6861                         mask |= XCB_CONFIG_WINDOW_WIDTH;
6862                         wc[i++] = ev->width;
6863                 }
6864                 if (ev->value_mask & XCB_CONFIG_WINDOW_HEIGHT) {
6865                         mask |= XCB_CONFIG_WINDOW_HEIGHT;
6866                         wc[i++] = ev->height;
6867                 }
6868                 if (ev->value_mask & XCB_CONFIG_WINDOW_BORDER_WIDTH) {
6869                         mask |= XCB_CONFIG_WINDOW_BORDER_WIDTH;
6870                         wc[i++] = ev->border_width;
6871                 }
6872                 if (ev->value_mask & XCB_CONFIG_WINDOW_SIBLING) {
6873                         mask |= XCB_CONFIG_WINDOW_SIBLING;
6874                         wc[i++] = ev->above;
6875                 }
6876                 if (ev->value_mask & XCB_CONFIG_WINDOW_STACK_MODE) {
6877                         mask |= XCB_CONFIG_WINDOW_STACK_MODE;
6878                         wc[i++] = ev->detail;
6879                 }
6880
6881                 DNPRINTF(SWM_D_EVENT, "configurerequest: new window: 0x%lx, "
6882                     "new: %s, (x,y) w x h: (%d,%d) %d x %d\n", ev->window,
6883                     YESNO(new), wc[0], wc[1], wc[2], wc[3]);
6884
6885                 xcb_configure_window(conn, ev->window, mask, wc);
6886         } else if ((!win->manual || win->quirks & SWM_Q_ANYWHERE) &&
6887             !(win->ewmh_flags & EWMH_F_FULLSCREEN)) {
6888                 win->g_float.x = ev->x - X(win->ws->r);
6889                 win->g_float.y = ev->y - Y(win->ws->r);
6890                 win->g_float.w = ev->width;
6891                 win->g_float.h = ev->height;
6892                 win->g_floatvalid = 1;
6893
6894                 if (win->floating) {
6895                         win->g = win->g_float;
6896                         win->g.x += X(win->ws->r);
6897                         win->g.y += Y(win->ws->r);
6898                         update_window(win);
6899                 } else {
6900                         config_win(win, ev);
6901                 }
6902         } else {
6903                 config_win(win, ev);
6904         }
6905 }
6906
6907 void
6908 configurenotify(XEvent *e)
6909 {
6910         struct ws_win           *win;
6911
6912         DNPRINTF(SWM_D_EVENT, "configurenotify: window: 0x%lx\n",
6913             e->xconfigure.window);
6914
6915         win = find_window(e->xconfigure.window);
6916         if (win) {
6917                 xcb_icccm_get_wm_normal_hints_reply(conn,
6918                         xcb_icccm_get_wm_normal_hints(conn, win->id),
6919                         &win->sh, NULL);
6920                 adjust_font(win);
6921                 if (font_adjusted)
6922                         stack();
6923                 if (focus_mode == SWM_FOCUS_DEFAULT)
6924                         drain_enter_notify();
6925         }
6926 }
6927
6928 void
6929 destroynotify(XEvent *e)
6930 {
6931         struct ws_win           *win;
6932         XDestroyWindowEvent     *ev = &e->xdestroywindow;
6933
6934         DNPRINTF(SWM_D_EVENT, "destroynotify: window: 0x%lx\n", ev->window);
6935
6936         if ((win = find_window(ev->window)) == NULL) {
6937                 if ((win = find_unmanaged_window(ev->window)) == NULL)
6938                         return;
6939                 free_window(win);
6940                 return;
6941         }
6942
6943         /* make sure we focus on something */
6944         win->floating = 0;
6945
6946         unmanage_window(win);
6947         stack();
6948         if (focus_mode == SWM_FOCUS_DEFAULT)
6949                 drain_enter_notify();
6950         free_window(win);
6951 }
6952
6953 void
6954 enternotify(XEvent *e)
6955 {
6956         XCrossingEvent          *ev = &e->xcrossing;
6957         XEvent                  cne;
6958         struct ws_win           *win;
6959 #if 0
6960         struct ws_win           *w;
6961         Window                  focus_return;
6962         int                     revert_to_return;
6963 #endif
6964         DNPRINTF(SWM_D_FOCUS, "enternotify: window: 0x%lx, mode: %d, detail: "
6965             "%d, root: 0x%lx, subwindow: 0x%lx, same_screen: %s, focus: %s, "
6966             "state: %d\n", ev->window, ev->mode, ev->detail, ev->root,
6967             ev->subwindow, YESNO(ev->same_screen), YESNO(ev->focus), ev->state);
6968
6969         if (ev->mode != NotifyNormal) {
6970                 DNPRINTF(SWM_D_EVENT, "skip enternotify: generated by "
6971                     "cursor grab.\n");
6972                 return;
6973         }
6974
6975         switch (focus_mode) {
6976         case SWM_FOCUS_DEFAULT:
6977                 break;
6978         case SWM_FOCUS_FOLLOW:
6979                 break;
6980         case SWM_FOCUS_SYNERGY:
6981 #if 0
6982         /*
6983          * all these checks need to be in this order because the
6984          * XCheckTypedWindowEvent relies on weeding out the previous events
6985          *
6986          * making this code an option would enable a follow mouse for focus
6987          * feature
6988          */
6989
6990         /*
6991          * state is set when we are switching workspaces and focus is set when
6992          * the window or a subwindow already has focus (occurs during restart).
6993          *
6994          * Only honor the focus flag if last_focus_event is not FocusOut,
6995          * this allows spectrwm to continue to control focus when another
6996          * program is also playing with it.
6997          */
6998         if (ev->state || (ev->focus && last_focus_event != FocusOut)) {
6999                 DNPRINTF(SWM_D_EVENT, "ignoring enternotify: focus\n");
7000                 return;
7001         }
7002
7003         /*
7004          * happens when a window is created or destroyed and the border
7005          * crosses the mouse pointer and when switching ws
7006          *
7007          * we need the subwindow test to see if we came from root in order
7008          * to give focus to floaters
7009          */
7010         if (ev->mode == NotifyNormal && ev->detail == NotifyVirtual &&
7011             ev->subwindow == 0) {
7012                 DNPRINTF(SWM_D_EVENT, "ignoring enternotify: NotifyVirtual\n");
7013                 return;
7014         }
7015
7016         /* this window already has focus */
7017         if (ev->mode == NotifyNormal && ev->detail == NotifyInferior) {
7018                 DNPRINTF(SWM_D_EVENT, "ignoring enternotify: win has focus\n");
7019                 return;
7020         }
7021
7022         /* this window is being deleted or moved to another ws */
7023         if (XCheckTypedWindowEvent(display, ev->window, ConfigureNotify,
7024             &cne) == True) {
7025                 DNPRINTF(SWM_D_EVENT, "ignoring enternotify: configurenotify\n");
7026                 XPutBackEvent(display, &cne);
7027                 return;
7028         }
7029
7030         if ((win = find_window(ev->window)) == NULL) {
7031                 DNPRINTF(SWM_D_EVENT, "ignoring enternotify: win == NULL\n");
7032                 return;
7033         }
7034
7035         /*
7036          * In fullstack kill all enters unless they come from a different ws
7037          * (i.e. another region) or focus has been grabbed externally.
7038          */
7039         if (win->ws->cur_layout->flags & SWM_L_FOCUSPREV &&
7040             last_focus_event != FocusOut) {
7041                 XGetInputFocus(display, &focus_return, &revert_to_return);
7042                 if ((w = find_window(focus_return)) == NULL ||
7043                     w->ws == win->ws) {
7044                         DNPRINTF(SWM_D_EVENT, "ignoring event: fullstack\n");
7045                         return;
7046                 }
7047         }
7048 #endif
7049                 break;
7050         }
7051
7052         if ((win = find_window(ev->window)) == NULL) {
7053                 DNPRINTF(SWM_D_EVENT, "skip enternotify: window is NULL\n");
7054                 return;
7055         }
7056
7057         /*
7058          * if we have more enternotifies let them handle it in due time
7059          */
7060         if (XCheckTypedEvent(display, EnterNotify, &cne) == True) {
7061                 DNPRINTF(SWM_D_EVENT,
7062                     "ignoring enternotify: got more enternotify\n");
7063                 XPutBackEvent(display, &cne);
7064                 return;
7065         }
7066
7067         focus_magic(win);
7068 }
7069
7070 /* lets us use one switch statement for arbitrary mode/detail combinations */
7071 #define MERGE_MEMBERS(a,b)      (((a & 0xffff) << 16) | (b & 0xffff))
7072
7073 void
7074 focusevent(XEvent *e)
7075 {
7076 #if 0
7077         struct ws_win           *win;
7078         u_int32_t               mode_detail;
7079         XFocusChangeEvent       *ev = &e->xfocus;
7080
7081         DNPRINTF(SWM_D_EVENT, "focusevent: %s window: 0x%lx mode: %d "
7082             "detail: %d\n", ev->type == FocusIn ? "entering" : "leaving",
7083             ev->window, ev->mode, ev->detail);
7084
7085         if (last_focus_event == ev->type) {
7086                 DNPRINTF(SWM_D_FOCUS, "ignoring focusevent: bad ordering\n");
7087                 return;
7088         }
7089
7090         last_focus_event = ev->type;
7091         mode_detail = MERGE_MEMBERS(ev->mode, ev->detail);
7092
7093         switch (mode_detail) {
7094         /* synergy client focus operations */
7095         case MERGE_MEMBERS(NotifyNormal, NotifyNonlinear):
7096         case MERGE_MEMBERS(NotifyNormal, NotifyNonlinearVirtual):
7097
7098         /* synergy server focus operations */
7099         case MERGE_MEMBERS(NotifyWhileGrabbed, NotifyNonlinear):
7100
7101         /* Entering applications like rdesktop that mangle the pointer */
7102         case MERGE_MEMBERS(NotifyNormal, NotifyPointer):
7103
7104                 if ((win = find_window(e->xfocus.window)) != NULL && win->ws->r)
7105                         XSetWindowBorder(display, win->id,
7106                             win->ws->r->s->c[ev->type == FocusIn ?
7107                             SWM_S_COLOR_FOCUS : SWM_S_COLOR_UNFOCUS].color);
7108                 break;
7109         default:
7110                 warnx("ignoring focusevent");
7111                 DNPRINTF(SWM_D_FOCUS, "ignoring focusevent\n");
7112                 break;
7113         }
7114 #endif
7115 }
7116
7117 void
7118 mapnotify(XEvent *e)
7119 {
7120         struct ws_win           *win;
7121         XMapEvent               *ev = &e->xmap;
7122
7123         DNPRINTF(SWM_D_EVENT, "mapnotify: window: 0x%lx\n", ev->window);
7124
7125         win = manage_window(ev->window);
7126         if (win)
7127                 set_win_state(win, XCB_ICCCM_WM_STATE_NORMAL);
7128
7129         /*
7130          * focus_win can only set input focus on a mapped window.
7131          * make sure the window really has focus since it is just being mapped.
7132          */
7133         if (win->ws->focus == win)
7134                 focus_win(win);
7135 }
7136
7137 void
7138 mappingnotify(XEvent *e)
7139 {
7140         XMappingEvent           *ev = &e->xmapping;
7141
7142         XRefreshKeyboardMapping(ev);
7143         if (ev->request == MappingKeyboard)
7144                 grabkeys();
7145 }
7146
7147 void
7148 maprequest(XEvent *e)
7149 {
7150         struct ws_win           *win;
7151         struct swm_region       *r;
7152         XMapRequestEvent        *ev = &e->xmaprequest;
7153         xcb_get_window_attributes_reply_t *war;
7154
7155         DNPRINTF(SWM_D_EVENT, "maprequest: window: 0x%lx\n",
7156             e->xmaprequest.window);
7157
7158         war = xcb_get_window_attributes_reply(conn,
7159                 xcb_get_window_attributes(conn, ev->window),
7160                 NULL);
7161         if (!war)
7162                 return;
7163         if (war->override_redirect) {
7164                 free(war);
7165                 return;
7166         }       
7167         free(war);
7168
7169         win = manage_window(e->xmaprequest.window);
7170         if (win == NULL) {
7171                 return; /* can't happen */
7172         }
7173
7174         stack();
7175
7176         /* make new win focused */
7177         r = root_to_region(win->wa->root);
7178         if (win->ws == r->ws)
7179                 focus_magic(win);
7180 }
7181
7182 void
7183 propertynotify(XEvent *e)
7184 {
7185         struct ws_win           *win;
7186         XPropertyEvent          *ev = &e->xproperty;
7187 #ifdef SWM_DEBUG
7188         char                            *name;
7189         size_t                          len;
7190         xcb_get_atom_name_reply_t       *r;
7191
7192         r = xcb_get_atom_name_reply(conn,
7193                 xcb_get_atom_name(conn, ev->atom),
7194                 NULL);
7195         if (r) {
7196                 len = xcb_get_atom_name_name_length(r);
7197                 if (len > 0) {
7198                         name = malloc(len + 1);
7199                         if (name) {
7200                                 memcpy(name, xcb_get_atom_name_name(r), len);
7201                                 name[len] = '\0';
7202                                 
7203                                 DNPRINTF(SWM_D_EVENT,
7204                                         "propertynotify: window: 0x%lx, "
7205                                         "atom: %s\n",
7206                                         ev->window, name);
7207                                 free(name);
7208                         }
7209                 }
7210                 free(r);
7211         }
7212 #endif
7213
7214         win = find_window(ev->window);
7215         if (win == NULL)
7216                 return;
7217
7218         if (ev->state == PropertyDelete && ev->atom == a_swm_iconic) {
7219                 update_iconic(win, 0);
7220                 map_window_raised(win->id);
7221                 stack();
7222                 focus_win(win);
7223                 return;
7224         }
7225
7226         switch (ev->atom) {
7227 #if 0
7228         case XA_WM_NORMAL_HINTS:
7229                 long            mask;
7230                 XGetWMNormalHints(display, win->id, &win->sh, &mask);
7231                 warnx("normal hints: flag 0x%x", win->sh.flags);
7232                 if (win->sh.flags & XCB_SIZE_HINT_P_MIN_SIZE) {
7233                         WIDTH(win) = win->sh.min_width;
7234                         HEIGHT(win) = win->sh.min_height;
7235                         warnx("min %d %d", WIDTH(win), HEIGHT(win));
7236                 }
7237                 XMoveResizeWindow(display, win->id,
7238                     X(win), Y(win), WIDTH(win), HEIGHT(win));
7239 #endif
7240         case XCB_ATOM_WM_CLASS:
7241         case XCB_ATOM_WM_NAME:
7242                 bar_update();
7243                 break;
7244         default:
7245                 break;
7246         }
7247 }
7248
7249 void
7250 unmapnotify(XEvent *e)
7251 {
7252         struct ws_win           *win;
7253
7254         DNPRINTF(SWM_D_EVENT, "unmapnotify: window: 0x%lx\n", e->xunmap.window);
7255
7256         /* determine if we need to help unmanage this window */
7257         win = find_window(e->xunmap.window);
7258         if (win == NULL)
7259                 return;
7260
7261         if (getstate(e->xunmap.window) == XCB_ICCCM_WM_STATE_NORMAL) {
7262                 unmanage_window(win);
7263                 stack();
7264
7265                 /* giant hack for apps that don't destroy transient windows */
7266                 /* eat a bunch of events to prevent remanaging the window */
7267                 XEvent                  cne;
7268                 while (XCheckWindowEvent(display, e->xunmap.window,
7269                     EnterWindowMask, &cne))
7270                         ;
7271                 while (XCheckWindowEvent(display, e->xunmap.window,
7272                     StructureNotifyMask, &cne))
7273                         ;
7274                 while (XCheckWindowEvent(display, e->xunmap.window,
7275                     SubstructureNotifyMask, &cne))
7276                         ;
7277                 /* resend unmap because we ated it */
7278                 xcb_unmap_window(conn, e->xunmap.window);
7279         }
7280
7281         if (focus_mode == SWM_FOCUS_DEFAULT)
7282                 drain_enter_notify();
7283 }
7284
7285 void
7286 visibilitynotify(XEvent *e)
7287 {
7288         int                     i, num_screens;
7289         struct swm_region       *r;
7290
7291         DNPRINTF(SWM_D_EVENT, "visibilitynotify: window: 0x%lx\n",
7292             e->xvisibility.window);
7293
7294         if (e->xvisibility.state == VisibilityUnobscured) {
7295                 num_screens = xcb_setup_roots_length(xcb_get_setup(conn));
7296                 for (i = 0; i < num_screens; i++)
7297                         TAILQ_FOREACH(r, &screens[i].rl, entry)
7298                                 if (e->xvisibility.window == WINID(r->bar))
7299                                         bar_update();
7300         }
7301 }
7302
7303 void
7304 clientmessage(XEvent *e)
7305 {
7306         XClientMessageEvent     *ev;
7307         struct ws_win           *win;
7308
7309         ev = &e->xclient;
7310
7311         win = find_window(ev->window);
7312         if (win == NULL) {
7313                 if (ev->message_type == ewmh[_NET_ACTIVE_WINDOW].atom) {
7314                         DNPRINTF(SWM_D_EVENT, "clientmessage: request focus on "
7315                             "unmanaged window.\n");
7316                         e->xmaprequest.window = ev->window;
7317                         maprequest(e);
7318                 }
7319                 return;
7320         }
7321
7322         DNPRINTF(SWM_D_EVENT, "clientmessage: window: 0x%lx, type: %ld\n",
7323             ev->window, ev->message_type);
7324
7325         if (ev->message_type == ewmh[_NET_ACTIVE_WINDOW].atom) {
7326                 DNPRINTF(SWM_D_EVENT, "clientmessage: _NET_ACTIVE_WINDOW\n");
7327                 focus_win(win);
7328         }
7329         if (ev->message_type == ewmh[_NET_CLOSE_WINDOW].atom) {
7330                 DNPRINTF(SWM_D_EVENT, "clientmessage: _NET_CLOSE_WINDOW\n");
7331                 if (win->can_delete)
7332                         client_msg(win, adelete);
7333                 else
7334                         xcb_kill_client(conn, win->id);
7335         }
7336         if (ev->message_type == ewmh[_NET_MOVERESIZE_WINDOW].atom) {
7337                 DNPRINTF(SWM_D_EVENT,
7338                     "clientmessage: _NET_MOVERESIZE_WINDOW\n");
7339                 if (win->floating) {
7340                         if (ev->data.l[0] & (1<<8)) /* x */
7341                                 X(win) = ev->data.l[1];
7342                         if (ev->data.l[0] & (1<<9)) /* y */
7343                                 Y(win) = ev->data.l[2];
7344                         if (ev->data.l[0] & (1<<10)) /* width */
7345                                 WIDTH(win) = ev->data.l[3];
7346                         if (ev->data.l[0] & (1<<11)) /* height */
7347                                 HEIGHT(win) = ev->data.l[4];
7348
7349                         update_window(win);
7350                 }
7351                 else {
7352                         /* TODO: Change stack sizes */
7353                         /* notify no change was made. */
7354                         config_win(win, NULL);
7355                 }
7356         }
7357         if (ev->message_type == ewmh[_NET_WM_STATE].atom) {
7358                 DNPRINTF(SWM_D_EVENT, "clientmessage: _NET_WM_STATE\n");
7359                 ewmh_update_win_state(win, ev->data.l[1], ev->data.l[0]);
7360                 if (ev->data.l[2])
7361                         ewmh_update_win_state(win, ev->data.l[2],
7362                             ev->data.l[0]);
7363
7364                 stack();
7365         }
7366 }
7367
7368 int
7369 xerror_start(Display *d, XErrorEvent *ee)
7370 {
7371         other_wm = 1;
7372         return (-1);
7373 }
7374
7375 int
7376 xerror(Display *d, XErrorEvent *ee)
7377 {
7378         /* warnx("error: %p %p", display, ee); */
7379         return (-1);
7380 }
7381
7382 int
7383 active_wm(void)
7384 {
7385         other_wm = 0;
7386         xerrorxlib = XSetErrorHandler(xerror_start);
7387
7388         /* this causes an error if some other window manager is running */
7389         XSelectInput(display, DefaultRootWindow(display),
7390             SubstructureRedirectMask);
7391         do_sync();
7392         if (other_wm)
7393                 return (1);
7394
7395         XSetErrorHandler(xerror);
7396         do_sync();
7397         return (0);
7398 }
7399
7400 void
7401 new_region(struct swm_screen *s, int x, int y, int w, int h)
7402 {
7403         struct swm_region       *r, *n;
7404         struct workspace        *ws = NULL;
7405         int                     i;
7406
7407         DNPRINTF(SWM_D_MISC, "new region: screen[%d]:%dx%d+%d+%d\n",
7408              s->idx, w, h, x, y);
7409
7410         /* remove any conflicting regions */
7411         n = TAILQ_FIRST(&s->rl);
7412         while (n) {
7413                 r = n;
7414                 n = TAILQ_NEXT(r, entry);
7415                 if (X(r) < (x + w) &&
7416                     (X(r) + WIDTH(r)) > x &&
7417                     Y(r) < (y + h) &&
7418                     (Y(r) + HEIGHT(r)) > y) {
7419                         if (r->ws->r != NULL)
7420                                 r->ws->old_r = r->ws->r;
7421                         r->ws->r = NULL;
7422                         bar_cleanup(r);
7423                         TAILQ_REMOVE(&s->rl, r, entry);
7424                         TAILQ_INSERT_TAIL(&s->orl, r, entry);
7425                 }
7426         }
7427
7428         /* search old regions for one to reuse */
7429
7430         /* size + location match */
7431         TAILQ_FOREACH(r, &s->orl, entry)
7432                 if (X(r) == x && Y(r) == y &&
7433                     HEIGHT(r) == h && WIDTH(r) == w)
7434                         break;
7435
7436         /* size match */
7437         TAILQ_FOREACH(r, &s->orl, entry)
7438                 if (HEIGHT(r) == h && WIDTH(r) == w)
7439                         break;
7440
7441         if (r != NULL) {
7442                 TAILQ_REMOVE(&s->orl, r, entry);
7443                 /* try to use old region's workspace */
7444                 if (r->ws->r == NULL)
7445                         ws = r->ws;
7446         } else
7447                 if ((r = calloc(1, sizeof(struct swm_region))) == NULL)
7448                         err(1, "new_region: calloc: failed to allocate memory "
7449                             "for screen");
7450
7451         /* if we don't have a workspace already, find one */
7452         if (ws == NULL) {
7453                 for (i = 0; i < workspace_limit; i++)
7454                         if (s->ws[i].r == NULL) {
7455                                 ws = &s->ws[i];
7456                                 break;
7457                         }
7458         }
7459
7460         if (ws == NULL)
7461                 errx(1, "new_region: no free workspaces");
7462
7463         X(r) = x;
7464         Y(r) = y;
7465         WIDTH(r) = w;
7466         HEIGHT(r) = h;
7467         r->s = s;
7468         r->ws = ws;
7469         r->ws_prior = NULL;
7470         ws->r = r;
7471         outputs++;
7472         TAILQ_INSERT_TAIL(&s->rl, r, entry);
7473 }
7474
7475 void
7476 scan_xrandr(int i)
7477 {
7478 #ifdef SWM_XRR_HAS_CRTC
7479         int                                             c;
7480         int                                             ncrtc = 0;
7481 #endif /* SWM_XRR_HAS_CRTC */
7482         struct swm_region                               *r;
7483         int                                             num_screens;
7484         xcb_randr_get_screen_resources_current_cookie_t src;
7485         xcb_randr_get_screen_resources_current_reply_t  *srr;
7486         xcb_randr_get_crtc_info_cookie_t                cic;
7487         xcb_randr_get_crtc_info_reply_t                 *cir = NULL;
7488         xcb_randr_crtc_t                                *crtc;
7489         xcb_screen_t                                    *screen = get_screen(i);
7490
7491         num_screens = xcb_setup_roots_length(xcb_get_setup(conn));
7492         if (i >= num_screens)
7493                 errx(1, "scan_xrandr: invalid screen");
7494
7495         /* remove any old regions */
7496         while ((r = TAILQ_FIRST(&screens[i].rl)) != NULL) {
7497                 r->ws->old_r = r->ws->r = NULL;
7498                 bar_cleanup(r);
7499                 TAILQ_REMOVE(&screens[i].rl, r, entry);
7500                 TAILQ_INSERT_TAIL(&screens[i].orl, r, entry);
7501         }
7502         outputs = 0;
7503
7504         /* map virtual screens onto physical screens */
7505 #ifdef SWM_XRR_HAS_CRTC
7506         if (xrandr_support) {
7507                 src = xcb_randr_get_screen_resources_current(conn,
7508                         screens[i].root);
7509                 srr = xcb_randr_get_screen_resources_current_reply(conn, src,
7510                         NULL);
7511                 if (srr == NULL) {
7512                         new_region(&screens[i], 0, 0,
7513                             screen->width_in_pixels,
7514                             screen->height_in_pixels);
7515                         return;
7516                 } else
7517                         ncrtc = srr->num_crtcs;
7518                 for (c = 0; c < ncrtc; c++) {
7519                         crtc = xcb_randr_get_screen_resources_current_crtcs(srr);
7520                         cic = xcb_randr_get_crtc_info(conn, crtc[c],
7521                                 XCB_CURRENT_TIME);
7522                         cir = xcb_randr_get_crtc_info_reply(conn, cic, NULL);
7523                         if (cir == NULL)
7524                                 continue;
7525                         if (cir->num_outputs == 0) {
7526                                 free(cir);
7527                                 continue;
7528                         }
7529
7530                         if (cir->mode == 0)
7531                                 new_region(&screens[i], 0, 0,
7532                                     screen->width_in_pixels,
7533                                     screen->height_in_pixels);
7534                         else
7535                                 new_region(&screens[i],
7536                                     cir->x, cir->y, cir->width, cir->height);
7537                         free(cir);
7538                 }
7539                 free(srr);
7540         } else
7541 #endif /* SWM_XRR_HAS_CRTC */
7542         {
7543                 new_region(&screens[i], 0, 0, screen->width_in_pixels,
7544                     screen->height_in_pixels);
7545         }
7546 }
7547
7548 void
7549 screenchange(XEvent *e)
7550 {
7551         XRRScreenChangeNotifyEvent      *xe = (XRRScreenChangeNotifyEvent *)e;
7552         struct swm_region               *r;
7553         int                             i, num_screens;
7554
7555         DNPRINTF(SWM_D_EVENT, "screenchange: root: 0x%lx\n", xe->root);
7556
7557         if (!XRRUpdateConfiguration(e))
7558                 return;
7559
7560         num_screens = xcb_setup_roots_length(xcb_get_setup(conn));
7561         /* silly event doesn't include the screen index */
7562         for (i = 0; i < num_screens; i++)
7563                 if (screens[i].root == xe->root)
7564                         break;
7565         if (i >= num_screens)
7566                 errx(1, "screenchange: screen not found");
7567
7568         /* brute force for now, just re-enumerate the regions */
7569         scan_xrandr(i);
7570
7571         /* add bars to all regions */
7572         for (i = 0; i < num_screens; i++)
7573                 TAILQ_FOREACH(r, &screens[i].rl, entry)
7574                         bar_setup(r);
7575         stack();
7576         if (focus_mode == SWM_FOCUS_DEFAULT)
7577                 drain_enter_notify();
7578 }
7579
7580 void
7581 grab_windows(void)
7582 {
7583         xcb_window_t            *wins   = NULL;
7584         int                     no;
7585         int                     i, j, num_screens;
7586         uint16_t                state, manage;
7587
7588         xcb_query_tree_cookie_t                 qtc;
7589         xcb_query_tree_reply_t                  *qtr;
7590         xcb_get_window_attributes_cookie_t      c;
7591         xcb_get_window_attributes_reply_t       *r;
7592         xcb_get_property_cookie_t               pc;
7593
7594         num_screens = xcb_setup_roots_length(xcb_get_setup(conn));
7595         for (i = 0; i < num_screens; i++) {
7596                 qtc = xcb_query_tree(conn, screens[i].root);
7597                 qtr = xcb_query_tree_reply(conn, qtc, NULL);
7598                 if (!qtr)
7599                         continue;
7600                 wins = xcb_query_tree_children(qtr);
7601                 no = xcb_query_tree_children_length(qtr);
7602                 /* attach windows to a region */
7603                 /* normal windows */
7604                 for (j = 0; j < no; j++) {
7605                         c = xcb_get_window_attributes(conn, wins[j]);
7606                         r = xcb_get_window_attributes_reply(conn, c, NULL);
7607                         if (!r)
7608                                 continue;
7609                         if (r->override_redirect) {
7610                                 free(r);
7611                                 continue;
7612                         }
7613
7614                         pc = xcb_icccm_get_wm_transient_for(conn, wins[j]);
7615                         if (xcb_icccm_get_wm_transient_for_reply(conn, pc, &wins[j],
7616                                         NULL)) {
7617                                 free(r);
7618                                 continue;
7619                         }
7620
7621                         state = getstate(wins[j]);
7622                         manage = state == XCB_ICCCM_WM_STATE_ICONIC;
7623                         if (r->map_state == XCB_MAP_STATE_VIEWABLE || manage)
7624                                 manage_window(wins[j]);
7625                         free(r);
7626                 }
7627                 /* transient windows */
7628                 for (j = 0; j < no; j++) {
7629                         c = xcb_get_window_attributes(conn, wins[j]);
7630                         r = xcb_get_window_attributes_reply(conn, c, NULL);
7631                         if (!r)
7632                                 continue;
7633                         if (r->override_redirect) {
7634                                 free(r);
7635                                 continue;
7636                         }
7637                         free(r);
7638
7639                         state = getstate(wins[j]);
7640                         manage = state == XCB_ICCCM_WM_STATE_ICONIC;
7641                         pc = xcb_icccm_get_wm_transient_for(conn, wins[j]);
7642                         if (xcb_icccm_get_wm_transient_for_reply(conn, pc, &wins[j],
7643                                         NULL) && manage)
7644                                 manage_window(wins[j]);
7645                 }
7646                 free(qtr);
7647         }
7648 }
7649
7650 void
7651 setup_screens(void)
7652 {
7653         int                     i, j, k, num_screens;
7654         struct workspace        *ws;
7655         uint32_t                gcv[1], wa[1];
7656         const xcb_query_extension_reply_t *qep;
7657         xcb_cursor_t                            cursor;
7658         xcb_font_t                              cursor_font;
7659         xcb_randr_query_version_cookie_t        c;
7660         xcb_randr_query_version_reply_t         *r;
7661
7662         num_screens = xcb_setup_roots_length(xcb_get_setup(conn));
7663         if ((screens = calloc(num_screens,
7664              sizeof(struct swm_screen))) == NULL)
7665                 err(1, "setup_screens: calloc: failed to allocate memory for "
7666                     "screens");
7667
7668         /* initial Xrandr setup */
7669         xrandr_support = False;
7670         c = xcb_randr_query_version(conn, True, True);
7671         r = xcb_randr_query_version_reply(conn, c, NULL);
7672         if (r) {
7673                 if (r->major_version >= 1)
7674                         xrandr_support = True;
7675                 free(r);
7676         }
7677         qep = xcb_get_extension_data(conn, &xcb_randr_id);
7678         xrandr_eventbase = qep->first_event;
7679
7680         cursor_font = xcb_generate_id(conn);
7681         xcb_open_font(conn, cursor_font, strlen("cursor"), "cursor");
7682
7683         cursor = xcb_generate_id(conn);
7684         xcb_create_glyph_cursor(conn, cursor, cursor_font, cursor_font,
7685                 XC_left_ptr, XC_left_ptr + 1, 0, 0, 0, 0xffff, 0xffff, 0xffff);
7686         wa[0] = cursor;
7687
7688         /* map physical screens */
7689         for (i = 0; i < num_screens; i++) {
7690                 DNPRINTF(SWM_D_WS, "setup_screens: init screen: %d\n", i);
7691                 screens[i].idx = i;
7692                 TAILQ_INIT(&screens[i].rl);
7693                 TAILQ_INIT(&screens[i].orl);
7694                 screens[i].root = RootWindow(display, i);
7695
7696                 /* set default colors */
7697                 setscreencolor("red", i + 1, SWM_S_COLOR_FOCUS);
7698                 setscreencolor("rgb:88/88/88", i + 1, SWM_S_COLOR_UNFOCUS);
7699                 setscreencolor("rgb:00/80/80", i + 1, SWM_S_COLOR_BAR_BORDER);
7700                 setscreencolor("black", i + 1, SWM_S_COLOR_BAR);
7701                 setscreencolor("rgb:a0/a0/a0", i + 1, SWM_S_COLOR_BAR_FONT);
7702
7703                 /* create graphics context on screen */
7704                 screens[i].bar_gc = xcb_generate_id(conn);
7705                 gcv[0] = 0;
7706                 xcb_create_gc(conn, screens[i].bar_gc, screens[i].root,
7707                         XCB_GC_GRAPHICS_EXPOSURES, gcv);
7708
7709                 /* set default cursor */
7710                 xcb_change_window_attributes(conn, screens[i].root,
7711                         XCB_CW_CURSOR, wa);     
7712
7713                 /* init all workspaces */
7714                 /* XXX these should be dynamically allocated too */
7715                 for (j = 0; j < SWM_WS_MAX; j++) {
7716                         ws = &screens[i].ws[j];
7717                         ws->idx = j;
7718                         ws->name = NULL;
7719                         ws->focus = NULL;
7720                         ws->r = NULL;
7721                         ws->old_r = NULL;
7722                         TAILQ_INIT(&ws->winlist);
7723                         TAILQ_INIT(&ws->unmanagedlist);
7724
7725                         for (k = 0; layouts[k].l_stack != NULL; k++)
7726                                 if (layouts[k].l_config != NULL)
7727                                         layouts[k].l_config(ws,
7728                                             SWM_ARG_ID_STACKINIT);
7729                         ws->cur_layout = &layouts[0];
7730                         ws->cur_layout->l_string(ws);
7731                 }
7732
7733                 scan_xrandr(i);
7734
7735                 if (xrandr_support)
7736                         xcb_randr_select_input(conn, screens[i].root,
7737                                 XCB_RANDR_NOTIFY_MASK_SCREEN_CHANGE);
7738         }
7739         xcb_free_cursor(conn, cursor);
7740         xcb_close_font(conn, cursor_font);
7741 }
7742
7743 void
7744 setup_globals(void)
7745 {
7746         if ((bar_fonts = strdup(SWM_BAR_FONTS)) == NULL)
7747                 err(1, "setup_globals: strdup: failed to allocate memory.");
7748
7749         if ((clock_format = strdup("%a %b %d %R %Z %Y")) == NULL)
7750                 err(1, "setup_globals: strdup: failed to allocate memory.");
7751 }
7752
7753 void
7754 workaround(void)
7755 {
7756         int                     i, num_screens;
7757         xcb_atom_t              netwmcheck, netwmname, utf8_string;
7758         xcb_window_t            root, win;
7759         xcb_screen_t            *screen;
7760         uint32_t                wa[2];
7761
7762         /* work around sun jdk bugs, code from wmname */
7763         netwmcheck = get_atom_from_string("_NET_SUPPORTING_WM_CHECK");
7764         netwmname = get_atom_from_string("_NET_WM_NAME");
7765         utf8_string = get_atom_from_string("UTF8_STRING");
7766
7767         num_screens = xcb_setup_roots_length(xcb_get_setup(conn));
7768         for (i = 0; i < num_screens; i++) {
7769                 root = screens[i].root;
7770                 screen = get_screen(i);
7771
7772                 win = xcb_generate_id(conn);
7773                 wa[0] = screens[i].c[SWM_S_COLOR_UNFOCUS].color;
7774                 wa[1] = screens[i].c[SWM_S_COLOR_UNFOCUS].color;
7775                 xcb_create_window(conn, screen->root_depth, win, 0,
7776                         0, 0, 1, 1, 0, XCB_WINDOW_CLASS_INPUT_OUTPUT,
7777                         screen->root_visual,
7778                         XCB_CW_BACK_PIXEL | XCB_CW_BORDER_PIXEL, wa);
7779
7780                 xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root,
7781                         netwmcheck, XCB_ATOM_WINDOW, 32, 1, &win);
7782                 xcb_change_property(conn, XCB_PROP_MODE_REPLACE, win,
7783                         netwmcheck, XCB_ATOM_WINDOW, 32, 1, &win);
7784                 xcb_change_property(conn, XCB_PROP_MODE_REPLACE, win,
7785                         netwmname, utf8_string, 8, strlen("LG3D"), "LG3D");
7786         }
7787 }
7788
7789 int
7790 main(int argc, char *argv[])
7791 {
7792         struct swm_region       *r, *rr;
7793         struct ws_win           *winfocus = NULL;
7794         struct timeval          tv;
7795         union arg               a;
7796         char                    conf[PATH_MAX], *cfile = NULL;
7797         struct stat             sb;
7798         XEvent                  e;
7799         int                     xfd, i, num_screens;
7800         fd_set                  rd;
7801         struct sigaction        sact;
7802
7803         start_argv = argv;
7804         warnx("Welcome to spectrwm V%s Build: %s", SPECTRWM_VERSION, buildstr);
7805         if (!setlocale(LC_CTYPE, "") || !setlocale(LC_TIME, "") ||
7806             !XSupportsLocale())
7807                 warnx("no locale support");
7808
7809         if (!X_HAVE_UTF8_STRING)
7810                 warnx("no UTF-8 support");
7811
7812         if (!(display = XOpenDisplay(0)))
7813                 errx(1, "can not open display");
7814
7815         if (!(conn = XGetXCBConnection(display)))
7816                 errx(1, "can not get XCB connection");
7817
7818         if (active_wm())
7819                 errx(1, "other wm running");
7820
7821         /* handle some signals */
7822         bzero(&sact, sizeof(sact));
7823         sigemptyset(&sact.sa_mask);
7824         sact.sa_flags = 0;
7825         sact.sa_handler = sighdlr;
7826         sigaction(SIGINT, &sact, NULL);
7827         sigaction(SIGQUIT, &sact, NULL);
7828         sigaction(SIGTERM, &sact, NULL);
7829         sigaction(SIGHUP, &sact, NULL);
7830
7831         sact.sa_handler = sighdlr;
7832         sact.sa_flags = SA_NOCLDSTOP;
7833         sigaction(SIGCHLD, &sact, NULL);
7834
7835         astate = get_atom_from_string("WM_STATE");
7836         aprot = get_atom_from_string("WM_PROTOCOLS");
7837         adelete = get_atom_from_string("WM_DELETE_WINDOW");
7838         takefocus = get_atom_from_string("WM_TAKE_FOCUS");
7839         a_wmname = get_atom_from_string("WM_NAME");
7840         a_netwmname = get_atom_from_string("_NET_WM_NAME");
7841         a_utf8_string = get_atom_from_string("UTF8_STRING");
7842         a_string = get_atom_from_string("STRING");
7843         a_swm_iconic = get_atom_from_string("_SWM_ICONIC");
7844
7845         /* look for local and global conf file */
7846         pwd = getpwuid(getuid());
7847         if (pwd == NULL)
7848                 errx(1, "invalid user: %d", getuid());
7849
7850         syms = xcb_key_symbols_alloc(conn);
7851         if (syms == NULL)
7852                 errx(1, "unable to allocate key symbols");
7853
7854         setup_globals();
7855         setup_screens();
7856         setup_keys();
7857         setup_quirks();
7858         setup_spawn();
7859
7860         /* load config */
7861         for (i = 0; ; i++) {
7862                 conf[0] = '\0';
7863                 switch (i) {
7864                 case 0:
7865                         /* ~ */
7866                         snprintf(conf, sizeof conf, "%s/.%s",
7867                             pwd->pw_dir, SWM_CONF_FILE);
7868                         break;
7869                 case 1:
7870                         /* global */
7871                         snprintf(conf, sizeof conf, "/etc/%s",
7872                             SWM_CONF_FILE);
7873                         break;
7874                 case 2:
7875                         /* ~ compat */
7876                         snprintf(conf, sizeof conf, "%s/.%s",
7877                             pwd->pw_dir, SWM_CONF_FILE_OLD);
7878                         break;
7879                 case 3:
7880                         /* global compat */
7881                         snprintf(conf, sizeof conf, "/etc/%s",
7882                             SWM_CONF_FILE_OLD);
7883                         break;
7884                 default:
7885                         goto noconfig;
7886                 }
7887
7888                 if (strlen(conf) && stat(conf, &sb) != -1)
7889                         if (S_ISREG(sb.st_mode)) {
7890                                 cfile = conf;
7891                                 break;
7892                         }
7893         }
7894 noconfig:
7895
7896         /* load conf (if any) */
7897         if (cfile)
7898                 conf_load(cfile, SWM_CONF_DEFAULT);
7899
7900         setup_ewmh();
7901         /* set some values to work around bad programs */
7902         workaround();
7903         /* grab existing windows (before we build the bars) */
7904         grab_windows();
7905
7906         if (getenv("SWM_STARTED") == NULL)
7907                 setenv("SWM_STARTED", "YES", 1);
7908
7909         /* setup all bars */
7910         num_screens = xcb_setup_roots_length(xcb_get_setup(conn));
7911         for (i = 0; i < num_screens; i++)
7912                 TAILQ_FOREACH(r, &screens[i].rl, entry) {
7913                         if (winfocus == NULL)
7914                                 winfocus = TAILQ_FIRST(&r->ws->winlist);
7915                         bar_setup(r);
7916                 }
7917
7918         unfocus_all();
7919
7920         grabkeys();
7921         stack();
7922         if (focus_mode == SWM_FOCUS_DEFAULT)
7923                 drain_enter_notify();
7924
7925         xfd = xcb_get_file_descriptor(conn);
7926         while (running) {
7927                 while (XPending(display)) {
7928                         XNextEvent(display, &e);
7929                         if (running == 0)
7930                                 goto done;
7931                         if (e.type < LASTEvent) {
7932                                 DNPRINTF(SWM_D_EVENTQ ,"XEvent: handled: %s, "
7933                                     "window: 0x%lx, type: %s (%d), %d remaining"
7934                                     "\n", YESNO(handler[e.type]),
7935                                     e.xany.window, geteventname(&e),
7936                                     e.type, QLength(display));
7937
7938                                 if (handler[e.type])
7939                                         handler[e.type](&e);
7940                         } else {
7941                                 DNPRINTF(SWM_D_EVENTQ, "XRandr Event: window: "
7942                                     "0x%lx, type: %s (%d)\n", e.xany.window,
7943                                     xrandr_geteventname(&e), e.type);
7944
7945                                 switch (e.type - xrandr_eventbase) {
7946                                 case XCB_RANDR_SCREEN_CHANGE_NOTIFY:
7947                                         screenchange(&e);
7948                                         break;
7949                                 default:
7950                                         break;
7951                                 }
7952                         }
7953                 }
7954
7955                 /* if we are being restarted go focus on first window */
7956                 if (winfocus) {
7957                         rr = winfocus->ws->r;
7958                         if (rr == NULL) {
7959                                 /* not a visible window */
7960                                 winfocus = NULL;
7961                                 continue;
7962                         }
7963                         /* move pointer to first screen if multi screen */
7964                         if (num_screens > 1 || outputs > 1)
7965                                 xcb_warp_pointer(conn, XCB_WINDOW_NONE,
7966                                         rr->s[0].root, 0, 0, 0, 0, X(rr),
7967                                         Y(rr) + (bar_enabled ? bar_height : 0));
7968
7969                         a.id = SWM_ARG_ID_FOCUSCUR;
7970                         focus(rr, &a);
7971                         winfocus = NULL;
7972                         continue;
7973                 }
7974
7975                 FD_ZERO(&rd);
7976                 FD_SET(xfd, &rd);
7977                 tv.tv_sec = 1;
7978                 tv.tv_usec = 0;
7979                 if (select(xfd + 1, &rd, NULL, NULL, &tv) == -1)
7980                         if (errno != EINTR)
7981                                 DNPRINTF(SWM_D_MISC, "select failed");
7982                 if (restart_wm == 1)
7983                         restart(NULL, NULL);
7984                 if (search_resp == 1)
7985                         search_do_resp();
7986                 if (running == 0)
7987                         goto done;
7988                 if (bar_alarm) {
7989                         bar_alarm = 0;
7990                         bar_update();
7991                 }
7992         }
7993 done:
7994         teardown_ewmh();
7995         bar_extra_stop();
7996
7997         for (i = 0; i < num_screens; ++i)
7998                 if (screens[i].bar_gc != 0)
7999                         xcb_free_gc(conn, screens[i].bar_gc);
8000         XFreeFontSet(display, bar_fs);
8001         
8002         xcb_key_symbols_free(syms);
8003         xcb_disconnect(conn);
8004
8005         return (0);
8006 }