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