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