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