JasonWoof Got questions, comments, patches, etc.? Contact Jason Woofenden
Fix typo.
[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;
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         TAILQ_FOREACH(w, &ws->winlist, entry) {
4471                 if (w->transient || w->iconic)
4472                         continue;
4473
4474                 if (!w->mapped && w != win)
4475                         map_window(w);
4476
4477                 if (w->floating && !w->floatmaxed) {
4478                         /*
4479                          * retain geometry for retrieval on exit from
4480                          * max_stack mode
4481                          */
4482                         store_float_geom(w, ws->r);
4483                         w->floatmaxed = 1;
4484                 }
4485
4486                 /* only reconfigure if necessary */
4487                 if (X(w) != gg.x || Y(w) != gg.y || WIDTH(w) != gg.w ||
4488                     HEIGHT(w) != gg.h) {
4489                         w->g = gg;
4490                         if (bar_enabled && ws->bar_enabled){
4491                                 w->bordered = 1;
4492                         } else {
4493                                 w->bordered = 0;
4494                                 WIDTH(w) += 2 * border_width;
4495                                 HEIGHT(w) += 2 * border_width;
4496                         }
4497
4498                         update_window(w);
4499                 }
4500         }
4501
4502         /* If a parent exists, map/raise it first. */
4503         if (parent) {
4504                 map_window(parent);
4505
4506                 /* Map siblings next. */
4507                 TAILQ_FOREACH(w, &ws->winlist, entry)
4508                         if (w != win && !w->iconic &&
4509                             w->transient == parent->id) {
4510                                 stack_floater(w, ws->r);
4511                                 map_window(w);
4512                         }
4513         }
4514
4515         /* Map/raise focused window. */
4516         map_window(win);
4517
4518         /* Finally, map/raise children of focus window. */
4519         TAILQ_FOREACH(w, &ws->winlist, entry)
4520                 if (w->transient == win->id && !w->iconic) {
4521                         stack_floater(w, ws->r);
4522                         map_window(w);
4523                 }
4524 }
4525
4526 void
4527 send_to_rg(struct swm_region *r, union arg *args)
4528 {
4529         int                     ridx = args->id, i, num_screens;
4530         struct swm_region       *rr = NULL;
4531         union arg               a;
4532
4533         num_screens = xcb_setup_roots_length(xcb_get_setup(conn));
4534         /* do nothing if we don't have more than one screen */
4535         if (!(num_screens > 1 || outputs > 1))
4536                 return;
4537
4538         DNPRINTF(SWM_D_FOCUS, "send_to_rg: id: %d\n", ridx);
4539
4540         rr = TAILQ_FIRST(&r->s->rl);
4541         for (i = 0; i < ridx; ++i)
4542                 rr = TAILQ_NEXT(rr, entry);
4543
4544         if (rr == NULL)
4545                 return;
4546
4547         a.id = rr->ws->idx;
4548
4549         send_to_ws(r, &a);
4550 }
4551
4552 void
4553 send_to_ws(struct swm_region *r, union arg *args)
4554 {
4555         int                     wsid = args->id;
4556         struct ws_win           *win = NULL, *parent;
4557         struct workspace        *ws, *nws, *pws;
4558         char                    ws_idx_str[SWM_PROPLEN];
4559
4560         if (wsid >= workspace_limit)
4561                 return;
4562
4563         if (r && r->ws && r->ws->focus)
4564                 win = r->ws->focus;
4565         else
4566                 return;
4567
4568         if (win->ws->idx == wsid)
4569                 return;
4570
4571         DNPRINTF(SWM_D_MOVE, "send_to_ws: win 0x%x, ws %d -> %d\n", win->id,
4572             win->ws->idx, wsid);
4573
4574         ws = win->ws;
4575         nws = &win->s->ws[wsid];
4576
4577         /* Update the window's workspace property: _SWM_WS */
4578         if (snprintf(ws_idx_str, SWM_PROPLEN, "%d", nws->idx) < SWM_PROPLEN) {
4579                 if (focus_mode != SWM_FOCUS_FOLLOW)
4580                         ws->focus_pending = get_focus_prev(win);
4581
4582                 /* Move the parent if this is a transient window. */
4583                 if (win->transient) {
4584                         parent = find_window(win->transient);
4585                         if (parent) {
4586                                 pws = parent->ws;
4587                                 /* Set new focus in parent's ws if needed. */
4588                                 if (pws->focus == parent) {
4589                                         if (focus_mode != SWM_FOCUS_FOLLOW)
4590                                                 pws->focus_pending =
4591                                                     get_focus_prev(parent);
4592
4593                                         unfocus_win(parent);
4594
4595                                         if (focus_mode != SWM_FOCUS_FOLLOW)
4596                                                 pws->focus = pws->focus_pending;
4597
4598                                         if (focus_mode != SWM_FOCUS_FOLLOW)
4599                                                 pws->focus_pending = NULL;
4600                                 }
4601
4602                                 /* Don't unmap parent if new ws is visible */
4603                                 if (nws->r == NULL)
4604                                         unmap_window(parent);
4605
4606                                 /* Transfer */
4607                                 TAILQ_REMOVE(&ws->winlist, parent, entry);
4608                                 TAILQ_INSERT_TAIL(&nws->winlist, parent, entry);
4609                                 parent->ws = nws;
4610
4611                                 DNPRINTF(SWM_D_PROP, "send_to_ws: set "
4612                                     "property: _SWM_WS: %s\n", ws_idx_str);
4613                                 xcb_change_property(conn, XCB_PROP_MODE_REPLACE,
4614                                     parent->id, a_swm_ws, XCB_ATOM_STRING, 8,
4615                                     strlen(ws_idx_str), ws_idx_str);
4616                         }
4617                 }
4618
4619                 unfocus_win(win);
4620
4621                 /* Don't unmap if new ws is visible */
4622                 if (nws->r == NULL)
4623                         unmap_window(win);
4624
4625                 /* Transfer */
4626                 TAILQ_REMOVE(&ws->winlist, win, entry);
4627                 TAILQ_INSERT_TAIL(&nws->winlist, win, entry);
4628                 win->ws = nws;
4629
4630                 /* Set focus on new ws. */
4631                 unfocus_win(nws->focus);
4632                 nws->focus = win;
4633
4634                 DNPRINTF(SWM_D_PROP, "send_to_ws: set property: _SWM_WS: %s\n",
4635                     ws_idx_str);
4636                 xcb_change_property(conn, XCB_PROP_MODE_REPLACE, win->id,
4637                     a_swm_ws, XCB_ATOM_STRING, 8, strlen(ws_idx_str),
4638                     ws_idx_str);
4639
4640                 /* Restack and set new focus. */
4641                 stack();
4642
4643                 if (focus_mode != SWM_FOCUS_FOLLOW) {
4644                         focus_win(ws->focus_pending);
4645                         ws->focus_pending = NULL;
4646                 }
4647
4648                 focus_flush();
4649         }
4650
4651         DNPRINTF(SWM_D_MOVE, "send_to_ws: done.\n");
4652 }
4653
4654 void
4655 pressbutton(struct swm_region *r, union arg *args)
4656 {
4657         /* suppress unused warning since var is needed */
4658         (void)r;
4659
4660         xcb_test_fake_input(conn, XCB_BUTTON_PRESS, args->id,
4661             XCB_CURRENT_TIME, XCB_WINDOW_NONE, 0, 0, 0);
4662         xcb_test_fake_input(conn, XCB_BUTTON_RELEASE, args->id,
4663             XCB_CURRENT_TIME, XCB_WINDOW_NONE, 0, 0, 0);
4664 }
4665
4666 void
4667 raise_toggle(struct swm_region *r, union arg *args)
4668 {
4669         /* suppress unused warning since var is needed */
4670         (void)args;
4671
4672         if (r == NULL || r->ws == NULL)
4673                 return;
4674
4675         r->ws->always_raise = !r->ws->always_raise;
4676
4677         /* bring floaters back to top */
4678         if (!r->ws->always_raise)
4679                 stack();
4680
4681         focus_flush();
4682 }
4683
4684 void
4685 iconify(struct swm_region *r, union arg *args)
4686 {
4687         /* suppress unused warning since var is needed */
4688         (void)args;
4689
4690         if (r->ws->focus == NULL)
4691                 return;
4692
4693         set_swm_iconic(r->ws->focus, 1);
4694
4695         xcb_flush(conn);
4696 }
4697
4698 char *
4699 get_win_name(xcb_window_t win)
4700 {
4701         char                            *name = NULL;
4702         xcb_get_property_cookie_t       c;
4703         xcb_get_property_reply_t        *r;
4704
4705         /* First try _NET_WM_NAME for UTF-8. */
4706         c = xcb_get_property(conn, 0, win, a_netwmname,
4707             XCB_GET_PROPERTY_TYPE_ANY, 0, UINT_MAX);
4708         r = xcb_get_property_reply(conn, c, NULL);
4709
4710         if (r) {
4711                 if (r->type == XCB_NONE) {
4712                         free(r);
4713                         /* Use WM_NAME instead; no UTF-8. */
4714                         c = xcb_get_property(conn, 0, win, XCB_ATOM_WM_NAME,
4715                                 XCB_GET_PROPERTY_TYPE_ANY, 0, UINT_MAX);
4716                         r = xcb_get_property_reply(conn, c, NULL);
4717
4718                         if (!r)
4719                                 return (NULL);
4720                         if (r->type == XCB_NONE) {
4721                                 free(r);
4722                                 return (NULL);
4723                         }
4724                 }
4725                 if (r->length > 0)
4726                         name = strndup(xcb_get_property_value(r),
4727                                    xcb_get_property_value_length(r));
4728
4729                 free(r);
4730         }
4731
4732         return (name);
4733 }
4734
4735 void
4736 uniconify(struct swm_region *r, union arg *args)
4737 {
4738         struct ws_win           *win;
4739         FILE                    *lfile;
4740         char                    *name;
4741         int                     count = 0;
4742
4743         DNPRINTF(SWM_D_MISC, "uniconify\n");
4744
4745         if (r == NULL || r->ws == NULL)
4746                 return;
4747
4748         /* make sure we have anything to uniconify */
4749         TAILQ_FOREACH(win, &r->ws->winlist, entry) {
4750                 if (win->ws == NULL)
4751                         continue; /* should never happen */
4752                 if (!win->iconic)
4753                         continue;
4754                 count++;
4755         }
4756         if (count == 0)
4757                 return;
4758
4759         search_r = r;
4760         search_resp_action = SWM_SEARCH_UNICONIFY;
4761
4762         spawn_select(r, args, "search", &searchpid);
4763
4764         if ((lfile = fdopen(select_list_pipe[1], "w")) == NULL)
4765                 return;
4766
4767         TAILQ_FOREACH(win, &r->ws->winlist, entry) {
4768                 if (win->ws == NULL)
4769                         continue; /* should never happen */
4770                 if (!win->iconic)
4771                         continue;
4772
4773                 name = get_win_name(win->id);
4774                 if (name == NULL)
4775                         continue;
4776                 fprintf(lfile, "%s.%u\n", name, win->id);
4777                 free(name);
4778         }
4779
4780         fclose(lfile);
4781 }
4782
4783 void
4784 name_workspace(struct swm_region *r, union arg *args)
4785 {
4786         FILE                    *lfile;
4787
4788         DNPRINTF(SWM_D_MISC, "name_workspace\n");
4789
4790         if (r == NULL)
4791                 return;
4792
4793         search_r = r;
4794         search_resp_action = SWM_SEARCH_NAME_WORKSPACE;
4795
4796         spawn_select(r, args, "name_workspace", &searchpid);
4797
4798         if ((lfile = fdopen(select_list_pipe[1], "w")) == NULL)
4799                 return;
4800
4801         fprintf(lfile, "%s", "");
4802         fclose(lfile);
4803 }
4804
4805 void
4806 search_workspace(struct swm_region *r, union arg *args)
4807 {
4808         int                     i;
4809         struct workspace        *ws;
4810         FILE                    *lfile;
4811
4812         DNPRINTF(SWM_D_MISC, "search_workspace\n");
4813
4814         if (r == NULL)
4815                 return;
4816
4817         search_r = r;
4818         search_resp_action = SWM_SEARCH_SEARCH_WORKSPACE;
4819
4820         spawn_select(r, args, "search", &searchpid);
4821
4822         if ((lfile = fdopen(select_list_pipe[1], "w")) == NULL)
4823                 return;
4824
4825         for (i = 0; i < workspace_limit; i++) {
4826                 ws = &r->s->ws[i];
4827                 if (ws == NULL)
4828                         continue;
4829                 fprintf(lfile, "%d%s%s\n", ws->idx + 1,
4830                     (ws->name ? ":" : ""), (ws->name ? ws->name : ""));
4831         }
4832
4833         fclose(lfile);
4834 }
4835
4836 void
4837 search_win_cleanup(void)
4838 {
4839         struct search_window    *sw = NULL;
4840
4841         while ((sw = TAILQ_FIRST(&search_wl)) != NULL) {
4842                 xcb_destroy_window(conn, sw->indicator);
4843                 TAILQ_REMOVE(&search_wl, sw, entry);
4844                 free(sw);
4845         }
4846 }
4847
4848 void
4849 search_win(struct swm_region *r, union arg *args)
4850 {
4851         struct ws_win           *win = NULL;
4852         struct search_window    *sw = NULL;
4853         xcb_window_t            w;
4854         uint32_t                wa[2];
4855         int                     i, width, height;
4856         char                    s[8];
4857         FILE                    *lfile;
4858         size_t                  len;
4859         XftDraw                 *draw;
4860         XGlyphInfo              info;
4861         GC                      l_draw;
4862         XGCValues               l_gcv;
4863         XRectangle              l_ibox, l_lbox;
4864
4865         DNPRINTF(SWM_D_MISC, "search_win\n");
4866
4867         search_r = r;
4868         search_resp_action = SWM_SEARCH_SEARCH_WINDOW;
4869
4870         spawn_select(r, args, "search", &searchpid);
4871
4872         if ((lfile = fdopen(select_list_pipe[1], "w")) == NULL)
4873                 return;
4874
4875         TAILQ_INIT(&search_wl);
4876
4877         i = 1;
4878         TAILQ_FOREACH(win, &r->ws->winlist, entry) {
4879                 if (win->iconic)
4880                         continue;
4881
4882                 sw = calloc(1, sizeof(struct search_window));
4883                 if (sw == NULL) {
4884                         warn("search_win: calloc");
4885                         fclose(lfile);
4886                         search_win_cleanup();
4887                         return;
4888                 }
4889                 sw->idx = i;
4890                 sw->win = win;
4891
4892                 snprintf(s, sizeof s, "%d", i);
4893                 len = strlen(s);
4894
4895                 w = xcb_generate_id(conn);
4896                 wa[0] = r->s->c[SWM_S_COLOR_FOCUS].pixel;
4897                 wa[1] = r->s->c[SWM_S_COLOR_UNFOCUS].pixel;
4898
4899                 if (bar_font_legacy) {
4900                         XmbTextExtents(bar_fs, s, len, &l_ibox, &l_lbox);
4901                         width = l_lbox.width + 4;
4902                         height = bar_fs_extents->max_logical_extent.height + 4;
4903                 } else {
4904                         XftTextExtentsUtf8(display, bar_font, (FcChar8 *)s, len,
4905                             &info);
4906                         width = info.width + 4;
4907                         height = bar_font->height + 4;
4908                 }
4909
4910                 xcb_create_window(conn, XCB_COPY_FROM_PARENT, w, win->id, 0, 0,
4911                     width, height, 1, XCB_WINDOW_CLASS_INPUT_OUTPUT,
4912                     XCB_COPY_FROM_PARENT, XCB_CW_BACK_PIXEL |
4913                     XCB_CW_BORDER_PIXEL, wa);
4914
4915                 xcb_map_window(conn, w);
4916
4917                 sw->indicator = w;
4918                 TAILQ_INSERT_TAIL(&search_wl, sw, entry);
4919
4920                 if (bar_font_legacy) {
4921                         l_gcv.graphics_exposures = 0;
4922                         l_draw = XCreateGC(display, w, 0, &l_gcv);
4923
4924                         XSetForeground(display, l_draw,
4925                                 r->s->c[SWM_S_COLOR_BAR].pixel);
4926
4927                         DRAWSTRING(display, w, bar_fs, l_draw, 2,
4928                             (bar_fs_extents->max_logical_extent.height -
4929                             l_lbox.height) / 2 - l_lbox.y, s, len);
4930
4931                         XFreeGC(display, l_draw);
4932                 } else {
4933
4934                         draw = XftDrawCreate(display, w,
4935                             DefaultVisual(display, r->s->idx),
4936                             DefaultColormap(display, r->s->idx));
4937
4938                         XftDrawStringUtf8(draw, &bar_font_color, bar_font, 2,
4939                             (HEIGHT(r->bar) + bar_font->height) / 2 -
4940                             bar_font->descent, (FcChar8 *)s, len);
4941
4942                         XftDrawDestroy(draw);
4943                 }
4944
4945                 DNPRINTF(SWM_D_MISC, "search_win: mapped window: 0x%x\n", w);
4946
4947                 fprintf(lfile, "%d\n", i);
4948                 i++;
4949         }
4950
4951         fclose(lfile);
4952
4953         xcb_flush(conn);
4954 }
4955
4956 void
4957 search_resp_uniconify(const char *resp, unsigned long len)
4958 {
4959         char                    *name;
4960         struct ws_win           *win;
4961         char                    *s;
4962
4963         DNPRINTF(SWM_D_MISC, "search_resp_uniconify: resp: %s\n", resp);
4964
4965         TAILQ_FOREACH(win, &search_r->ws->winlist, entry) {
4966                 if (!win->iconic)
4967                         continue;
4968                 name = get_win_name(win->id);
4969                 if (name == NULL)
4970                         continue;
4971                 if (asprintf(&s, "%s.%u", name, win->id) == -1) {
4972                         free(name);
4973                         continue;
4974                 }
4975                 free(name);
4976                 if (strncmp(s, resp, len) == 0) {
4977                         /* XXX this should be a callback to generalize */
4978                         set_swm_iconic(win, 0);
4979                         free(s);
4980                         break;
4981                 }
4982                 free(s);
4983         }
4984 }
4985
4986 void
4987 search_resp_name_workspace(const char *resp, unsigned long len)
4988 {
4989         struct workspace        *ws;
4990
4991         DNPRINTF(SWM_D_MISC, "search_resp_name_workspace: resp: %s\n", resp);
4992
4993         if (search_r->ws == NULL)
4994                 return;
4995         ws = search_r->ws;
4996
4997         if (ws->name) {
4998                 free(search_r->ws->name);
4999                 search_r->ws->name = NULL;
5000         }
5001
5002         if (len > 1) {
5003                 ws->name = strdup(resp);
5004                 if (ws->name == NULL) {
5005                         DNPRINTF(SWM_D_MISC, "search_resp_name_workspace: "
5006                             "strdup: %s", strerror(errno));
5007                         return;
5008                 }
5009         }
5010 }
5011
5012 void
5013 search_resp_search_workspace(const char *resp)
5014 {
5015         char                    *p, *q;
5016         int                     ws_idx;
5017         const char              *errstr;
5018         union arg               a;
5019
5020         DNPRINTF(SWM_D_MISC, "search_resp_search_workspace: resp: %s\n", resp);
5021
5022         q = strdup(resp);
5023         if (!q) {
5024                 DNPRINTF(SWM_D_MISC, "search_resp_search_workspace: strdup: %s",
5025                     strerror(errno));
5026                 return;
5027         }
5028         p = strchr(q, ':');
5029         if (p != NULL)
5030                 *p = '\0';
5031         ws_idx = (int)strtonum(q, 1, workspace_limit, &errstr);
5032         if (errstr) {
5033                 DNPRINTF(SWM_D_MISC, "workspace idx is %s: %s",
5034                     errstr, q);
5035                 free(q);
5036                 return;
5037         }
5038         free(q);
5039         a.id = ws_idx - 1;
5040         switchws(search_r, &a);
5041 }
5042
5043 void
5044 search_resp_search_window(const char *resp)
5045 {
5046         char                    *s;
5047         int                     idx;
5048         const char              *errstr;
5049         struct search_window    *sw;
5050
5051         DNPRINTF(SWM_D_MISC, "search_resp_search_window: resp: %s\n", resp);
5052
5053         s = strdup(resp);
5054         if (!s) {
5055                 DNPRINTF(SWM_D_MISC, "search_resp_search_window: strdup: %s",
5056                     strerror(errno));
5057                 return;
5058         }
5059
5060         idx = (int)strtonum(s, 1, INT_MAX, &errstr);
5061         if (errstr) {
5062                 DNPRINTF(SWM_D_MISC, "window idx is %s: %s",
5063                     errstr, s);
5064                 free(s);
5065                 return;
5066         }
5067         free(s);
5068
5069         TAILQ_FOREACH(sw, &search_wl, entry)
5070                 if (idx == sw->idx) {
5071                         focus_win(sw->win);
5072                         break;
5073                 }
5074 }
5075
5076 #define MAX_RESP_LEN    1024
5077
5078 void
5079 search_do_resp(void)
5080 {
5081         ssize_t                 rbytes;
5082         char                    *resp;
5083         unsigned long           len;
5084
5085         DNPRINTF(SWM_D_MISC, "search_do_resp:\n");
5086
5087         search_resp = 0;
5088         searchpid = 0;
5089
5090         if ((resp = calloc(1, MAX_RESP_LEN + 1)) == NULL) {
5091                 warn("search: calloc");
5092                 goto done;
5093         }
5094
5095         rbytes = read(select_resp_pipe[0], resp, MAX_RESP_LEN);
5096         if (rbytes <= 0) {
5097                 warn("search: read error");
5098                 goto done;
5099         }
5100         resp[rbytes] = '\0';
5101
5102         /* XXX:
5103          * Older versions of dmenu (Atleast pre 4.4.1) do not send a
5104          * newline, so work around that by sanitizing the resp now.
5105          */
5106         resp[strcspn(resp, "\n")] = '\0';
5107         len = strlen(resp);
5108
5109         switch (search_resp_action) {
5110         case SWM_SEARCH_UNICONIFY:
5111                 search_resp_uniconify(resp, len);
5112                 break;
5113         case SWM_SEARCH_NAME_WORKSPACE:
5114                 search_resp_name_workspace(resp, len);
5115                 break;
5116         case SWM_SEARCH_SEARCH_WORKSPACE:
5117                 search_resp_search_workspace(resp);
5118                 break;
5119         case SWM_SEARCH_SEARCH_WINDOW:
5120                 search_resp_search_window(resp);
5121                 break;
5122         }
5123
5124 done:
5125         if (search_resp_action == SWM_SEARCH_SEARCH_WINDOW)
5126                 search_win_cleanup();
5127
5128         search_resp_action = SWM_SEARCH_NONE;
5129         close(select_resp_pipe[0]);
5130         free(resp);
5131
5132         xcb_flush(conn);
5133 }
5134
5135 void
5136 wkill(struct swm_region *r, union arg *args)
5137 {
5138         DNPRINTF(SWM_D_MISC, "wkill: id: %d\n", args->id);
5139
5140         if (r->ws->focus == NULL)
5141                 return;
5142
5143         if (args->id == SWM_ARG_ID_KILLWINDOW)
5144                 xcb_kill_client(conn, r->ws->focus->id);
5145         else
5146                 if (r->ws->focus->can_delete)
5147                         client_msg(r->ws->focus, a_delete, 0);
5148
5149         focus_flush();
5150 }
5151
5152 int
5153 floating_toggle_win(struct ws_win *win)
5154 {
5155         struct swm_region       *r;
5156
5157         if (win == NULL)
5158                 return (0);
5159
5160         if (!win->ws->r)
5161                 return (0);
5162
5163         r = win->ws->r;
5164
5165         /* reject floating toggles in max stack mode */
5166         if (win->ws->cur_layout == &layouts[SWM_MAX_STACK])
5167                 return (0);
5168
5169         if (win->floating) {
5170                 if (!win->floatmaxed) {
5171                         /* retain position for refloat */
5172                         store_float_geom(win, r);
5173                 }
5174                 win->floating = 0;
5175         } else {
5176                 load_float_geom(win, r);
5177                 win->floating = 1;
5178         }
5179
5180         ewmh_update_actions(win);
5181
5182         return (1);
5183 }
5184
5185 void
5186 floating_toggle(struct swm_region *r, union arg *args)
5187 {
5188         struct ws_win           *win = r->ws->focus;
5189
5190         /* suppress unused warning since var is needed */
5191         (void)args;
5192
5193         if (win == NULL)
5194                 return;
5195
5196         if (win->ewmh_flags & EWMH_F_FULLSCREEN)
5197                 return;
5198
5199         ewmh_update_win_state(win, ewmh[_NET_WM_STATE_ABOVE].atom,
5200             _NET_WM_STATE_TOGGLE);
5201
5202         stack();
5203
5204         if (win == win->ws->focus)
5205                 focus_win(win);
5206
5207         focus_flush();
5208 }
5209
5210 void
5211 constrain_window(struct ws_win *win, struct swm_region *r, int resizable)
5212 {
5213         if (MAX_X(win) + BORDER(win) > MAX_X(r)) {
5214                 if (resizable)
5215                         WIDTH(win) = MAX_X(r) - X(win) - BORDER(win);
5216                 else
5217                         X(win) = MAX_X(r)- WIDTH(win) - BORDER(win);
5218         }
5219
5220         if (X(win) + BORDER(win) < X(r)) {
5221                 if (resizable)
5222                         WIDTH(win) -= X(r) - X(win) - BORDER(win);
5223
5224                 X(win) = X(r) - BORDER(win);
5225         }
5226
5227         if (MAX_Y(win) + BORDER(win) > MAX_Y(r)) {
5228                 if (resizable)
5229                         HEIGHT(win) = MAX_Y(r) - Y(win) - BORDER(win);
5230                 else
5231                         Y(win) = MAX_Y(r) - HEIGHT(win) - BORDER(win);
5232         }
5233
5234         if (Y(win) + BORDER(win) < Y(r)) {
5235                 if (resizable)
5236                         HEIGHT(win) -= Y(r) - Y(win) - BORDER(win);
5237
5238                 Y(win) = Y(r) - BORDER(win);
5239         }
5240
5241         if (resizable) {
5242                 if (WIDTH(win) < 1)
5243                         WIDTH(win) = 1;
5244                 if (HEIGHT(win) < 1)
5245                         HEIGHT(win) = 1;
5246         }
5247 }
5248
5249 void
5250 update_window(struct ws_win *win)
5251 {
5252         uint16_t        mask;
5253         uint32_t        wc[5];
5254
5255         mask = XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y |
5256             XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT |
5257             XCB_CONFIG_WINDOW_BORDER_WIDTH;
5258         wc[0] = X(win);
5259         wc[1] = Y(win);
5260         wc[2] = WIDTH(win);
5261         wc[3] = HEIGHT(win);
5262         wc[4] = BORDER(win);
5263
5264         DNPRINTF(SWM_D_EVENT, "update_window: window: 0x%x, (x,y) w x h: "
5265             "(%d,%d) %d x %d, bordered: %s\n", win->id, wc[0], wc[1], wc[2],
5266             wc[3], YESNO(win->bordered));
5267
5268         xcb_configure_window(conn, win->id, mask, wc);
5269 }
5270
5271 #define SWM_RESIZE_STEPS        (50)
5272
5273 void
5274 resize(struct ws_win *win, union arg *args)
5275 {
5276         xcb_timestamp_t         timestamp = 0;
5277         struct swm_region       *r = NULL;
5278         int                     resize_stp = 0;
5279         struct swm_geometry     g;
5280         int                     top = 0, left = 0, resizing;
5281         int                     dx, dy;
5282         xcb_cursor_t                    cursor;
5283         xcb_query_pointer_reply_t       *xpr;
5284         xcb_generic_event_t             *evt;
5285         xcb_motion_notify_event_t       *mne;
5286
5287         if (win == NULL)
5288                 return;
5289         r = win->ws->r;
5290
5291         if (win->ewmh_flags & EWMH_F_FULLSCREEN)
5292                 return;
5293
5294         DNPRINTF(SWM_D_EVENT, "resize: window: 0x%x, floating: %s, "
5295             "transient: 0x%x\n", win->id, YESNO(win->floating),
5296             win->transient);
5297
5298         if (!win->transient && !win->floating)
5299                 return;
5300
5301         /* reject resizes in max mode for floaters (transient ok) */
5302         if (win->floatmaxed)
5303                 return;
5304
5305         win->manual = 1;
5306         ewmh_update_win_state(win, ewmh[_SWM_WM_STATE_MANUAL].atom,
5307             _NET_WM_STATE_ADD);
5308
5309         stack();
5310
5311         focus_flush();
5312
5313         switch (args->id) {
5314         case SWM_ARG_ID_WIDTHSHRINK:
5315                 WIDTH(win) -= SWM_RESIZE_STEPS;
5316                 resize_stp = 1;
5317                 break;
5318         case SWM_ARG_ID_WIDTHGROW:
5319                 WIDTH(win) += SWM_RESIZE_STEPS;
5320                 resize_stp = 1;
5321                 break;
5322         case SWM_ARG_ID_HEIGHTSHRINK:
5323                 HEIGHT(win) -= SWM_RESIZE_STEPS;
5324                 resize_stp = 1;
5325                 break;
5326         case SWM_ARG_ID_HEIGHTGROW:
5327                 HEIGHT(win) += SWM_RESIZE_STEPS;
5328                 resize_stp = 1;
5329                 break;
5330         default:
5331                 break;
5332         }
5333         if (resize_stp) {
5334                 constrain_window(win, r, 1);
5335                 update_window(win);
5336                 store_float_geom(win,r);
5337                 return;
5338         }
5339
5340         /* get cursor offset from window root */
5341         xpr = xcb_query_pointer_reply(conn, xcb_query_pointer(conn, win->id),
5342             NULL);
5343         if (!xpr)
5344                 return;
5345
5346         g = win->g;
5347
5348         if (xpr->win_x < WIDTH(win) / 2)
5349                 left = 1;
5350
5351         if (xpr->win_y < HEIGHT(win) / 2)
5352                 top = 1;
5353
5354         if (args->id == SWM_ARG_ID_CENTER)
5355                 cursor = cursors[XC_SIZING].cid;
5356         else if (top)
5357                 cursor = cursors[left ? XC_TOP_LEFT_CORNER :
5358                     XC_TOP_RIGHT_CORNER].cid;
5359         else
5360                 cursor = cursors[left ? XC_BOTTOM_LEFT_CORNER :
5361                     XC_BOTTOM_RIGHT_CORNER].cid;
5362
5363         xcb_grab_pointer(conn, 0, win->id, MOUSEMASK,
5364             XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC, XCB_WINDOW_NONE, cursor,
5365             XCB_CURRENT_TIME),
5366
5367         xcb_flush(conn);
5368         resizing = 1;
5369         while ((evt = xcb_wait_for_event(conn)) && resizing) {
5370                 switch (XCB_EVENT_RESPONSE_TYPE(evt)) {
5371                 case XCB_BUTTON_RELEASE:
5372                         DNPRINTF(SWM_D_EVENT, "resize: BUTTON_RELEASE\n");
5373                         resizing = 0;
5374                         break;
5375                 case XCB_MOTION_NOTIFY:
5376                         mne = (xcb_motion_notify_event_t *)evt;
5377                         /* cursor offset/delta from start of the operation */
5378                         dx = mne->root_x - xpr->root_x;
5379                         dy = mne->root_y - xpr->root_y;
5380
5381                         /* vertical */
5382                         if (top)
5383                                 dy = -dy;
5384
5385                         if (args->id == SWM_ARG_ID_CENTER) {
5386                                 if (g.h / 2 + dy < 1)
5387                                         dy = 1 - g.h / 2;
5388
5389                                 Y(win) = g.y - dy;
5390                                 HEIGHT(win) = g.h + 2 * dy;
5391                         } else {
5392                                 if (g.h + dy < 1)
5393                                         dy = 1 - g.h;
5394
5395                                 if (top)
5396                                         Y(win) = g.y - dy;
5397
5398                                 HEIGHT(win) = g.h + dy;
5399                         }
5400
5401                         /* horizontal */
5402                         if (left)
5403                                 dx = -dx;
5404
5405                         if (args->id == SWM_ARG_ID_CENTER) {
5406                                 if (g.w / 2 + dx < 1)
5407                                         dx = 1 - g.w / 2;
5408
5409                                 X(win) = g.x - dx;
5410                                 WIDTH(win) = g.w + 2 * dx;
5411                         } else {
5412                                 if (g.w + dx < 1)
5413                                         dx = 1 - g.w;
5414
5415                                 if (left)
5416                                         X(win) = g.x - dx;
5417
5418                                 WIDTH(win) = g.w + dx;
5419                         }
5420
5421                         constrain_window(win, r, 1);
5422
5423                         /* not free, don't sync more than 120 times / second */
5424                         if ((mne->time - timestamp) > (1000 / 120) ) {
5425                                 timestamp = mne->time;
5426                                 update_window(win);
5427                                 xcb_flush(conn);
5428                         }
5429                         break;
5430                 default:
5431                         event_handle(evt);
5432                         break;
5433                 }
5434                 free(evt);
5435         }
5436         if (timestamp) {
5437                 update_window(win);
5438                 xcb_flush(conn);
5439         }
5440         store_float_geom(win,r);
5441
5442         xcb_ungrab_pointer(conn, XCB_CURRENT_TIME);
5443         free(xpr);
5444         DNPRINTF(SWM_D_EVENT, "resize: done.\n");
5445 }
5446
5447 void
5448 resize_step(struct swm_region *r, union arg *args)
5449 {
5450         struct ws_win           *win = NULL;
5451
5452         if (r && r->ws && r->ws->focus)
5453                 win = r->ws->focus;
5454         else
5455                 return;
5456
5457         resize(win, args);
5458         focus_flush();
5459 }
5460
5461 #define SWM_MOVE_STEPS  (50)
5462
5463 void
5464 move(struct ws_win *win, union arg *args)
5465 {
5466         xcb_timestamp_t         timestamp = 0;
5467         int                     move_stp = 0, moving;
5468         struct swm_region       *r = NULL;
5469         xcb_query_pointer_reply_t       *qpr;
5470         xcb_generic_event_t             *evt;
5471         xcb_motion_notify_event_t       *mne;
5472
5473         if (win == NULL)
5474                 return;
5475         r = win->ws->r;
5476
5477         if (win->ewmh_flags & EWMH_F_FULLSCREEN)
5478                 return;
5479
5480         DNPRINTF(SWM_D_EVENT, "move: window: 0x%x, floating: %s, transient: "
5481             "0x%x\n", win->id, YESNO(win->floating), win->transient);
5482
5483         /* in max_stack mode should only move transients */
5484         if (win->ws->cur_layout == &layouts[SWM_MAX_STACK] && !win->transient)
5485                 return;
5486
5487         win->manual = 1;
5488         if (!win->floating && !win->transient) {
5489                 store_float_geom(win, r);
5490                 ewmh_update_win_state(win, ewmh[_NET_WM_STATE_ABOVE].atom,
5491                     _NET_WM_STATE_ADD);
5492         }
5493         ewmh_update_win_state(win, ewmh[_SWM_WM_STATE_MANUAL].atom,
5494             _NET_WM_STATE_ADD);
5495
5496         stack();
5497
5498         focus_flush();
5499
5500         move_stp = 0;
5501         switch (args->id) {
5502         case SWM_ARG_ID_MOVELEFT:
5503                 X(win) -= (SWM_MOVE_STEPS - border_width);
5504                 move_stp = 1;
5505                 break;
5506         case SWM_ARG_ID_MOVERIGHT:
5507                 X(win) += (SWM_MOVE_STEPS - border_width);
5508                 move_stp = 1;
5509                 break;
5510         case SWM_ARG_ID_MOVEUP:
5511                 Y(win) -= (SWM_MOVE_STEPS - border_width);
5512                 move_stp = 1;
5513                 break;
5514         case SWM_ARG_ID_MOVEDOWN:
5515                 Y(win) += (SWM_MOVE_STEPS - border_width);
5516                 move_stp = 1;
5517                 break;
5518         default:
5519                 break;
5520         }
5521         if (move_stp) {
5522                 constrain_window(win, r, 0);
5523                 update_window(win);
5524                 store_float_geom(win, r);
5525                 return;
5526         }
5527
5528         xcb_grab_pointer(conn, 0, win->id, MOUSEMASK,
5529             XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC,
5530             XCB_WINDOW_NONE, cursors[XC_FLEUR].cid, XCB_CURRENT_TIME);
5531
5532         /* get cursor offset from window root */
5533         qpr = xcb_query_pointer_reply(conn, xcb_query_pointer(conn, win->id),
5534                 NULL);
5535         if (!qpr) {
5536                 xcb_ungrab_pointer(conn, XCB_CURRENT_TIME);
5537                 return;
5538         }
5539
5540         xcb_flush(conn);
5541         moving = 1;
5542         while ((evt = xcb_wait_for_event(conn)) && moving) {
5543                 switch (XCB_EVENT_RESPONSE_TYPE(evt)) {
5544                 case XCB_BUTTON_RELEASE:
5545                         DNPRINTF(SWM_D_EVENT, "move: BUTTON_RELEASE\n");
5546                         moving = 0;
5547                         break;
5548                 case XCB_MOTION_NOTIFY:
5549                         mne = (xcb_motion_notify_event_t *)evt;
5550                         X(win) = mne->root_x - qpr->win_x - border_width;
5551                         Y(win) = mne->root_y - qpr->win_y - border_width;
5552
5553                         constrain_window(win, r, 0);
5554
5555                         /* not free, don't sync more than 120 times / second */
5556                         if ((mne->time - timestamp) > (1000 / 120) ) {
5557                                 timestamp = mne->time;
5558                                 update_window(win);
5559                                 xcb_flush(conn);
5560                         }
5561                         break;
5562                 default:
5563                         event_handle(evt);
5564                         break;
5565                 }
5566                 free(evt);
5567         }
5568         if (timestamp) {
5569                 update_window(win);
5570                 xcb_flush(conn);
5571         }
5572         store_float_geom(win, r);
5573         free(qpr);
5574         xcb_ungrab_pointer(conn, XCB_CURRENT_TIME);
5575         DNPRINTF(SWM_D_EVENT, "move: done.\n");
5576 }
5577
5578 void
5579 move_step(struct swm_region *r, union arg *args)
5580 {
5581         struct ws_win           *win = NULL;
5582
5583         if (r && r->ws && r->ws->focus)
5584                 win = r->ws->focus;
5585         else
5586                 return;
5587
5588         if (!win->transient && !win->floating)
5589                 return;
5590
5591         move(win, args);
5592         focus_flush();
5593 }
5594
5595 /* key definitions */
5596 struct keyfunc {
5597         char                    name[SWM_FUNCNAME_LEN];
5598         void                    (*func)(struct swm_region *r, union arg *);
5599         union arg               args;
5600 } keyfuncs[KF_INVALID + 1] = {
5601         /* name                 function        argument */
5602         { "bar_toggle",         bar_toggle,     {.id = SWM_ARG_ID_BAR_TOGGLE} },
5603         { "bar_toggle_ws",      bar_toggle,     {.id = SWM_ARG_ID_BAR_TOGGLE_WS} },
5604         { "button2",            pressbutton,    {2} },
5605         { "cycle_layout",       cycle_layout,   {0} },
5606         { "flip_layout",        stack_config,   {.id = SWM_ARG_ID_FLIPLAYOUT} },
5607         { "float_toggle",       floating_toggle,{0} },
5608         { "focus_main",         focus,          {.id = SWM_ARG_ID_FOCUSMAIN} },
5609         { "focus_next",         focus,          {.id = SWM_ARG_ID_FOCUSNEXT} },
5610         { "focus_prev",         focus,          {.id = SWM_ARG_ID_FOCUSPREV} },
5611         { "height_grow",        resize_step,    {.id = SWM_ARG_ID_HEIGHTGROW} },
5612         { "height_shrink",      resize_step,    {.id = SWM_ARG_ID_HEIGHTSHRINK} },
5613         { "iconify",            iconify,        {0} },
5614         { "master_shrink",      stack_config,   {.id = SWM_ARG_ID_MASTERSHRINK} },
5615         { "master_grow",        stack_config,   {.id = SWM_ARG_ID_MASTERGROW} },
5616         { "master_add",         stack_config,   {.id = SWM_ARG_ID_MASTERADD} },
5617         { "master_del",         stack_config,   {.id = SWM_ARG_ID_MASTERDEL} },
5618         { "move_down",          move_step,      {.id = SWM_ARG_ID_MOVEDOWN} },
5619         { "move_left",          move_step,      {.id = SWM_ARG_ID_MOVELEFT} },
5620         { "move_right",         move_step,      {.id = SWM_ARG_ID_MOVERIGHT} },
5621         { "move_up",            move_step,      {.id = SWM_ARG_ID_MOVEUP} },
5622         { "mvrg_1",             send_to_rg,     {.id = 0} },
5623         { "mvrg_2",             send_to_rg,     {.id = 1} },
5624         { "mvrg_3",             send_to_rg,     {.id = 2} },
5625         { "mvrg_4",             send_to_rg,     {.id = 3} },
5626         { "mvrg_5",             send_to_rg,     {.id = 4} },
5627         { "mvrg_6",             send_to_rg,     {.id = 5} },
5628         { "mvrg_7",             send_to_rg,     {.id = 6} },
5629         { "mvrg_8",             send_to_rg,     {.id = 7} },
5630         { "mvrg_9",             send_to_rg,     {.id = 8} },
5631         { "mvws_1",             send_to_ws,     {.id = 0} },
5632         { "mvws_2",             send_to_ws,     {.id = 1} },
5633         { "mvws_3",             send_to_ws,     {.id = 2} },
5634         { "mvws_4",             send_to_ws,     {.id = 3} },
5635         { "mvws_5",             send_to_ws,     {.id = 4} },
5636         { "mvws_6",             send_to_ws,     {.id = 5} },
5637         { "mvws_7",             send_to_ws,     {.id = 6} },
5638         { "mvws_8",             send_to_ws,     {.id = 7} },
5639         { "mvws_9",             send_to_ws,     {.id = 8} },
5640         { "mvws_10",            send_to_ws,     {.id = 9} },
5641         { "mvws_11",            send_to_ws,     {.id = 10} },
5642         { "mvws_12",            send_to_ws,     {.id = 11} },
5643         { "mvws_13",            send_to_ws,     {.id = 12} },
5644         { "mvws_14",            send_to_ws,     {.id = 13} },
5645         { "mvws_15",            send_to_ws,     {.id = 14} },
5646         { "mvws_16",            send_to_ws,     {.id = 15} },
5647         { "mvws_17",            send_to_ws,     {.id = 16} },
5648         { "mvws_18",            send_to_ws,     {.id = 17} },
5649         { "mvws_19",            send_to_ws,     {.id = 18} },
5650         { "mvws_20",            send_to_ws,     {.id = 19} },
5651         { "mvws_21",            send_to_ws,     {.id = 20} },
5652         { "mvws_22",            send_to_ws,     {.id = 21} },
5653         { "name_workspace",     name_workspace, {0} },
5654         { "quit",               quit,           {0} },
5655         { "raise_toggle",       raise_toggle,   {0} },
5656         { "restart",            restart,        {0} },
5657         { "rg_1",               focusrg,        {.id = 0} },
5658         { "rg_2",               focusrg,        {.id = 1} },
5659         { "rg_3",               focusrg,        {.id = 2} },
5660         { "rg_4",               focusrg,        {.id = 3} },
5661         { "rg_5",               focusrg,        {.id = 4} },
5662         { "rg_6",               focusrg,        {.id = 5} },
5663         { "rg_7",               focusrg,        {.id = 6} },
5664         { "rg_8",               focusrg,        {.id = 7} },
5665         { "rg_9",               focusrg,        {.id = 8} },
5666         { "rg_next",            cyclerg,        {.id = SWM_ARG_ID_CYCLERG_UP} },
5667         { "rg_prev",            cyclerg,        {.id = SWM_ARG_ID_CYCLERG_DOWN} },
5668         { "screen_next",        cyclerg,        {.id = SWM_ARG_ID_CYCLERG_UP} },
5669         { "screen_prev",        cyclerg,        {.id = SWM_ARG_ID_CYCLERG_DOWN} },
5670         { "search_win",         search_win,     {0} },
5671         { "search_workspace",   search_workspace,       {0} },
5672         { "spawn_custom",       NULL,           {0} },
5673         { "stack_inc",          stack_config,   {.id = SWM_ARG_ID_STACKINC} },
5674         { "stack_dec",          stack_config,   {.id = SWM_ARG_ID_STACKDEC} },
5675         { "stack_reset",        stack_config,   {.id = SWM_ARG_ID_STACKRESET} },
5676         { "swap_main",          swapwin,        {.id = SWM_ARG_ID_SWAPMAIN} },
5677         { "swap_next",          swapwin,        {.id = SWM_ARG_ID_SWAPNEXT} },
5678         { "swap_prev",          swapwin,        {.id = SWM_ARG_ID_SWAPPREV} },
5679         { "uniconify",          uniconify,      {0} },
5680         { "version",            version,        {0} },
5681         { "width_grow",         resize_step,    {.id = SWM_ARG_ID_WIDTHGROW} },
5682         { "width_shrink",       resize_step,    {.id = SWM_ARG_ID_WIDTHSHRINK} },
5683         { "wind_del",           wkill,          {.id = SWM_ARG_ID_DELETEWINDOW} },
5684         { "wind_kill",          wkill,          {.id = SWM_ARG_ID_KILLWINDOW} },
5685         { "ws_1",               switchws,       {.id = 0} },
5686         { "ws_2",               switchws,       {.id = 1} },
5687         { "ws_3",               switchws,       {.id = 2} },
5688         { "ws_4",               switchws,       {.id = 3} },
5689         { "ws_5",               switchws,       {.id = 4} },
5690         { "ws_6",               switchws,       {.id = 5} },
5691         { "ws_7",               switchws,       {.id = 6} },
5692         { "ws_8",               switchws,       {.id = 7} },
5693         { "ws_9",               switchws,       {.id = 8} },
5694         { "ws_10",              switchws,       {.id = 9} },
5695         { "ws_11",              switchws,       {.id = 10} },
5696         { "ws_12",              switchws,       {.id = 11} },
5697         { "ws_13",              switchws,       {.id = 12} },
5698         { "ws_14",              switchws,       {.id = 13} },
5699         { "ws_15",              switchws,       {.id = 14} },
5700         { "ws_16",              switchws,       {.id = 15} },
5701         { "ws_17",              switchws,       {.id = 16} },
5702         { "ws_18",              switchws,       {.id = 17} },
5703         { "ws_19",              switchws,       {.id = 18} },
5704         { "ws_20",              switchws,       {.id = 19} },
5705         { "ws_21",              switchws,       {.id = 20} },
5706         { "ws_22",              switchws,       {.id = 21} },
5707         { "ws_next",            cyclews,        {.id = SWM_ARG_ID_CYCLEWS_UP} },
5708         { "ws_next_all",        cyclews,        {.id = SWM_ARG_ID_CYCLEWS_UP_ALL} },
5709         { "ws_prev",            cyclews,        {.id = SWM_ARG_ID_CYCLEWS_DOWN} },
5710         { "ws_prev_all",        cyclews,        {.id = SWM_ARG_ID_CYCLEWS_DOWN_ALL} },
5711         { "ws_prior",           priorws,        {0} },
5712         { "dumpwins",           dumpwins,       {0} }, /* MUST BE LAST */
5713         { "invalid key func",   NULL,           {0} },
5714 };
5715
5716 int
5717 key_cmp(struct key *kp1, struct key *kp2)
5718 {
5719         if (kp1->keysym < kp2->keysym)
5720                 return (-1);
5721         if (kp1->keysym > kp2->keysym)
5722                 return (1);
5723
5724         if (kp1->mod < kp2->mod)
5725                 return (-1);
5726         if (kp1->mod > kp2->mod)
5727                 return (1);
5728
5729         return (0);
5730 }
5731
5732 /* mouse */
5733 enum { client_click, root_click };
5734 struct button {
5735         unsigned int            action;
5736         unsigned int            mask;
5737         unsigned int            button;
5738         void                    (*func)(struct ws_win *, union arg *);
5739         union arg               args;
5740 } buttons[] = {
5741 #define MODKEY_SHIFT    MODKEY | XCB_MOD_MASK_SHIFT
5742           /* action     key             mouse button    func    args */
5743         { client_click, MODKEY,         XCB_BUTTON_INDEX_3,     resize, {.id = SWM_ARG_ID_DONTCENTER} },
5744         { client_click, MODKEY_SHIFT,   XCB_BUTTON_INDEX_3,     resize, {.id = SWM_ARG_ID_CENTER} },
5745         { client_click, MODKEY,         XCB_BUTTON_INDEX_1,     move,   {0} },
5746 #undef MODKEY_SHIFT
5747 };
5748
5749 void
5750 update_modkey(unsigned int mod)
5751 {
5752         int                     i;
5753         struct key              *kp;
5754
5755         mod_key = mod;
5756         RB_FOREACH(kp, key_tree, &keys)
5757                 if (kp->mod & XCB_MOD_MASK_SHIFT)
5758                         kp->mod = mod | XCB_MOD_MASK_SHIFT;
5759                 else
5760                         kp->mod = mod;
5761
5762         for (i = 0; i < LENGTH(buttons); i++)
5763                 if (buttons[i].mask & XCB_MOD_MASK_SHIFT)
5764                         buttons[i].mask = mod | XCB_MOD_MASK_SHIFT;
5765                 else
5766                         buttons[i].mask = mod;
5767 }
5768
5769 int
5770 spawn_expand(struct swm_region *r, union arg *args, const char *spawn_name,
5771     char ***ret_args)
5772 {
5773         struct spawn_prog       *prog = NULL;
5774         int                     i;
5775         char                    *ap, **real_args;
5776
5777         /* suppress unused warning since var is needed */
5778         (void)args;
5779
5780         DNPRINTF(SWM_D_SPAWN, "spawn_expand: %s\n", spawn_name);
5781
5782         /* find program */
5783         TAILQ_FOREACH(prog, &spawns, entry) {
5784                 if (!strcasecmp(spawn_name, prog->name))
5785                         break;
5786         }
5787         if (prog == NULL) {
5788                 warnx("spawn_custom: program %s not found", spawn_name);
5789                 return (-1);
5790         }
5791
5792         /* make room for expanded args */
5793         if ((real_args = calloc(prog->argc + 1, sizeof(char *))) == NULL)
5794                 err(1, "spawn_custom: calloc real_args");
5795
5796         /* expand spawn_args into real_args */
5797         for (i = 0; i < prog->argc; i++) {
5798                 ap = prog->argv[i];
5799                 DNPRINTF(SWM_D_SPAWN, "spawn_custom: raw arg: %s\n", ap);
5800                 if (!strcasecmp(ap, "$bar_border")) {
5801                         if ((real_args[i] =
5802                             strdup(r->s->c[SWM_S_COLOR_BAR_BORDER].name))
5803                             == NULL)
5804                                 err(1,  "spawn_custom border color");
5805                 } else if (!strcasecmp(ap, "$bar_color")) {
5806                         if ((real_args[i] =
5807                             strdup(r->s->c[SWM_S_COLOR_BAR].name))
5808                             == NULL)
5809                                 err(1, "spawn_custom bar color");
5810                 } else if (!strcasecmp(ap, "$bar_font")) {
5811                         if ((real_args[i] = strdup(bar_fonts))
5812                             == NULL)
5813                                 err(1, "spawn_custom bar fonts");
5814                 } else if (!strcasecmp(ap, "$bar_font_color")) {
5815                         if ((real_args[i] =
5816                             strdup(r->s->c[SWM_S_COLOR_BAR_FONT].name))
5817                             == NULL)
5818                                 err(1, "spawn_custom color font");
5819                 } else if (!strcasecmp(ap, "$color_focus")) {
5820                         if ((real_args[i] =
5821                             strdup(r->s->c[SWM_S_COLOR_FOCUS].name))
5822                             == NULL)
5823                                 err(1, "spawn_custom color focus");
5824                 } else if (!strcasecmp(ap, "$color_unfocus")) {
5825                         if ((real_args[i] =
5826                             strdup(r->s->c[SWM_S_COLOR_UNFOCUS].name))
5827                             == NULL)
5828                                 err(1, "spawn_custom color unfocus");
5829                 } else if (!strcasecmp(ap, "$region_index")) {
5830                         if (asprintf(&real_args[i], "%d",
5831                             get_region_index(r) + 1) < 1)
5832                                 err(1, "spawn_custom region index");
5833                 } else if (!strcasecmp(ap, "$workspace_index")) {
5834                         if (asprintf(&real_args[i], "%d", r->ws->idx + 1) < 1)
5835                                 err(1, "spawn_custom workspace index");
5836                 } else {
5837                         /* no match --> copy as is */
5838                         if ((real_args[i] = strdup(ap)) == NULL)
5839                                 err(1, "spawn_custom strdup(ap)");
5840                 }
5841                 DNPRINTF(SWM_D_SPAWN, "spawn_custom: cooked arg: %s\n",
5842                     real_args[i]);
5843         }
5844
5845 #ifdef SWM_DEBUG
5846         DNPRINTF(SWM_D_SPAWN, "spawn_custom: result: ");
5847         for (i = 0; i < prog->argc; i++)
5848                 DNPRINTF(SWM_D_SPAWN, "\"%s\" ", real_args[i]);
5849         DNPRINTF(SWM_D_SPAWN, "\n");
5850 #endif
5851         *ret_args = real_args;
5852         return (prog->argc);
5853 }
5854
5855 void
5856 spawn_custom(struct swm_region *r, union arg *args, const char *spawn_name)
5857 {
5858         union arg               a;
5859         char                    **real_args;
5860         int                     spawn_argc, i;
5861
5862         if ((spawn_argc = spawn_expand(r, args, spawn_name, &real_args)) < 0)
5863                 return;
5864         a.argv = real_args;
5865         if (fork() == 0)
5866                 spawn(r->ws->idx, &a, 1);
5867
5868         for (i = 0; i < spawn_argc; i++)
5869                 free(real_args[i]);
5870         free(real_args);
5871 }
5872
5873 void
5874 spawn_select(struct swm_region *r, union arg *args, const char *spawn_name,
5875     int *pid)
5876 {
5877         union arg               a;
5878         char                    **real_args;
5879         int                     i, spawn_argc;
5880
5881         if ((spawn_argc = spawn_expand(r, args, spawn_name, &real_args)) < 0)
5882                 return;
5883         a.argv = real_args;
5884
5885         if (pipe(select_list_pipe) == -1)
5886                 err(1, "pipe error");
5887         if (pipe(select_resp_pipe) == -1)
5888                 err(1, "pipe error");
5889
5890         if (signal(SIGPIPE, SIG_IGN) == SIG_ERR)
5891                 err(1, "could not disable SIGPIPE");
5892         switch (*pid = fork()) {
5893         case -1:
5894                 err(1, "cannot fork");
5895                 break;
5896         case 0: /* child */
5897                 if (dup2(select_list_pipe[0], STDIN_FILENO) == -1)
5898                         err(1, "dup2");
5899                 if (dup2(select_resp_pipe[1], STDOUT_FILENO) == -1)
5900                         err(1, "dup2");
5901                 close(select_list_pipe[1]);
5902                 close(select_resp_pipe[0]);
5903                 spawn(r->ws->idx, &a, 0);
5904                 break;
5905         default: /* parent */
5906                 close(select_list_pipe[0]);
5907                 close(select_resp_pipe[1]);
5908                 break;
5909         }
5910
5911         for (i = 0; i < spawn_argc; i++)
5912                 free(real_args[i]);
5913         free(real_args);
5914 }
5915
5916 void
5917 spawn_insert(const char *name, const char *args)
5918 {
5919         char                    *arg, *cp, *ptr;
5920         struct spawn_prog       *sp;
5921
5922         DNPRINTF(SWM_D_SPAWN, "spawn_insert: %s\n", name);
5923
5924         if ((sp = calloc(1, sizeof *sp)) == NULL)
5925                 err(1, "spawn_insert: calloc");
5926         if ((sp->name = strdup(name)) == NULL)
5927                 err(1, "spawn_insert: strdup");
5928
5929         /* convert the arguments to an argument list */
5930         if ((ptr = cp = strdup(args)) == NULL)
5931                 err(1, "spawn_insert: strdup");
5932         while ((arg = strsep(&ptr, " \t")) != NULL) {
5933                 /* empty field; skip it */
5934                 if (*arg == '\0')
5935                         continue;
5936
5937                 sp->argc++;
5938                 if ((sp->argv = realloc(sp->argv, sp->argc *
5939                     sizeof *sp->argv)) == NULL)
5940                         err(1, "spawn_insert: realloc");
5941                 if ((sp->argv[sp->argc - 1] = strdup(arg)) == NULL)
5942                         err(1, "spawn_insert: strdup");
5943         }
5944         free(cp);
5945
5946         TAILQ_INSERT_TAIL(&spawns, sp, entry);
5947         DNPRINTF(SWM_D_SPAWN, "spawn_insert: leave\n");
5948 }
5949
5950 void
5951 spawn_remove(struct spawn_prog *sp)
5952 {
5953         int                     i;
5954
5955         DNPRINTF(SWM_D_SPAWN, "spawn_remove: %s\n", sp->name);
5956
5957         TAILQ_REMOVE(&spawns, sp, entry);
5958         for (i = 0; i < sp->argc; i++)
5959                 free(sp->argv[i]);
5960         free(sp->argv);
5961         free(sp->name);
5962         free(sp);
5963
5964         DNPRINTF(SWM_D_SPAWN, "spawn_remove: leave\n");
5965 }
5966
5967 void
5968 spawn_replace(struct spawn_prog *sp, const char *name, const char *args)
5969 {
5970         DNPRINTF(SWM_D_SPAWN, "spawn_replace: %s [%s]\n", sp->name, name);
5971
5972         spawn_remove(sp);
5973         spawn_insert(name, args);
5974
5975         DNPRINTF(SWM_D_SPAWN, "spawn_replace: leave\n");
5976 }
5977
5978 void
5979 setspawn(const char *name, const char *args)
5980 {
5981         struct spawn_prog       *sp;
5982
5983         DNPRINTF(SWM_D_SPAWN, "setspawn: %s\n", name);
5984
5985         if (name == NULL)
5986                 return;
5987
5988         TAILQ_FOREACH(sp, &spawns, entry) {
5989                 if (!strcmp(sp->name, name)) {
5990                         if (*args == '\0')
5991                                 spawn_remove(sp);
5992                         else
5993                                 spawn_replace(sp, name, args);
5994                         DNPRINTF(SWM_D_SPAWN, "setspawn: leave\n");
5995                         return;
5996                 }
5997         }
5998         if (*args == '\0') {
5999                 warnx("error: setspawn: cannot find program: %s", name);
6000                 return;
6001         }
6002
6003         spawn_insert(name, args);
6004         DNPRINTF(SWM_D_SPAWN, "setspawn: leave\n");
6005 }
6006
6007 int
6008 setconfspawn(char *selector, char *value, int flags)
6009 {
6010         char *args;
6011
6012         /* suppress unused warning since var is needed */
6013         (void)flags;
6014
6015         args = expand_tilde(value);
6016
6017         DNPRINTF(SWM_D_SPAWN, "setconfspawn: [%s] [%s]\n", selector, args);
6018
6019         setspawn(selector, args);
6020         free(args);
6021
6022         DNPRINTF(SWM_D_SPAWN, "setconfspawn: done.\n");
6023         return (0);
6024 }
6025
6026 void
6027 setup_spawn(void)
6028 {
6029         setconfspawn("term",            "xterm",                0);
6030         setconfspawn("spawn_term",      "xterm",                0);
6031         setconfspawn("screenshot_all",  "screenshot.sh full",   0);
6032         setconfspawn("screenshot_wind", "screenshot.sh window", 0);
6033         setconfspawn("lock",            "xlock",                0);
6034         setconfspawn("initscr",         "initscreen.sh",        0);
6035         setconfspawn("menu",            "dmenu_run"
6036                                         " -fn $bar_font"
6037                                         " -nb $bar_color"
6038                                         " -nf $bar_font_color"
6039                                         " -sb $bar_border"
6040                                         " -sf $bar_color",      0);
6041         setconfspawn("search",          "dmenu"
6042                                         " -i"
6043                                         " -fn $bar_font"
6044                                         " -nb $bar_color"
6045                                         " -nf $bar_font_color"
6046                                         " -sb $bar_border"
6047                                         " -sf $bar_color",      0);
6048         setconfspawn("name_workspace",  "dmenu"
6049                                         " -p Workspace"
6050                                         " -fn $bar_font"
6051                                         " -nb $bar_color"
6052                                         " -nf $bar_font_color"
6053                                         " -sb $bar_border"
6054                                         " -sf $bar_color",      0);
6055 }
6056
6057 /* key bindings */
6058 #define SWM_MODNAME_SIZE        32
6059 #define SWM_KEY_WS              "\n+ \t"
6060 int
6061 parsekeys(char *keystr, unsigned int currmod, unsigned int *mod, KeySym *ks)
6062 {
6063         char                    *cp, *name;
6064         KeySym                  uks;
6065         DNPRINTF(SWM_D_KEY, "parsekeys: enter [%s]\n", keystr);
6066         if (mod == NULL || ks == NULL) {
6067                 DNPRINTF(SWM_D_KEY, "parsekeys: no mod or key vars\n");
6068                 return (1);
6069         }
6070         if (keystr == NULL || strlen(keystr) == 0) {
6071                 DNPRINTF(SWM_D_KEY, "parsekeys: no keystr\n");
6072                 return (1);
6073         }
6074         cp = keystr;
6075         *ks = XCB_NO_SYMBOL;
6076         *mod = 0;
6077         while ((name = strsep(&cp, SWM_KEY_WS)) != NULL) {
6078                 DNPRINTF(SWM_D_KEY, "parsekeys: key [%s]\n", name);
6079                 if (cp)
6080                         cp += (long)strspn(cp, SWM_KEY_WS);
6081                 if (strncasecmp(name, "MOD", SWM_MODNAME_SIZE) == 0)
6082                         *mod |= currmod;
6083                 else if (!strncasecmp(name, "Mod1", SWM_MODNAME_SIZE))
6084                         *mod |= XCB_MOD_MASK_1;
6085                 else if (!strncasecmp(name, "Mod2", SWM_MODNAME_SIZE))
6086                         *mod += XCB_MOD_MASK_2;
6087                 else if (!strncmp(name, "Mod3", SWM_MODNAME_SIZE))
6088                         *mod |= XCB_MOD_MASK_3;
6089                 else if (!strncmp(name, "Mod4", SWM_MODNAME_SIZE))
6090                         *mod |= XCB_MOD_MASK_4;
6091                 else if (strncasecmp(name, "SHIFT", SWM_MODNAME_SIZE) == 0)
6092                         *mod |= XCB_MOD_MASK_SHIFT;
6093                 else if (strncasecmp(name, "CONTROL", SWM_MODNAME_SIZE) == 0)
6094                         *mod |= XCB_MOD_MASK_CONTROL;
6095                 else {
6096                         *ks = XStringToKeysym(name);
6097                         XConvertCase(*ks, ks, &uks);
6098                         if (ks == XCB_NO_SYMBOL) {
6099                                 DNPRINTF(SWM_D_KEY,
6100                                     "parsekeys: invalid key %s\n",
6101                                     name);
6102                                 return (1);
6103                         }
6104                 }
6105         }
6106         DNPRINTF(SWM_D_KEY, "parsekeys: leave ok\n");
6107         return (0);
6108 }
6109
6110 char *
6111 strdupsafe(const char *str)
6112 {
6113         if (str == NULL)
6114                 return (NULL);
6115         else
6116                 return (strdup(str));
6117 }
6118
6119 void
6120 key_insert(unsigned int mod, KeySym ks, enum keyfuncid kfid,
6121     const char *spawn_name)
6122 {
6123         struct key              *kp;
6124
6125         DNPRINTF(SWM_D_KEY, "key_insert: enter %s [%s]\n",
6126             keyfuncs[kfid].name, spawn_name);
6127
6128         if ((kp = malloc(sizeof *kp)) == NULL)
6129                 err(1, "key_insert: malloc");
6130
6131         kp->mod = mod;
6132         kp->keysym = ks;
6133         kp->funcid = kfid;
6134         kp->spawn_name = strdupsafe(spawn_name);
6135         RB_INSERT(key_tree, &keys, kp);
6136
6137         DNPRINTF(SWM_D_KEY, "key_insert: leave\n");
6138 }
6139
6140 struct key *
6141 key_lookup(unsigned int mod, KeySym ks)
6142 {
6143         struct key              kp;
6144
6145         kp.keysym = ks;
6146         kp.mod = mod;
6147
6148         return (RB_FIND(key_tree, &keys, &kp));
6149 }
6150
6151 void
6152 key_remove(struct key *kp)
6153 {
6154         DNPRINTF(SWM_D_KEY, "key_remove: %s\n", keyfuncs[kp->funcid].name);
6155
6156         RB_REMOVE(key_tree, &keys, kp);
6157         free(kp->spawn_name);
6158         free(kp);
6159
6160         DNPRINTF(SWM_D_KEY, "key_remove: leave\n");
6161 }
6162
6163 void
6164 key_replace(struct key *kp, unsigned int mod, KeySym ks, enum keyfuncid kfid,
6165     const char *spawn_name)
6166 {
6167         DNPRINTF(SWM_D_KEY, "key_replace: %s [%s]\n", keyfuncs[kp->funcid].name,
6168             spawn_name);
6169
6170         key_remove(kp);
6171         key_insert(mod, ks, kfid, spawn_name);
6172
6173         DNPRINTF(SWM_D_KEY, "key_replace: leave\n");
6174 }
6175
6176 void
6177 setkeybinding(unsigned int mod, KeySym ks, enum keyfuncid kfid,
6178     const char *spawn_name)
6179 {
6180         struct key              *kp;
6181
6182         DNPRINTF(SWM_D_KEY, "setkeybinding: enter %s [%s]\n",
6183             keyfuncs[kfid].name, spawn_name);
6184
6185         if ((kp = key_lookup(mod, ks)) != NULL) {
6186                 if (kfid == KF_INVALID)
6187                         key_remove(kp);
6188                 else
6189                         key_replace(kp, mod, ks, kfid, spawn_name);
6190                 DNPRINTF(SWM_D_KEY, "setkeybinding: leave\n");
6191                 return;
6192         }
6193         if (kfid == KF_INVALID) {
6194                 warnx("bind: Key combination already unbound.");
6195                 DNPRINTF(SWM_D_KEY, "setkeybinding: leave\n");
6196                 return;
6197         }
6198
6199         key_insert(mod, ks, kfid, spawn_name);
6200         DNPRINTF(SWM_D_KEY, "setkeybinding: leave\n");
6201 }
6202
6203 int
6204 setconfbinding(char *selector, char *value, int flags)
6205 {
6206         enum keyfuncid          kfid;
6207         unsigned int            mod;
6208         KeySym                  ks;
6209         struct spawn_prog       *sp;
6210
6211         /* suppress unused warning since var is needed */
6212         (void)flags;
6213
6214         DNPRINTF(SWM_D_KEY, "setconfbinding: enter\n");
6215         if (selector == NULL) {
6216                 DNPRINTF(SWM_D_KEY, "setconfbinding: unbind %s\n", value);
6217                 if (parsekeys(value, mod_key, &mod, &ks) == 0) {
6218                         kfid = KF_INVALID;
6219                         setkeybinding(mod, ks, kfid, NULL);
6220                         return (0);
6221                 } else
6222                         return (1);
6223         }
6224         /* search by key function name */
6225         for (kfid = 0; kfid < KF_INVALID; (kfid)++) {
6226                 if (strncasecmp(selector, keyfuncs[kfid].name,
6227                     SWM_FUNCNAME_LEN) == 0) {
6228                         DNPRINTF(SWM_D_KEY, "setconfbinding: %s: match\n",
6229                             selector);
6230                         if (parsekeys(value, mod_key, &mod, &ks) == 0) {
6231                                 setkeybinding(mod, ks, kfid, NULL);
6232                                 return (0);
6233                         } else
6234                                 return (1);
6235                 }
6236         }
6237         /* search by custom spawn name */
6238         TAILQ_FOREACH(sp, &spawns, entry) {
6239                 if (strcasecmp(selector, sp->name) == 0) {
6240                         DNPRINTF(SWM_D_KEY, "setconfbinding: %s: match\n",
6241                             selector);
6242                         if (parsekeys(value, mod_key, &mod, &ks) == 0) {
6243                                 setkeybinding(mod, ks, KF_SPAWN_CUSTOM,
6244                                     sp->name);
6245                                 return (0);
6246                         } else
6247                                 return (1);
6248                 }
6249         }
6250         DNPRINTF(SWM_D_KEY, "setconfbinding: no match\n");
6251         return (1);
6252 }
6253
6254 void
6255 setup_keys(void)
6256 {
6257 #define MODKEY_SHIFT    MODKEY | XCB_MOD_MASK_SHIFT
6258         setkeybinding(MODKEY,           XK_b,           KF_BAR_TOGGLE,  NULL);
6259         setkeybinding(MODKEY_SHIFT,     XK_b,           KF_BAR_TOGGLE_WS,NULL);
6260         setkeybinding(MODKEY,           XK_v,           KF_BUTTON2,     NULL);
6261         setkeybinding(MODKEY,           XK_space,       KF_CYCLE_LAYOUT,NULL);
6262         setkeybinding(MODKEY_SHIFT,     XK_backslash,   KF_FLIP_LAYOUT, NULL);
6263         setkeybinding(MODKEY,           XK_t,           KF_FLOAT_TOGGLE,NULL);
6264         setkeybinding(MODKEY,           XK_m,           KF_FOCUS_MAIN,  NULL);
6265         setkeybinding(MODKEY,           XK_j,           KF_FOCUS_NEXT,  NULL);
6266         setkeybinding(MODKEY,           XK_Tab,         KF_FOCUS_NEXT,  NULL);
6267         setkeybinding(MODKEY,           XK_k,           KF_FOCUS_PREV,  NULL);
6268         setkeybinding(MODKEY_SHIFT,     XK_Tab,         KF_FOCUS_PREV,  NULL);
6269         setkeybinding(MODKEY_SHIFT,     XK_equal,       KF_HEIGHT_GROW,NULL);
6270         setkeybinding(MODKEY_SHIFT,     XK_minus,       KF_HEIGHT_SHRINK,NULL);
6271         setkeybinding(MODKEY,           XK_w,           KF_ICONIFY,     NULL);
6272         setkeybinding(MODKEY,           XK_h,           KF_MASTER_SHRINK, NULL);
6273         setkeybinding(MODKEY,           XK_l,           KF_MASTER_GROW, NULL);
6274         setkeybinding(MODKEY,           XK_comma,       KF_MASTER_ADD,  NULL);
6275         setkeybinding(MODKEY,           XK_period,      KF_MASTER_DEL,  NULL);
6276         setkeybinding(MODKEY_SHIFT,     XK_bracketright,KF_MOVE_DOWN,NULL);
6277         setkeybinding(MODKEY,           XK_bracketleft, KF_MOVE_LEFT,NULL);
6278         setkeybinding(MODKEY,           XK_bracketright,KF_MOVE_RIGHT,NULL);
6279         setkeybinding(MODKEY_SHIFT,     XK_bracketleft, KF_MOVE_UP,     NULL);
6280         setkeybinding(MODKEY_SHIFT,     XK_KP_End,      KF_MVRG_1,      NULL);
6281         setkeybinding(MODKEY_SHIFT,     XK_KP_Down,     KF_MVRG_2,      NULL);
6282         setkeybinding(MODKEY_SHIFT,     XK_KP_Next,     KF_MVRG_3,      NULL);
6283         setkeybinding(MODKEY_SHIFT,     XK_KP_Left,     KF_MVRG_4,      NULL);
6284         setkeybinding(MODKEY_SHIFT,     XK_KP_Begin,    KF_MVRG_5,      NULL);
6285         setkeybinding(MODKEY_SHIFT,     XK_KP_Right,    KF_MVRG_6,      NULL);
6286         setkeybinding(MODKEY_SHIFT,     XK_KP_Home,     KF_MVRG_7,      NULL);
6287         setkeybinding(MODKEY_SHIFT,     XK_KP_Up,       KF_MVRG_8,      NULL);
6288         setkeybinding(MODKEY_SHIFT,     XK_KP_Prior,    KF_MVRG_9,      NULL);
6289         setkeybinding(MODKEY_SHIFT,     XK_1,           KF_MVWS_1,      NULL);
6290         setkeybinding(MODKEY_SHIFT,     XK_2,           KF_MVWS_2,      NULL);
6291         setkeybinding(MODKEY_SHIFT,     XK_3,           KF_MVWS_3,      NULL);
6292         setkeybinding(MODKEY_SHIFT,     XK_4,           KF_MVWS_4,      NULL);
6293         setkeybinding(MODKEY_SHIFT,     XK_5,           KF_MVWS_5,      NULL);
6294         setkeybinding(MODKEY_SHIFT,     XK_6,           KF_MVWS_6,      NULL);
6295         setkeybinding(MODKEY_SHIFT,     XK_7,           KF_MVWS_7,      NULL);
6296         setkeybinding(MODKEY_SHIFT,     XK_8,           KF_MVWS_8,      NULL);
6297         setkeybinding(MODKEY_SHIFT,     XK_9,           KF_MVWS_9,      NULL);
6298         setkeybinding(MODKEY_SHIFT,     XK_0,           KF_MVWS_10,     NULL);
6299         setkeybinding(MODKEY_SHIFT,     XK_F1,          KF_MVWS_11,     NULL);
6300         setkeybinding(MODKEY_SHIFT,     XK_F2,          KF_MVWS_12,     NULL);
6301         setkeybinding(MODKEY_SHIFT,     XK_F3,          KF_MVWS_13,     NULL);
6302         setkeybinding(MODKEY_SHIFT,     XK_F4,          KF_MVWS_14,     NULL);
6303         setkeybinding(MODKEY_SHIFT,     XK_F5,          KF_MVWS_15,     NULL);
6304         setkeybinding(MODKEY_SHIFT,     XK_F6,          KF_MVWS_16,     NULL);
6305         setkeybinding(MODKEY_SHIFT,     XK_F7,          KF_MVWS_17,     NULL);
6306         setkeybinding(MODKEY_SHIFT,     XK_F8,          KF_MVWS_18,     NULL);
6307         setkeybinding(MODKEY_SHIFT,     XK_F9,          KF_MVWS_19,     NULL);
6308         setkeybinding(MODKEY_SHIFT,     XK_F10,         KF_MVWS_20,     NULL);
6309         setkeybinding(MODKEY_SHIFT,     XK_F11,         KF_MVWS_21,     NULL);
6310         setkeybinding(MODKEY_SHIFT,     XK_F12,         KF_MVWS_22,     NULL);
6311         setkeybinding(MODKEY_SHIFT,     XK_slash,       KF_NAME_WORKSPACE,NULL);
6312         setkeybinding(MODKEY_SHIFT,     XK_q,           KF_QUIT,        NULL);
6313         setkeybinding(MODKEY_SHIFT,     XK_r,           KF_RAISE_TOGGLE,NULL);
6314         setkeybinding(MODKEY,           XK_q,           KF_RESTART,     NULL);
6315         setkeybinding(MODKEY,           XK_KP_End,      KF_RG_1,        NULL);
6316         setkeybinding(MODKEY,           XK_KP_Down,     KF_RG_2,        NULL);
6317         setkeybinding(MODKEY,           XK_KP_Next,     KF_RG_3,        NULL);
6318         setkeybinding(MODKEY,           XK_KP_Left,     KF_RG_4,        NULL);
6319         setkeybinding(MODKEY,           XK_KP_Begin,    KF_RG_5,        NULL);
6320         setkeybinding(MODKEY,           XK_KP_Right,    KF_RG_6,        NULL);
6321         setkeybinding(MODKEY,           XK_KP_Home,     KF_RG_7,        NULL);
6322         setkeybinding(MODKEY,           XK_KP_Up,       KF_RG_8,        NULL);
6323         setkeybinding(MODKEY,           XK_KP_Prior,    KF_RG_9,        NULL);
6324         setkeybinding(MODKEY_SHIFT,     XK_Right,       KF_RG_NEXT,     NULL);
6325         setkeybinding(MODKEY_SHIFT,     XK_Left,        KF_RG_PREV,     NULL);
6326         setkeybinding(MODKEY,           XK_f,           KF_SEARCH_WIN,  NULL);
6327         setkeybinding(MODKEY,           XK_slash,       KF_SEARCH_WORKSPACE,NULL);
6328         setkeybinding(MODKEY_SHIFT,     XK_i,           KF_SPAWN_CUSTOM,"initscr");
6329         setkeybinding(MODKEY_SHIFT,     XK_Delete,      KF_SPAWN_CUSTOM,"lock");
6330         setkeybinding(MODKEY,           XK_p,           KF_SPAWN_CUSTOM,"menu");
6331         setkeybinding(MODKEY,           XK_s,           KF_SPAWN_CUSTOM,"screenshot_all");
6332         setkeybinding(MODKEY_SHIFT,     XK_s,           KF_SPAWN_CUSTOM,"screenshot_wind");
6333         setkeybinding(MODKEY_SHIFT,     XK_Return,      KF_SPAWN_CUSTOM,"term");
6334         setkeybinding(MODKEY_SHIFT,     XK_comma,       KF_STACK_INC,   NULL);
6335         setkeybinding(MODKEY_SHIFT,     XK_period,      KF_STACK_DEC,   NULL);
6336         setkeybinding(MODKEY_SHIFT,     XK_space,       KF_STACK_RESET, NULL);
6337         setkeybinding(MODKEY,           XK_Return,      KF_SWAP_MAIN,   NULL);
6338         setkeybinding(MODKEY_SHIFT,     XK_j,           KF_SWAP_NEXT,   NULL);
6339         setkeybinding(MODKEY_SHIFT,     XK_k,           KF_SWAP_PREV,   NULL);
6340         setkeybinding(MODKEY_SHIFT,     XK_w,           KF_UNICONIFY,   NULL);
6341         setkeybinding(MODKEY_SHIFT,     XK_v,           KF_VERSION,     NULL);
6342         setkeybinding(MODKEY,           XK_equal,       KF_WIDTH_GROW,  NULL);
6343         setkeybinding(MODKEY,           XK_minus,       KF_WIDTH_SHRINK,NULL);
6344         setkeybinding(MODKEY,           XK_x,           KF_WIND_DEL,    NULL);
6345         setkeybinding(MODKEY_SHIFT,     XK_x,           KF_WIND_KILL,   NULL);
6346         setkeybinding(MODKEY,           XK_1,           KF_WS_1,        NULL);
6347         setkeybinding(MODKEY,           XK_2,           KF_WS_2,        NULL);
6348         setkeybinding(MODKEY,           XK_3,           KF_WS_3,        NULL);
6349         setkeybinding(MODKEY,           XK_4,           KF_WS_4,        NULL);
6350         setkeybinding(MODKEY,           XK_5,           KF_WS_5,        NULL);
6351         setkeybinding(MODKEY,           XK_6,           KF_WS_6,        NULL);
6352         setkeybinding(MODKEY,           XK_7,           KF_WS_7,        NULL);
6353         setkeybinding(MODKEY,           XK_8,           KF_WS_8,        NULL);
6354         setkeybinding(MODKEY,           XK_9,           KF_WS_9,        NULL);
6355         setkeybinding(MODKEY,           XK_0,           KF_WS_10,       NULL);
6356         setkeybinding(MODKEY,           XK_F1,          KF_WS_11,       NULL);
6357         setkeybinding(MODKEY,           XK_F2,          KF_WS_12,       NULL);
6358         setkeybinding(MODKEY,           XK_F3,          KF_WS_13,       NULL);
6359         setkeybinding(MODKEY,           XK_F4,          KF_WS_14,       NULL);
6360         setkeybinding(MODKEY,           XK_F5,          KF_WS_15,       NULL);
6361         setkeybinding(MODKEY,           XK_F6,          KF_WS_16,       NULL);
6362         setkeybinding(MODKEY,           XK_F7,          KF_WS_17,       NULL);
6363         setkeybinding(MODKEY,           XK_F8,          KF_WS_18,       NULL);
6364         setkeybinding(MODKEY,           XK_F9,          KF_WS_19,       NULL);
6365         setkeybinding(MODKEY,           XK_F10,         KF_WS_20,       NULL);
6366         setkeybinding(MODKEY,           XK_F11,         KF_WS_21,       NULL);
6367         setkeybinding(MODKEY,           XK_F12,         KF_WS_22,       NULL);
6368         setkeybinding(MODKEY,           XK_Right,       KF_WS_NEXT,     NULL);
6369         setkeybinding(MODKEY,           XK_Left,        KF_WS_PREV,     NULL);
6370         setkeybinding(MODKEY,           XK_Up,          KF_WS_NEXT_ALL, NULL);
6371         setkeybinding(MODKEY,           XK_Down,        KF_WS_PREV_ALL, NULL);
6372         setkeybinding(MODKEY,           XK_a,           KF_WS_PRIOR,    NULL);
6373 #ifdef SWM_DEBUG
6374         setkeybinding(MODKEY_SHIFT,     XK_d,           KF_DUMPWINS,    NULL);
6375 #endif
6376 #undef MODKEY_SHIFT
6377 }
6378
6379 void
6380 clear_keys(void)
6381 {
6382         struct key              *kp;
6383
6384         while (RB_EMPTY(&keys) == 0) {
6385                 kp = RB_ROOT(&keys);
6386                 key_remove(kp);
6387         }
6388 }
6389
6390 int
6391 setkeymapping(char *selector, char *value, int flags)
6392 {
6393         char                    keymapping_file[PATH_MAX];
6394
6395         /* suppress unused warnings since vars are needed */
6396         (void)selector;
6397         (void)flags;
6398
6399         DNPRINTF(SWM_D_KEY, "setkeymapping: enter\n");
6400         if (value[0] == '~')
6401                 snprintf(keymapping_file, sizeof keymapping_file, "%s/%s",
6402                     pwd->pw_dir, &value[1]);
6403         else
6404                 strlcpy(keymapping_file, value, sizeof keymapping_file);
6405         clear_keys();
6406         /* load new key bindings; if it fails, revert to default bindings */
6407         if (conf_load(keymapping_file, SWM_CONF_KEYMAPPING)) {
6408                 clear_keys();
6409                 setup_keys();
6410         }
6411         DNPRINTF(SWM_D_KEY, "setkeymapping: leave\n");
6412         return (0);
6413 }
6414
6415 void
6416 updatenumlockmask(void)
6417 {
6418         unsigned int                            i, j;
6419         xcb_get_modifier_mapping_reply_t        *modmap_r;
6420         xcb_keycode_t                           *modmap, kc, *keycode;
6421
6422         numlockmask = 0;
6423
6424         modmap_r = xcb_get_modifier_mapping_reply(conn,
6425             xcb_get_modifier_mapping(conn),
6426             NULL);
6427         if (modmap_r) {
6428                 modmap = xcb_get_modifier_mapping_keycodes(modmap_r);
6429                 for (i = 0; i < 8; i++) {
6430                         for (j = 0; j < modmap_r->keycodes_per_modifier; j++) {
6431                                 kc = modmap[i * modmap_r->keycodes_per_modifier
6432                                     + j];
6433                                 keycode = xcb_key_symbols_get_keycode(syms,
6434                                                 XK_Num_Lock);
6435                                 if (kc == *keycode)
6436                                         numlockmask = (1 << i);
6437                                 free(keycode);
6438                         }
6439                 }
6440                 free(modmap_r);
6441         }
6442         DNPRINTF(SWM_D_MISC, "updatenumlockmask: %d\n", numlockmask);
6443 }
6444
6445 void
6446 grabkeys(void)
6447 {
6448         struct key              *kp;
6449         int                     num_screens, k, j;
6450         unsigned int            modifiers[4];
6451         xcb_keycode_t           *code;
6452
6453         DNPRINTF(SWM_D_MISC, "grabkeys\n");
6454         updatenumlockmask();
6455
6456         modifiers[0] = 0;
6457         modifiers[1] = numlockmask;
6458         modifiers[2] = XCB_MOD_MASK_LOCK;
6459         modifiers[3] = numlockmask | XCB_MOD_MASK_LOCK;
6460
6461         num_screens = xcb_setup_roots_length(xcb_get_setup(conn));
6462         for (k = 0; k < num_screens; k++) {
6463                 if (TAILQ_EMPTY(&screens[k].rl))
6464                         continue;
6465                 xcb_ungrab_key(conn, XCB_GRAB_ANY, screens[k].root,
6466                         XCB_MOD_MASK_ANY);
6467                 RB_FOREACH(kp, key_tree, &keys) {
6468                         if ((code = xcb_key_symbols_get_keycode(syms,
6469                                         kp->keysym))) {
6470                                 for (j = 0; j < LENGTH(modifiers); j++)
6471                                         xcb_grab_key(conn, 1,
6472                                             screens[k].root,
6473                                             kp->mod | modifiers[j],
6474                                             *code, XCB_GRAB_MODE_SYNC,
6475                                             XCB_GRAB_MODE_SYNC);
6476                                 free(code);
6477                         }
6478                 }
6479         }
6480 }
6481
6482 void
6483 grabbuttons(struct ws_win *win)
6484 {
6485         unsigned int    modifiers[4];
6486         int             i, j;
6487
6488         DNPRINTF(SWM_D_MOUSE, "grabbuttons: win 0x%x\n", win->id);
6489         updatenumlockmask();
6490
6491         modifiers[0] = 0;
6492         modifiers[1] = numlockmask;
6493         modifiers[2] = XCB_MOD_MASK_LOCK;
6494         modifiers[3] = numlockmask | XCB_MOD_MASK_LOCK;
6495
6496         for (i = 0; i < LENGTH(buttons); i++)
6497                 if (buttons[i].action == client_click)
6498                         for (j = 0; j < LENGTH(modifiers); ++j)
6499                                 xcb_grab_button(conn, 0, win->id, BUTTONMASK,
6500                                     XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC,
6501                                     XCB_WINDOW_NONE, XCB_CURSOR_NONE,
6502                                     buttons[i].button, buttons[i].mask |
6503                                     modifiers[j]);
6504 }
6505
6506 const char *quirkname[] = {
6507         "NONE",         /* config string for "no value" */
6508         "FLOAT",
6509         "TRANSSZ",
6510         "ANYWHERE",
6511         "XTERM_FONTADJ",
6512         "FULLSCREEN",
6513         "FOCUSPREV",
6514 };
6515
6516 /* SWM_Q_WS: retain '|' for back compat for now (2009-08-11) */
6517 #define SWM_Q_WS                "\n|+ \t"
6518 int
6519 parsequirks(char *qstr, unsigned long *quirk)
6520 {
6521         char                    *cp, *name;
6522         int                     i;
6523
6524         if (quirk == NULL)
6525                 return (1);
6526
6527         cp = qstr;
6528         *quirk = 0;
6529         while ((name = strsep(&cp, SWM_Q_WS)) != NULL) {
6530                 if (cp)
6531                         cp += (long)strspn(cp, SWM_Q_WS);
6532                 for (i = 0; i < LENGTH(quirkname); i++) {
6533                         if (!strncasecmp(name, quirkname[i], SWM_QUIRK_LEN)) {
6534                                 DNPRINTF(SWM_D_QUIRK,
6535                                     "parsequirks: %s\n", name);
6536                                 if (i == 0) {
6537                                         *quirk = 0;
6538                                         return (0);
6539                                 }
6540                                 *quirk |= 1 << (i-1);
6541                                 break;
6542                         }
6543                 }
6544                 if (i >= LENGTH(quirkname)) {
6545                         DNPRINTF(SWM_D_QUIRK,
6546                             "parsequirks: invalid quirk [%s]\n", name);
6547                         return (1);
6548                 }
6549         }
6550         return (0);
6551 }
6552
6553 void
6554 quirk_insert(const char *class, const char *name, unsigned long quirk)
6555 {
6556         struct quirk            *qp;
6557
6558         DNPRINTF(SWM_D_QUIRK, "quirk_insert: %s:%s [%lu]\n", class, name,
6559             quirk);
6560
6561         if ((qp = malloc(sizeof *qp)) == NULL)
6562                 err(1, "quirk_insert: malloc");
6563         if ((qp->class = strdup(class)) == NULL)
6564                 err(1, "quirk_insert: strdup");
6565         if ((qp->name = strdup(name)) == NULL)
6566                 err(1, "quirk_insert: strdup");
6567
6568         qp->quirk = quirk;
6569         TAILQ_INSERT_TAIL(&quirks, qp, entry);
6570
6571         DNPRINTF(SWM_D_QUIRK, "quirk_insert: leave\n");
6572 }
6573
6574 void
6575 quirk_remove(struct quirk *qp)
6576 {
6577         DNPRINTF(SWM_D_QUIRK, "quirk_remove: %s:%s [%lu]\n", qp->class,
6578             qp->name, qp->quirk);
6579
6580         TAILQ_REMOVE(&quirks, qp, entry);
6581         free(qp->class);
6582         free(qp->name);
6583         free(qp);
6584
6585         DNPRINTF(SWM_D_QUIRK, "quirk_remove: leave\n");
6586 }
6587
6588 void
6589 quirk_replace(struct quirk *qp, const char *class, const char *name,
6590     unsigned long quirk)
6591 {
6592         DNPRINTF(SWM_D_QUIRK, "quirk_replace: %s:%s [%lu]\n", qp->class,
6593             qp->name, qp->quirk);
6594
6595         quirk_remove(qp);
6596         quirk_insert(class, name, quirk);
6597
6598         DNPRINTF(SWM_D_QUIRK, "quirk_replace: leave\n");
6599 }
6600
6601 void
6602 setquirk(const char *class, const char *name, unsigned long quirk)
6603 {
6604         struct quirk            *qp;
6605
6606         DNPRINTF(SWM_D_QUIRK, "setquirk: enter %s:%s [%lu]\n", class, name,
6607            quirk);
6608
6609         /* Remove/replace existing quirk. */
6610         TAILQ_FOREACH(qp, &quirks, entry) {
6611                 if (!strcmp(qp->class, class) && !strcmp(qp->name, name)) {
6612                         if (!quirk)
6613                                 quirk_remove(qp);
6614                         else
6615                                 quirk_replace(qp, class, name, quirk);
6616                         DNPRINTF(SWM_D_QUIRK, "setquirk: leave\n");
6617                         return;
6618                 }
6619         }
6620
6621         /* Only insert if quirk is not NONE. */
6622         if (quirk)
6623                 quirk_insert(class, name, quirk);
6624
6625         DNPRINTF(SWM_D_QUIRK, "setquirk: leave\n");
6626 }
6627
6628 int
6629 setconfquirk(char *selector, char *value, int flags)
6630 {
6631         char                    *cp, *class, *name;
6632         int                     retval;
6633         unsigned long           qrks;
6634
6635         /* suppress unused warning since var is needed */
6636         (void)flags;
6637
6638         if (selector == NULL)
6639                 return (0);
6640         if ((cp = strchr(selector, ':')) == NULL)
6641                 return (0);
6642         *cp = '\0';
6643         class = selector;
6644         name = cp + 1;
6645         if ((retval = parsequirks(value, &qrks)) == 0)
6646                 setquirk(class, name, qrks);
6647         return (retval);
6648 }
6649
6650 void
6651 setup_quirks(void)
6652 {
6653         setquirk("MPlayer",             "xv",           SWM_Q_FLOAT | SWM_Q_FULLSCREEN | SWM_Q_FOCUSPREV);
6654         setquirk("OpenOffice.org 3.2",  "VCLSalFrame",  SWM_Q_FLOAT);
6655         setquirk("Firefox-bin",         "firefox-bin",  SWM_Q_TRANSSZ);
6656         setquirk("Firefox",             "Dialog",       SWM_Q_FLOAT);
6657         setquirk("Gimp",                "gimp",         SWM_Q_FLOAT | SWM_Q_ANYWHERE);
6658         setquirk("XTerm",               "xterm",        SWM_Q_XTERM_FONTADJ);
6659         setquirk("xine",                "Xine Window",  SWM_Q_FLOAT | SWM_Q_ANYWHERE);
6660         setquirk("Xitk",                "Xitk Combo",   SWM_Q_FLOAT | SWM_Q_ANYWHERE);
6661         setquirk("xine",                "xine Panel",   SWM_Q_FLOAT | SWM_Q_ANYWHERE);
6662         setquirk("Xitk",                "Xine Window",  SWM_Q_FLOAT | SWM_Q_ANYWHERE);
6663         setquirk("xine",                "xine Video Fullscreen Window", SWM_Q_FULLSCREEN | SWM_Q_FLOAT);
6664         setquirk("pcb",                 "pcb",          SWM_Q_FLOAT);
6665         setquirk("SDL_App",             "SDL_App",      SWM_Q_FLOAT | SWM_Q_FULLSCREEN);
6666 }
6667
6668 /* conf file stuff */
6669 #define SWM_CONF_FILE           "spectrwm.conf"
6670 #define SWM_CONF_FILE_OLD       "scrotwm.conf"
6671
6672 enum {
6673         SWM_S_BAR_ACTION,
6674         SWM_S_BAR_AT_BOTTOM,
6675         SWM_S_BAR_BORDER_WIDTH,
6676         SWM_S_BAR_DELAY,
6677         SWM_S_BAR_ENABLED,
6678         SWM_S_BAR_ENABLED_WS,
6679         SWM_S_BAR_FONT,
6680         SWM_S_BAR_FORMAT,
6681         SWM_S_BAR_JUSTIFY,
6682         SWM_S_BORDER_WIDTH,
6683         SWM_S_CLOCK_ENABLED,
6684         SWM_S_CLOCK_FORMAT,
6685         SWM_S_CYCLE_EMPTY,
6686         SWM_S_CYCLE_VISIBLE,
6687         SWM_S_DIALOG_RATIO,
6688         SWM_S_DISABLE_BORDER,
6689         SWM_S_FOCUS_CLOSE,
6690         SWM_S_FOCUS_CLOSE_WRAP,
6691         SWM_S_FOCUS_DEFAULT,
6692         SWM_S_FOCUS_MODE,
6693         SWM_S_REGION_PADDING,
6694         SWM_S_SPAWN_ORDER,
6695         SWM_S_SPAWN_TERM,
6696         SWM_S_SS_APP,
6697         SWM_S_SS_ENABLED,
6698         SWM_S_STACK_ENABLED,
6699         SWM_S_TERM_WIDTH,
6700         SWM_S_TILE_GAP,
6701         SWM_S_TITLE_CLASS_ENABLED,
6702         SWM_S_TITLE_NAME_ENABLED,
6703         SWM_S_URGENT_ENABLED,
6704         SWM_S_VERBOSE_LAYOUT,
6705         SWM_S_WINDOW_NAME_ENABLED,
6706         SWM_S_WORKSPACE_LIMIT
6707 };
6708
6709 int
6710 setconfvalue(char *selector, char *value, int flags)
6711 {
6712         struct workspace        *ws;
6713         int                     i, ws_id, num_screens;
6714         char                    *b;
6715
6716         /* suppress unused warning since var is needed */
6717         (void)selector;
6718
6719         switch (flags) {
6720         case SWM_S_BAR_ACTION:
6721                 free(bar_argv[0]);
6722                 if ((bar_argv[0] = expand_tilde(value)) == NULL)
6723                         err(1, "setconfvalue: bar_action");
6724                 break;
6725         case SWM_S_BAR_AT_BOTTOM:
6726                 bar_at_bottom = atoi(value);
6727                 break;
6728         case SWM_S_BAR_BORDER_WIDTH:
6729                 bar_border_width = atoi(value);
6730                 if (bar_border_width < 0)
6731                         bar_border_width = 0;
6732                 break;
6733         case SWM_S_BAR_DELAY:
6734                 /* No longer needed; leave to not break old conf files. */
6735                 break;
6736         case SWM_S_BAR_ENABLED:
6737                 bar_enabled = atoi(value);
6738                 break;
6739         case SWM_S_BAR_ENABLED_WS:
6740                 ws_id = atoi(selector) - 1;
6741                 if (ws_id < 0 || ws_id >= workspace_limit)
6742                         errx(1, "setconfvalue: bar_enabled_ws: invalid "
6743                             "workspace %d.", ws_id + 1);
6744
6745                 num_screens = xcb_setup_roots_length(xcb_get_setup(conn));
6746                 for (i = 0; i < num_screens; i++) {
6747                         ws = (struct workspace *)&screens[i].ws;
6748                         ws[ws_id].bar_enabled = atoi(value);
6749                 }
6750                 break;
6751         case SWM_S_BAR_FONT:
6752                 b = bar_fonts;
6753                 if (asprintf(&bar_fonts, "%s,%s", value, bar_fonts) == -1)
6754                         err(1, "setconfvalue: asprintf: failed to allocate "
6755                                 "memory for bar_fonts.");
6756                 free(b);
6757
6758                 /* If already in xft mode, then we are done. */
6759                 if (!bar_font_legacy)
6760                         break;
6761
6762                 /* If there are any non-XLFD entries, switch to Xft mode. */
6763                 while ((b = strsep(&value, ",")) != NULL) {
6764                         if (*b == '\0')
6765                                 continue;
6766                         if (!isxlfd(b)) {
6767                                 bar_font_legacy = 0;
6768                                 break;
6769                         }
6770                 }
6771                 break;
6772         case SWM_S_BAR_FORMAT:
6773                 free(bar_format);
6774                 if ((bar_format = strdup(value)) == NULL)
6775                         err(1, "setconfvalue: bar_format");
6776                 break;
6777         case SWM_S_BAR_JUSTIFY:
6778                 if (!strcmp(value, "left"))
6779                         bar_justify = SWM_BAR_JUSTIFY_LEFT;
6780                 else if (!strcmp(value, "center"))
6781                         bar_justify = SWM_BAR_JUSTIFY_CENTER;
6782                 else if (!strcmp(value, "right"))
6783                         bar_justify = SWM_BAR_JUSTIFY_RIGHT;
6784                 else
6785                         errx(1, "invalid bar_justify");
6786                 break;
6787         case SWM_S_BORDER_WIDTH:
6788                 border_width = atoi(value);
6789                 if (border_width < 0)
6790                         border_width = 0;
6791                 break;
6792         case SWM_S_CLOCK_ENABLED:
6793                 clock_enabled = atoi(value);
6794                 break;
6795         case SWM_S_CLOCK_FORMAT:
6796 #ifndef SWM_DENY_CLOCK_FORMAT
6797                 free(clock_format);
6798                 if ((clock_format = strdup(value)) == NULL)
6799                         err(1, "setconfvalue: clock_format");
6800 #endif
6801                 break;
6802         case SWM_S_CYCLE_EMPTY:
6803                 cycle_empty = atoi(value);
6804                 break;
6805         case SWM_S_CYCLE_VISIBLE:
6806                 cycle_visible = atoi(value);
6807                 break;
6808         case SWM_S_DIALOG_RATIO:
6809                 dialog_ratio = atof(value);
6810                 if (dialog_ratio > 1.0 || dialog_ratio <= .3)
6811                         dialog_ratio = .6;
6812                 break;
6813         case SWM_S_DISABLE_BORDER:
6814                 disable_border = atoi(value);
6815                 break;
6816         case SWM_S_FOCUS_CLOSE:
6817                 if (!strcmp(value, "first"))
6818                         focus_close = SWM_STACK_BOTTOM;
6819                 else if (!strcmp(value, "last"))
6820                         focus_close = SWM_STACK_TOP;
6821                 else if (!strcmp(value, "next"))
6822                         focus_close = SWM_STACK_ABOVE;
6823                 else if (!strcmp(value, "previous"))
6824                         focus_close = SWM_STACK_BELOW;
6825                 else
6826                         errx(1, "focus_close");
6827                 break;
6828         case SWM_S_FOCUS_CLOSE_WRAP:
6829                 focus_close_wrap = atoi(value);
6830                 break;
6831         case SWM_S_FOCUS_DEFAULT:
6832                 if (!strcmp(value, "last"))
6833                         focus_default = SWM_STACK_TOP;
6834                 else if (!strcmp(value, "first"))
6835                         focus_default = SWM_STACK_BOTTOM;
6836                 else
6837                         errx(1, "focus_default");
6838                 break;
6839         case SWM_S_FOCUS_MODE:
6840                 if (!strcmp(value, "default"))
6841                         focus_mode = SWM_FOCUS_DEFAULT;
6842                 else if (!strcmp(value, "follow") ||
6843                     !strcmp(value, "follow_cursor"))
6844                         focus_mode = SWM_FOCUS_FOLLOW;
6845                 else if (!strcmp(value, "manual"))
6846                         focus_mode = SWM_FOCUS_MANUAL;
6847                 else
6848                         errx(1, "focus_mode");
6849                 break;
6850         case SWM_S_REGION_PADDING:
6851                 region_padding = atoi(value);
6852                 if (region_padding < 0)
6853                         region_padding = 0;
6854                 break;
6855         case SWM_S_SPAWN_ORDER:
6856                 if (!strcmp(value, "first"))
6857                         spawn_position = SWM_STACK_BOTTOM;
6858                 else if (!strcmp(value, "last"))
6859                         spawn_position = SWM_STACK_TOP;
6860                 else if (!strcmp(value, "next"))
6861                         spawn_position = SWM_STACK_ABOVE;
6862                 else if (!strcmp(value, "previous"))
6863                         spawn_position = SWM_STACK_BELOW;
6864                 else
6865                         errx(1, "spawn_position");
6866                 break;
6867         case SWM_S_SPAWN_TERM:
6868                 setconfspawn("term", value, 0);
6869                 setconfspawn("spawn_term", value, 0);
6870                 break;
6871         case SWM_S_SS_APP:
6872                 /* No longer needed; leave to not break old conf files. */
6873                 break;
6874         case SWM_S_SS_ENABLED:
6875                 /* No longer needed; leave to not break old conf files. */
6876                 break;
6877         case SWM_S_STACK_ENABLED:
6878                 stack_enabled = atoi(value);
6879                 break;
6880         case SWM_S_TERM_WIDTH:
6881                 term_width = atoi(value);
6882                 if (term_width < 0)
6883                         term_width = 0;
6884                 break;
6885         case SWM_S_TILE_GAP:
6886                 tile_gap = atoi(value);
6887                 if (tile_gap < 0)
6888                         tile_gap = 0;
6889                 break;
6890         case SWM_S_TITLE_CLASS_ENABLED:
6891                 title_class_enabled = atoi(value);
6892                 break;
6893         case SWM_S_TITLE_NAME_ENABLED:
6894                 title_name_enabled = atoi(value);
6895                 break;
6896         case SWM_S_URGENT_ENABLED:
6897                 urgent_enabled = atoi(value);
6898                 break;
6899         case SWM_S_VERBOSE_LAYOUT:
6900                 verbose_layout = atoi(value);
6901                 for (i = 0; layouts[i].l_stack != NULL; i++) {
6902                         if (verbose_layout)
6903                                 layouts[i].l_string = fancy_stacker;
6904                         else
6905                                 layouts[i].l_string = plain_stacker;
6906                 }
6907                 break;
6908         case SWM_S_WINDOW_NAME_ENABLED:
6909                 window_name_enabled = atoi(value);
6910                 break;
6911         case SWM_S_WORKSPACE_LIMIT:
6912                 workspace_limit = atoi(value);
6913                 if (workspace_limit > SWM_WS_MAX)
6914                         workspace_limit = SWM_WS_MAX;
6915                 else if (workspace_limit < 1)
6916                         workspace_limit = 1;
6917                 break;
6918         default:
6919                 return (1);
6920         }
6921         return (0);
6922 }
6923
6924 int
6925 setconfmodkey(char *selector, char *value, int flags)
6926 {
6927         /* suppress unused warnings since vars are needed */
6928         (void)selector;
6929         (void)flags;
6930
6931         if (!strncasecmp(value, "Mod1", strlen("Mod1")))
6932                 update_modkey(XCB_MOD_MASK_1);
6933         else if (!strncasecmp(value, "Mod2", strlen("Mod2")))
6934                 update_modkey(XCB_MOD_MASK_2);
6935         else if (!strncasecmp(value, "Mod3", strlen("Mod3")))
6936                 update_modkey(XCB_MOD_MASK_3);
6937         else if (!strncasecmp(value, "Mod4", strlen("Mod4")))
6938                 update_modkey(XCB_MOD_MASK_4);
6939         else
6940                 return (1);
6941         return (0);
6942 }
6943
6944 int
6945 setconfcolor(char *selector, char *value, int flags)
6946 {
6947         setscreencolor(value, ((selector == NULL)?-1:atoi(selector)), flags);
6948         return (0);
6949 }
6950
6951 int
6952 setconfregion(char *selector, char *value, int flags)
6953 {
6954         /* suppress unused warnings since vars are needed */
6955         (void)selector;
6956         (void)flags;
6957
6958         custom_region(value);
6959         return (0);
6960 }
6961
6962 int
6963 setautorun(char *selector, char *value, int flags)
6964 {
6965         int                     ws_id;
6966         char                    s[1024];
6967         char                    *ap, *sp;
6968         union arg               a;
6969         int                     argc = 0;
6970         pid_t                   pid;
6971         struct pid_e            *p;
6972
6973         /* suppress unused warnings since vars are needed */
6974         (void)selector;
6975         (void)flags;
6976
6977         if (getenv("SWM_STARTED"))
6978                 return (0);
6979
6980         bzero(s, sizeof s);
6981         if (sscanf(value, "ws[%d]:%1023c", &ws_id, s) != 2)
6982                 errx(1, "invalid autorun entry, should be 'ws[<idx>]:command'");
6983         ws_id--;
6984         if (ws_id < 0 || ws_id >= workspace_limit)
6985                 errx(1, "autorun: invalid workspace %d", ws_id + 1);
6986
6987         sp = expand_tilde((char *)&s);
6988
6989         /*
6990          * This is a little intricate
6991          *
6992          * If the pid already exists we simply reuse it because it means it was
6993          * used before AND not claimed by manage_window.  We get away with
6994          * altering it in the parent after INSERT because this can not be a race
6995          */
6996         a.argv = NULL;
6997         while ((ap = strsep(&sp, " \t")) != NULL) {
6998                 if (*ap == '\0')
6999                         continue;
7000                 DNPRINTF(SWM_D_SPAWN, "setautorun: arg [%s]\n", ap);
7001                 argc++;
7002                 if ((a.argv = realloc(a.argv, argc * sizeof(char *))) == NULL)
7003                         err(1, "setautorun: realloc");
7004                 a.argv[argc - 1] = ap;
7005         }
7006         free(sp);
7007
7008         if ((a.argv = realloc(a.argv, (argc + 1) * sizeof(char *))) == NULL)
7009                 err(1, "setautorun: realloc");
7010         a.argv[argc] = NULL;
7011
7012         if ((pid = fork()) == 0) {
7013                 spawn(ws_id, &a, 1);
7014                 /* NOTREACHED */
7015                 _exit(1);
7016         }
7017         free(a.argv);
7018
7019         /* parent */
7020         p = find_pid(pid);
7021         if (p == NULL) {
7022                 p = calloc(1, sizeof *p);
7023                 if (p == NULL)
7024                         return (1);
7025                 TAILQ_INSERT_TAIL(&pidlist, p, entry);
7026         }
7027
7028         p->pid = pid;
7029         p->ws = ws_id;
7030
7031         return (0);
7032 }
7033
7034 int
7035 setlayout(char *selector, char *value, int flags)
7036 {
7037         int                     ws_id, i, x, mg, ma, si, ar, f = 0;
7038         int                     st = SWM_V_STACK, num_screens;
7039         char                    s[1024];
7040         struct workspace        *ws;
7041
7042         /* suppress unused warnings since vars are needed */
7043         (void)selector;
7044         (void)flags;
7045
7046         if (getenv("SWM_STARTED"))
7047                 return (0);
7048
7049         bzero(s, sizeof s);
7050         if (sscanf(value, "ws[%d]:%d:%d:%d:%d:%1023c",
7051             &ws_id, &mg, &ma, &si, &ar, s) != 6)
7052                 errx(1, "invalid layout entry, should be 'ws[<idx>]:"
7053                     "<master_grow>:<master_add>:<stack_inc>:<always_raise>:"
7054                     "<type>'");
7055         ws_id--;
7056         if (ws_id < 0 || ws_id >= workspace_limit)
7057                 errx(1, "layout: invalid workspace %d", ws_id + 1);
7058
7059         if (!strcasecmp(s, "vertical"))
7060                 st = SWM_V_STACK;
7061         else if (!strcasecmp(s, "vertical_flip")) {
7062                 st = SWM_V_STACK;
7063                 f = 1;
7064         } else if (!strcasecmp(s, "horizontal"))
7065                 st = SWM_H_STACK;
7066         else if (!strcasecmp(s, "horizontal_flip")) {
7067                 st = SWM_H_STACK;
7068                 f = 1;
7069         } else if (!strcasecmp(s, "fullscreen"))
7070                 st = SWM_MAX_STACK;
7071         else
7072                 errx(1, "invalid layout entry, should be 'ws[<idx>]:"
7073                     "<master_grow>:<master_add>:<stack_inc>:<always_raise>:"
7074                     "<type>'");
7075
7076         num_screens = xcb_setup_roots_length(xcb_get_setup(conn));
7077         for (i = 0; i < num_screens; i++) {
7078                 ws = (struct workspace *)&screens[i].ws;
7079                 ws[ws_id].cur_layout = &layouts[st];
7080
7081                 ws[ws_id].always_raise = ar;
7082                 if (st == SWM_MAX_STACK)
7083                         continue;
7084
7085                 /* master grow */
7086                 for (x = 0; x < abs(mg); x++) {
7087                         ws[ws_id].cur_layout->l_config(&ws[ws_id],
7088                             mg >= 0 ?  SWM_ARG_ID_MASTERGROW :
7089                             SWM_ARG_ID_MASTERSHRINK);
7090                         stack();
7091                 }
7092                 /* master add */
7093                 for (x = 0; x < abs(ma); x++) {
7094                         ws[ws_id].cur_layout->l_config(&ws[ws_id],
7095                             ma >= 0 ?  SWM_ARG_ID_MASTERADD :
7096                             SWM_ARG_ID_MASTERDEL);
7097                         stack();
7098                 }
7099                 /* stack inc */
7100                 for (x = 0; x < abs(si); x++) {
7101                         ws[ws_id].cur_layout->l_config(&ws[ws_id],
7102                             si >= 0 ?  SWM_ARG_ID_STACKINC :
7103                             SWM_ARG_ID_STACKDEC);
7104                         stack();
7105                 }
7106                 /* Apply flip */
7107                 if (f) {
7108                         ws[ws_id].cur_layout->l_config(&ws[ws_id],
7109                             SWM_ARG_ID_FLIPLAYOUT);
7110                         stack();
7111                 }
7112         }
7113
7114         focus_flush();
7115
7116         return (0);
7117 }
7118
7119 /* config options */
7120 struct config_option {
7121         char                    *optname;
7122         int                     (*func)(char*, char*, int);
7123         int                     funcflags;
7124 };
7125 struct config_option configopt[] = {
7126         { "autorun",                    setautorun,     0 },
7127         { "bar_action",                 setconfvalue,   SWM_S_BAR_ACTION },
7128         { "bar_at_bottom",              setconfvalue,   SWM_S_BAR_AT_BOTTOM },
7129         { "bar_border",                 setconfcolor,   SWM_S_COLOR_BAR_BORDER },
7130         { "bar_border_unfocus",         setconfcolor,   SWM_S_COLOR_BAR_BORDER_UNFOCUS },
7131         { "bar_border_width",           setconfvalue,   SWM_S_BAR_BORDER_WIDTH },
7132         { "bar_color",                  setconfcolor,   SWM_S_COLOR_BAR },
7133         { "bar_delay",                  setconfvalue,   SWM_S_BAR_DELAY },
7134         { "bar_enabled",                setconfvalue,   SWM_S_BAR_ENABLED },
7135         { "bar_enabled_ws",             setconfvalue,   SWM_S_BAR_ENABLED_WS },
7136         { "bar_font",                   setconfvalue,   SWM_S_BAR_FONT },
7137         { "bar_font_color",             setconfcolor,   SWM_S_COLOR_BAR_FONT },
7138         { "bar_format",                 setconfvalue,   SWM_S_BAR_FORMAT },
7139         { "bar_justify",                setconfvalue,   SWM_S_BAR_JUSTIFY },
7140         { "bind",                       setconfbinding, 0 },
7141         { "border_width",               setconfvalue,   SWM_S_BORDER_WIDTH },
7142         { "clock_enabled",              setconfvalue,   SWM_S_CLOCK_ENABLED },
7143         { "clock_format",               setconfvalue,   SWM_S_CLOCK_FORMAT },
7144         { "color_focus",                setconfcolor,   SWM_S_COLOR_FOCUS },
7145         { "color_unfocus",              setconfcolor,   SWM_S_COLOR_UNFOCUS },
7146         { "cycle_empty",                setconfvalue,   SWM_S_CYCLE_EMPTY },
7147         { "cycle_visible",              setconfvalue,   SWM_S_CYCLE_VISIBLE },
7148         { "dialog_ratio",               setconfvalue,   SWM_S_DIALOG_RATIO },
7149         { "disable_border",             setconfvalue,   SWM_S_DISABLE_BORDER },
7150         { "focus_close",                setconfvalue,   SWM_S_FOCUS_CLOSE },
7151         { "focus_close_wrap",           setconfvalue,   SWM_S_FOCUS_CLOSE_WRAP },
7152         { "focus_default",              setconfvalue,   SWM_S_FOCUS_DEFAULT },
7153         { "focus_mode",                 setconfvalue,   SWM_S_FOCUS_MODE },
7154         { "keyboard_mapping",           setkeymapping,  0 },
7155         { "layout",                     setlayout,      0 },
7156         { "modkey",                     setconfmodkey,  0 },
7157         { "program",                    setconfspawn,   0 },
7158         { "quirk",                      setconfquirk,   0 },
7159         { "region",                     setconfregion,  0 },
7160         { "region_padding",             setconfvalue,   SWM_S_REGION_PADDING },
7161         { "screenshot_app",             setconfvalue,   SWM_S_SS_APP },
7162         { "screenshot_enabled",         setconfvalue,   SWM_S_SS_ENABLED },
7163         { "spawn_position",             setconfvalue,   SWM_S_SPAWN_ORDER },
7164         { "spawn_term",                 setconfvalue,   SWM_S_SPAWN_TERM },
7165         { "stack_enabled",              setconfvalue,   SWM_S_STACK_ENABLED },
7166         { "term_width",                 setconfvalue,   SWM_S_TERM_WIDTH },
7167         { "tile_gap",                   setconfvalue,   SWM_S_TILE_GAP },
7168         { "title_class_enabled",        setconfvalue,   SWM_S_TITLE_CLASS_ENABLED },
7169         { "title_name_enabled",         setconfvalue,   SWM_S_TITLE_NAME_ENABLED },
7170         { "urgent_enabled",             setconfvalue,   SWM_S_URGENT_ENABLED },
7171         { "verbose_layout",             setconfvalue,   SWM_S_VERBOSE_LAYOUT },
7172         { "window_name_enabled",        setconfvalue,   SWM_S_WINDOW_NAME_ENABLED },
7173         { "workspace_limit",            setconfvalue,   SWM_S_WORKSPACE_LIMIT },
7174 };
7175
7176 int
7177 conf_load(const char *filename, int keymapping)
7178 {
7179         FILE                    *config;
7180         char                    *line, *cp, *optsub, *optval;
7181         size_t                  linelen, lineno = 0;
7182         int                     wordlen, i, optidx;
7183         struct config_option    *opt = NULL;
7184
7185         DNPRINTF(SWM_D_CONF, "conf_load: begin\n");
7186
7187         if (filename == NULL) {
7188                 warnx("conf_load: no filename");
7189                 return (1);
7190         }
7191         if ((config = fopen(filename, "r")) == NULL) {
7192                 warn("conf_load: fopen: %s", filename);
7193                 return (1);
7194         }
7195
7196         while (!feof(config)) {
7197                 if ((line = fparseln(config, &linelen, &lineno, NULL, 0))
7198                     == NULL) {
7199                         if (ferror(config))
7200                                 err(1, "%s", filename);
7201                         else
7202                                 continue;
7203                 }
7204                 cp = line;
7205                 cp += strspn(cp, " \t\n"); /* eat whitespace */
7206                 if (cp[0] == '\0') {
7207                         /* empty line */
7208                         free(line);
7209                         continue;
7210                 }
7211                 /* get config option */
7212                 wordlen = strcspn(cp, "=[ \t\n");
7213                 if (wordlen == 0) {
7214                         warnx("%s: line %zd: no option found",
7215                             filename, lineno);
7216                         goto out;
7217                 }
7218                 optidx = -1;
7219                 for (i = 0; i < LENGTH(configopt); i++) {
7220                         opt = &configopt[i];
7221                         if (!strncasecmp(cp, opt->optname, wordlen) &&
7222                             (int)strlen(opt->optname) == wordlen) {
7223                                 optidx = i;
7224                                 break;
7225                         }
7226                 }
7227                 if (optidx == -1) {
7228                         warnx("%s: line %zd: unknown option %.*s",
7229                             filename, lineno, wordlen, cp);
7230                         goto out;
7231                 }
7232                 if (keymapping && opt && strcmp(opt->optname, "bind")) {
7233                         warnx("%s: line %zd: invalid option %.*s",
7234                             filename, lineno, wordlen, cp);
7235                         goto out;
7236                 }
7237                 cp += wordlen;
7238                 cp += strspn(cp, " \t\n"); /* eat whitespace */
7239                 /* get [selector] if any */
7240                 optsub = NULL;
7241                 if (*cp == '[') {
7242                         cp++;
7243                         wordlen = strcspn(cp, "]");
7244                         if (*cp != ']') {
7245                                 if (wordlen == 0) {
7246                                         warnx("%s: line %zd: syntax error",
7247                                             filename, lineno);
7248                                         goto out;
7249                                 }
7250
7251                                 if (asprintf(&optsub, "%.*s", wordlen, cp) ==
7252                                     -1) {
7253                                         warnx("%s: line %zd: unable to allocate"
7254                                             "memory for selector", filename,
7255                                             lineno);
7256                                         goto out;
7257                                 }
7258                         }
7259                         cp += wordlen;
7260                         cp += strspn(cp, "] \t\n"); /* eat trailing */
7261                 }
7262                 cp += strspn(cp, "= \t\n"); /* eat trailing */
7263                 /* get RHS value */
7264                 optval = strdup(cp);
7265                 /* call function to deal with it all */
7266                 if (configopt[optidx].func(optsub, optval,
7267                     configopt[optidx].funcflags) != 0)
7268                         errx(1, "%s: line %zd: invalid data for %s",
7269                             filename, lineno, configopt[optidx].optname);
7270                 free(optval);
7271                 free(optsub);
7272                 free(line);
7273         }
7274
7275         fclose(config);
7276         DNPRINTF(SWM_D_CONF, "conf_load: end\n");
7277
7278         return (0);
7279
7280 out:
7281         free(line);
7282         fclose(config);
7283         DNPRINTF(SWM_D_CONF, "conf_load: end with error.\n");
7284
7285         return (1);
7286 }
7287
7288 void
7289 set_child_transient(struct ws_win *win, xcb_window_t *trans)
7290 {
7291         struct ws_win           *parent, *w;
7292         struct swm_region       *r;
7293         struct workspace        *ws;
7294         xcb_icccm_wm_hints_t    wmh;
7295
7296         parent = find_window(win->transient);
7297         if (parent)
7298                 parent->focus_child = win;
7299         else {
7300                 DNPRINTF(SWM_D_MISC, "set_child_transient: parent doesn't exist"
7301                     " for 0x%x trans 0x%x\n", win->id, win->transient);
7302
7303                 r = root_to_region(win->wa->root, SWM_CK_ALL);
7304                 ws = r->ws;
7305                 /* parent doen't exist in our window list */
7306                 TAILQ_FOREACH(w, &ws->winlist, entry) {
7307                         if (xcb_icccm_get_wm_hints_reply(conn,
7308                             xcb_icccm_get_wm_hints(conn, w->id),
7309                             &wmh, NULL) != 1) {
7310                                 warnx("can't get hints for 0x%x", w->id);
7311                                 continue;
7312                         }
7313
7314                         if (win->hints.window_group != wmh.window_group)
7315                                 continue;
7316
7317                         w->focus_child = win;
7318                         win->transient = w->id;
7319                         *trans = w->id;
7320                         DNPRINTF(SWM_D_MISC, "set_child_transient: adjusting "
7321                             "transient to 0x%x\n", win->transient);
7322                         break;
7323                 }
7324         }
7325 }
7326
7327 pid_t
7328 window_get_pid(xcb_window_t win)
7329 {
7330         pid_t                           ret = 0;
7331         const char                      *errstr;
7332         xcb_atom_t                      apid;
7333         xcb_get_property_cookie_t       pc;
7334         xcb_get_property_reply_t        *pr;
7335
7336         apid = get_atom_from_string("_NET_WM_PID");
7337         if (apid == XCB_ATOM_NONE)
7338                 goto tryharder;
7339
7340         pc = xcb_get_property(conn, 0, win, apid, XCB_ATOM_CARDINAL, 0, 1);
7341         pr = xcb_get_property_reply(conn, pc, NULL);
7342         if (!pr)
7343                 goto tryharder;
7344         if (pr->type != XCB_ATOM_CARDINAL) {
7345                 free(pr);
7346                 goto tryharder;
7347         }
7348
7349         if (pr->type == apid && pr->format == 32)
7350                 ret = *((pid_t *)xcb_get_property_value(pr));
7351         free(pr);
7352
7353         return (ret);
7354
7355 tryharder:
7356         apid = get_atom_from_string("_SWM_PID");
7357         pc = xcb_get_property(conn, 0, win, apid, XCB_ATOM_STRING,
7358             0, SWM_PROPLEN);
7359         pr = xcb_get_property_reply(conn, pc, NULL);
7360         if (!pr)
7361                 return (0);
7362         if (pr->type != apid) {
7363                 free(pr);
7364                 return (0);
7365         }
7366
7367         ret = (pid_t)strtonum(xcb_get_property_value(pr), 0, INT_MAX, &errstr);
7368         free(pr);
7369
7370         return (ret);
7371 }
7372
7373 int
7374 get_ws_idx(xcb_window_t id)
7375 {
7376         int                     ws_idx = -1;
7377         char                    *prop = NULL;
7378         size_t                  proplen;
7379         const char              *errstr;
7380         xcb_get_property_reply_t        *gpr;
7381
7382         gpr = xcb_get_property_reply(conn,
7383                 xcb_get_property(conn, 0, id, a_swm_ws,
7384                     XCB_ATOM_STRING, 0, SWM_PROPLEN),
7385                 NULL);
7386         if (!gpr)
7387                 return (-1);
7388         if (gpr->type) {
7389                 proplen = xcb_get_property_value_length(gpr);
7390                 if (proplen > 0) {
7391                         prop = malloc(proplen + 1);
7392                         if (prop) {
7393                                 memcpy(prop,
7394                                     xcb_get_property_value(gpr),
7395                                     proplen);
7396                                 prop[proplen] = '\0';
7397                         }
7398                 }
7399         }
7400         free(gpr);
7401
7402         if (prop) {
7403                 DNPRINTF(SWM_D_PROP, "get_ws_idx: _SWM_WS: %s\n", prop);
7404                 ws_idx = (int)strtonum(prop, 0, workspace_limit - 1, &errstr);
7405                 if (errstr) {
7406                         DNPRINTF(SWM_D_PROP, "get_ws_idx: window: #%s: %s",
7407                             errstr, prop);
7408                 }
7409                 free(prop);
7410         }
7411
7412         return ws_idx;
7413 }
7414
7415 struct ws_win *
7416 manage_window(xcb_window_t id, uint16_t mapped)
7417 {
7418         xcb_window_t            trans = XCB_WINDOW_NONE;
7419         struct ws_win           *win, *ww;
7420         int                     ws_idx;
7421         char                    ws_idx_str[SWM_PROPLEN];
7422         struct swm_region       *r;
7423         struct pid_e            *p;
7424         struct quirk            *qp;
7425         uint32_t                i, wa[2];
7426         xcb_icccm_get_wm_protocols_reply_t      wpr;
7427
7428         if ((win = find_window(id)) != NULL) {
7429                 DNPRINTF(SWM_D_MISC, "manage_window: win 0x%x already "
7430                     "managed; skipping.)\n", id);
7431                 return (win);   /* Already managed. */
7432         }
7433
7434         /* See if window is on the unmanaged list. */
7435         if ((win = find_unmanaged_window(id)) != NULL) {
7436                 DNPRINTF(SWM_D_MISC, "manage_window: win 0x%x found on "
7437                     "unmanaged list.\n", id);
7438                 TAILQ_REMOVE(&win->ws->unmanagedlist, win, entry);
7439
7440                 if (win->transient)
7441                         set_child_transient(win, &trans);
7442
7443                 goto out;
7444         } else {
7445                 DNPRINTF(SWM_D_MISC, "manage_window: win 0x%x is new.\n", id);
7446         }
7447
7448         /* Create and initialize ws_win object. */
7449         if ((win = calloc(1, sizeof(struct ws_win))) == NULL)
7450                 err(1, "manage_window: calloc: failed to allocate memory for "
7451                     "new window");
7452
7453         win->id = id;
7454
7455         /* Get window geometry. */
7456         win->wa = xcb_get_geometry_reply(conn,
7457             xcb_get_geometry(conn, win->id),
7458             NULL);
7459
7460         /* Figure out which region the window belongs to. */
7461         r = root_to_region(win->wa->root, SWM_CK_ALL);
7462
7463         /* Ignore window border if there is one. */
7464         WIDTH(win) = win->wa->width;
7465         HEIGHT(win) = win->wa->height;
7466         X(win) = win->wa->x + win->wa->border_width - border_width;
7467         Y(win) = win->wa->y + win->wa->border_width - border_width;
7468         win->bordered = 1;
7469         win->mapped = mapped;
7470         win->floatmaxed = 0;
7471         win->ewmh_flags = 0;
7472         win->s = r->s;  /* this never changes */
7473
7474         store_float_geom(win, r);
7475
7476         /* Get WM_SIZE_HINTS. */
7477         xcb_icccm_get_wm_normal_hints_reply(conn,
7478             xcb_icccm_get_wm_normal_hints(conn, win->id),
7479             &win->sh, NULL);
7480
7481         /* Get WM_HINTS. */
7482         xcb_icccm_get_wm_hints_reply(conn,
7483             xcb_icccm_get_wm_hints(conn, win->id),
7484             &win->hints, NULL);
7485
7486         /* Get WM_TRANSIENT_FOR; see if window is a transient. */
7487         xcb_icccm_get_wm_transient_for_reply(conn,
7488             xcb_icccm_get_wm_transient_for(conn, win->id),
7489             &trans, NULL);
7490         if (trans) {
7491                 win->transient = trans;
7492                 set_child_transient(win, &win->transient);
7493         }
7494
7495         /* Get supported protocols. */
7496         if (xcb_icccm_get_wm_protocols_reply(conn,
7497             xcb_icccm_get_wm_protocols(conn, win->id, a_prot),
7498             &wpr, NULL)) {
7499                 for (i = 0; i < wpr.atoms_len; i++) {
7500                         if (wpr.atoms[i] == a_takefocus)
7501                                 win->take_focus = 1;
7502                         if (wpr.atoms[i] == a_delete)
7503                                 win->can_delete = 1;
7504                 }
7505                 xcb_icccm_get_wm_protocols_reply_wipe(&wpr);
7506         }
7507
7508         win->iconic = get_swm_iconic(win);
7509
7510         /* Figure out which workspace the window belongs to. */
7511         if ((p = find_pid(window_get_pid(win->id))) != NULL) {
7512                 win->ws = &r->s->ws[p->ws];
7513                 TAILQ_REMOVE(&pidlist, p, entry);
7514                 free(p);
7515                 p = NULL;
7516         } else if ((ws_idx = get_ws_idx(win->id)) != -1 &&
7517             !win->transient) {
7518                 /* _SWM_WS is set; use that. */
7519                 win->ws = &r->s->ws[ws_idx];
7520         } else if (trans && (ww = find_window(trans)) != NULL) {
7521                 /* Launch transients in the same ws as parent. */
7522                 win->ws = ww->ws;
7523         } else {
7524                 win->ws = r->ws;
7525         }
7526
7527         /* Set the _SWM_WS atom so we can remember this after reincarnation. */
7528         if (snprintf(ws_idx_str, SWM_PROPLEN, "%d", win->ws->idx) <
7529             SWM_PROPLEN) {
7530                 DNPRINTF(SWM_D_PROP, "manage_window: set _SWM_WS: %s\n",
7531                     ws_idx_str);
7532                 xcb_change_property(conn, XCB_PROP_MODE_REPLACE, win->id,
7533                     a_swm_ws, XCB_ATOM_STRING, 8, strlen(ws_idx_str),
7534                     ws_idx_str);
7535         }
7536
7537         /* Handle EWMH */
7538         ewmh_autoquirk(win);
7539
7540         /* Determine initial quirks. */
7541         if (xcb_icccm_get_wm_class_reply(conn,
7542             xcb_icccm_get_wm_class(conn, win->id),
7543             &win->ch, NULL)) {
7544                 DNPRINTF(SWM_D_CLASS, "manage_window: class: %s, name: %s\n",
7545                     win->ch.class_name, win->ch.instance_name);
7546
7547                 /* java is retarded so treat it special */
7548                 if (strstr(win->ch.instance_name, "sun-awt")) {
7549                         DNPRINTF(SWM_D_CLASS, "manage_window: java window "
7550                             "detected.\n");
7551                         win->java = 1;
7552                 }
7553
7554                 TAILQ_FOREACH(qp, &quirks, entry) {
7555                         if (!strcmp(win->ch.class_name, qp->class) &&
7556                             !strcmp(win->ch.instance_name, qp->name)) {
7557                                 DNPRINTF(SWM_D_CLASS, "manage_window: on quirks"
7558                                     "list; mask: 0x%lx\n", qp->quirk);
7559                                 if (qp->quirk & SWM_Q_FLOAT)
7560                                         win->floating = 1;
7561                                 win->quirks = qp->quirk;
7562                         }
7563                 }
7564         }
7565
7566         /* Alter window position if quirky */
7567         if (win->quirks & SWM_Q_ANYWHERE)
7568                 win->manual = 1;
7569
7570         /* Reset font sizes (the bruteforce way; no default keybinding). */
7571         if (win->quirks & SWM_Q_XTERM_FONTADJ) {
7572                 for (i = 0; i < SWM_MAX_FONT_STEPS; i++)
7573                         fake_keypress(win, XK_KP_Subtract, XCB_MOD_MASK_SHIFT);
7574                 for (i = 0; i < SWM_MAX_FONT_STEPS; i++)
7575                         fake_keypress(win, XK_KP_Add, XCB_MOD_MASK_SHIFT);
7576         }
7577
7578         /* Make sure window is positioned inside its region, if its active. */
7579         if (win->ws->r) {
7580                 constrain_window(win, win->ws->r, 0);
7581                 update_window(win);
7582         }
7583
7584         /* Select which X events to monitor and set border pixel color. */
7585         wa[0] = win->s->c[SWM_S_COLOR_UNFOCUS].pixel;
7586         wa[1] = XCB_EVENT_MASK_ENTER_WINDOW | XCB_EVENT_MASK_PROPERTY_CHANGE |
7587             XCB_EVENT_MASK_STRUCTURE_NOTIFY;
7588 #ifdef SWM_DEBUG
7589         wa[1] |= XCB_EVENT_MASK_LEAVE_WINDOW | XCB_EVENT_MASK_FOCUS_CHANGE;
7590 #endif
7591
7592         xcb_change_window_attributes(conn, win->id, XCB_CW_BORDER_PIXEL |
7593             XCB_CW_EVENT_MASK, wa);
7594
7595 out:
7596         /* Figure out where to stack the window in the workspace. */
7597         if (trans && (ww = find_window(trans)))
7598                 TAILQ_INSERT_AFTER(&win->ws->winlist, ww, win, entry);
7599         else if (win->ws->focus && spawn_position == SWM_STACK_ABOVE)
7600                 TAILQ_INSERT_AFTER(&win->ws->winlist, win->ws->focus, win,
7601                     entry);
7602         else if (win->ws->focus && spawn_position == SWM_STACK_BELOW)
7603                 TAILQ_INSERT_BEFORE(win->ws->focus, win, entry);
7604         else switch (spawn_position) {
7605         default:
7606         case SWM_STACK_TOP:
7607         case SWM_STACK_ABOVE:
7608                 TAILQ_INSERT_TAIL(&win->ws->winlist, win, entry);
7609                 break;
7610         case SWM_STACK_BOTTOM:
7611         case SWM_STACK_BELOW:
7612                 TAILQ_INSERT_HEAD(&win->ws->winlist, win, entry);
7613         }
7614
7615         /* Get initial _NET_WM_STATE */
7616         ewmh_get_win_state(win);
7617         /* Set initial _NET_WM_ALLOWED_ACTIONS */
7618         ewmh_update_actions(win);
7619
7620         grabbuttons(win);
7621
7622         DNPRINTF(SWM_D_MISC, "manage_window: done. window: 0x%x, (x,y) w x h: "
7623             "(%d,%d) %d x %d, ws: %d, iconic: %s, transient: 0x%x\n", win->id,
7624             X(win), Y(win), WIDTH(win), HEIGHT(win), win->ws->idx,
7625             YESNO(win->iconic), win->transient);
7626
7627         return (win);
7628 }
7629
7630 void
7631 free_window(struct ws_win *win)
7632 {
7633         DNPRINTF(SWM_D_MISC, "free_window: window: 0x%x\n", win->id);
7634
7635         if (win == NULL)
7636                 return;
7637
7638         TAILQ_REMOVE(&win->ws->unmanagedlist, win, entry);
7639
7640         if (win->wa)
7641                 free(win->wa);
7642
7643         xcb_icccm_get_wm_class_reply_wipe(&win->ch);
7644
7645         kill_refs(win);
7646
7647         /* paint memory */
7648         memset(win, 0xff, sizeof *win); /* XXX kill later */
7649
7650         free(win);
7651         DNPRINTF(SWM_D_MISC, "free_window: done.\n");
7652 }
7653
7654 void
7655 unmanage_window(struct ws_win *win)
7656 {
7657         struct ws_win           *parent;
7658
7659         if (win == NULL)
7660                 return;
7661
7662         DNPRINTF(SWM_D_MISC, "unmanage_window: window: 0x%x\n", win->id);
7663
7664         if (win->transient) {
7665                 parent = find_window(win->transient);
7666                 if (parent)
7667                         parent->focus_child = NULL;
7668         }
7669
7670         TAILQ_REMOVE(&win->ws->winlist, win, entry);
7671         TAILQ_INSERT_TAIL(&win->ws->unmanagedlist, win, entry);
7672 }
7673
7674 void
7675 expose(xcb_expose_event_t *e)
7676 {
7677         int                     i, num_screens;
7678         struct swm_region       *r;
7679
7680         DNPRINTF(SWM_D_EVENT, "expose: window: 0x%x\n", e->window);
7681
7682         num_screens = xcb_setup_roots_length(xcb_get_setup(conn));
7683         for (i = 0; i < num_screens; i++)
7684                 TAILQ_FOREACH(r, &screens[i].rl, entry)
7685                         if (e->window == WINID(r->bar))
7686                                 bar_draw();
7687
7688         xcb_flush(conn);
7689 }
7690
7691 #ifdef SWM_DEBUG
7692 void
7693 focusin(xcb_focus_in_event_t *e)
7694 {
7695         DNPRINTF(SWM_D_EVENT, "focusin: window: 0x%x, mode: %s(%u), "
7696             "detail: %s(%u)\n", e->event, get_notify_mode_label(e->mode),
7697             e->mode, get_notify_detail_label(e->detail), e->detail);
7698 }
7699
7700 void
7701 focusout(xcb_focus_out_event_t *e)
7702 {
7703         DNPRINTF(SWM_D_EVENT, "focusout: window: 0x%x, mode: %s(%u), "
7704             "detail: %s(%u)\n", e->event, get_notify_mode_label(e->mode),
7705             e->mode, get_notify_detail_label(e->detail), e->detail);
7706 }
7707 #endif
7708
7709 void
7710 keypress(xcb_key_press_event_t *e)
7711 {
7712         xcb_keysym_t            keysym;
7713         struct key              *kp;
7714
7715         keysym = xcb_key_press_lookup_keysym(syms, e, 0);
7716
7717         DNPRINTF(SWM_D_EVENT, "keypress: keysym: %u, win (x,y): 0x%x (%d,%d), "
7718             "detail: %u, time: %u, root (x,y): 0x%x (%d,%d), child: 0x%x, "
7719             "state: %u, same_screen: %s\n", keysym, e->event, e->event_x,
7720             e->event_y, e->detail, e->time, e->root, e->root_x, e->root_y,
7721             e->child, e->state, YESNO(e->same_screen));
7722
7723         if ((kp = key_lookup(CLEANMASK(e->state), keysym)) == NULL)
7724                 goto out;
7725
7726         last_event_time = e->time;
7727
7728         if (kp->funcid == KF_SPAWN_CUSTOM)
7729                 spawn_custom(root_to_region(e->root, SWM_CK_ALL),
7730                     &(keyfuncs[kp->funcid].args), kp->spawn_name);
7731         else if (keyfuncs[kp->funcid].func)
7732                 keyfuncs[kp->funcid].func(root_to_region(e->root, SWM_CK_ALL),
7733                     &(keyfuncs[kp->funcid].args));
7734
7735 out:
7736         /* Unfreeze grab events. */
7737         xcb_allow_events(conn, XCB_ALLOW_ASYNC_KEYBOARD, e->time);
7738         xcb_flush(conn);
7739
7740         DNPRINTF(SWM_D_EVENT, "keypress: done.\n");
7741 }
7742
7743 void
7744 buttonpress(xcb_button_press_event_t *e)
7745 {
7746         struct ws_win           *win = NULL;
7747         struct swm_region       *r, *old_r;
7748         int                     i;
7749         int                     handled = 0;
7750
7751         DNPRINTF(SWM_D_EVENT, "buttonpress: win (x,y): 0x%x (%d,%d), "
7752             "detail: %u, time: %u, root (x,y): 0x%x (%d,%d), child: 0x%x, "
7753             "state: %u, same_screen: %s\n", e->event, e->event_x, e->event_y,
7754             e->detail, e->time, e->root, e->root_x, e->root_y, e->child,
7755             e->state, YESNO(e->same_screen));
7756
7757         if (e->event == e->root) {
7758                 if (e->child != 0) {
7759                         win = find_window(e->child);
7760                         /* Pass ButtonPress to window if it isn't managed. */
7761                         if (win == NULL)
7762                                 goto out;
7763                 } else {
7764                         /* Focus on empty region */
7765                         /* If no windows on region if its empty. */
7766                         r = root_to_region(e->root, SWM_CK_POINTER);
7767                         if (r == NULL) {
7768                                 DNPRINTF(SWM_D_EVENT, "buttonpress: "
7769                                     "NULL region; ignoring.\n");
7770                                 goto out;
7771                         }
7772
7773                         if (TAILQ_EMPTY(&r->ws->winlist)) {
7774                                 old_r = root_to_region(e->root, SWM_CK_FOCUS);
7775                                 if (old_r && old_r != r)
7776                                         unfocus_win(old_r->ws->focus);
7777
7778                                 xcb_set_input_focus(conn,
7779                                     XCB_INPUT_FOCUS_PARENT, e->root, e->time);
7780
7781                                 /* Clear bar since empty. */
7782                                 bar_draw();
7783
7784                                 handled = 1;
7785                                 goto out;
7786                         }
7787                 }
7788         } else {
7789                 win = find_window(e->event);
7790         }
7791
7792         if (win == NULL)
7793                 goto out;
7794
7795         last_event_time = e->time;
7796
7797         focus_win(get_focus_magic(win));
7798
7799         for (i = 0; i < LENGTH(buttons); i++)
7800                 if (client_click == buttons[i].action && buttons[i].func &&
7801                     buttons[i].button == e->detail &&
7802                     CLEANMASK(buttons[i].mask) == CLEANMASK(e->state)) {
7803                         buttons[i].func(win, &buttons[i].args);
7804                         handled = 1;
7805                 }
7806
7807 out:
7808         if (!handled) {
7809                 DNPRINTF(SWM_D_EVENT, "buttonpress: passing to window.\n");
7810                 /* Replay event to event window */
7811                 xcb_allow_events(conn, XCB_ALLOW_REPLAY_POINTER, e->time);
7812         } else {
7813                 DNPRINTF(SWM_D_EVENT, "buttonpress: handled.\n");
7814                 /* Unfreeze grab events. */
7815                 xcb_allow_events(conn, XCB_ALLOW_SYNC_POINTER, e->time);
7816         }
7817
7818         xcb_flush(conn);
7819 }
7820
7821 #ifdef SWM_DEBUG
7822 void
7823 print_win_geom(xcb_window_t w)
7824 {
7825         xcb_get_geometry_reply_t        *wa;
7826
7827         wa = xcb_get_geometry_reply(conn, xcb_get_geometry(conn, w), NULL);
7828
7829         if (!wa) {
7830                 DNPRINTF(SWM_D_MISC, "print_win_geom: window not found: 0x%x\n",
7831                     w);
7832                 return;
7833         }
7834
7835         DNPRINTF(SWM_D_MISC, "print_win_geom: window: 0x%x, root: 0x%x, "
7836             "depth: %u, (x,y) w x h: (%d,%d) %d x %d, border: %d\n",
7837             w, wa->root, wa->depth, wa->x,  wa->y, wa->width, wa->height,
7838             wa->border_width);
7839
7840         free(wa);
7841 }
7842 #endif
7843
7844 #ifdef SWM_DEBUG
7845 char *
7846 get_stack_mode_name(uint8_t mode)
7847 {
7848         char    *name;
7849
7850         switch(mode) {
7851         case XCB_STACK_MODE_ABOVE:
7852                 name = "Above";
7853                 break;
7854         case XCB_STACK_MODE_BELOW:
7855                 name = "Below";
7856                 break;
7857         case XCB_STACK_MODE_TOP_IF:
7858                 name = "TopIf";
7859                 break;
7860         case XCB_STACK_MODE_BOTTOM_IF:
7861                 name = "BottomIf";
7862                 break;
7863         case XCB_STACK_MODE_OPPOSITE:
7864                 name = "Opposite";
7865                 break;
7866         default:
7867                 name = "Unknown";
7868         }
7869
7870         return name;
7871 }
7872 #endif
7873
7874 void
7875 configurerequest(xcb_configure_request_event_t *e)
7876 {
7877         struct ws_win           *win;
7878         struct swm_region       *r = NULL;
7879         int                     new = 0, i = 0;
7880         uint16_t                mask = 0;
7881         uint32_t                wc[7] = {0};
7882
7883         if ((win = find_window(e->window)) == NULL)
7884                 if ((win = find_unmanaged_window(e->window)) == NULL)
7885                         new = 1;
7886
7887 #ifdef SWM_DEBUG
7888         if (swm_debug & SWM_D_EVENT) {
7889                 print_win_geom(e->window);
7890
7891                 DNPRINTF(SWM_D_EVENT, "configurerequest: window: 0x%x, "
7892                     "parent: 0x%x, new: %s, value_mask: %u { ", e->window,
7893                     e->parent, YESNO(new), e->value_mask);
7894                 if (e->value_mask & XCB_CONFIG_WINDOW_X)
7895                         DPRINTF("X: %d ", e->x);
7896                 if (e->value_mask & XCB_CONFIG_WINDOW_Y)
7897                         DPRINTF("Y: %d ", e->y);
7898                 if (e->value_mask & XCB_CONFIG_WINDOW_WIDTH)
7899                         DPRINTF("W: %u ", e->width);
7900                 if (e->value_mask & XCB_CONFIG_WINDOW_HEIGHT)
7901                         DPRINTF("H: %u ", e->height);
7902                 if (e->value_mask & XCB_CONFIG_WINDOW_BORDER_WIDTH)
7903                         DPRINTF("Border: %u ", e->border_width);
7904                 if (e->value_mask & XCB_CONFIG_WINDOW_SIBLING)
7905                         DPRINTF("Sibling: 0x%x ", e->sibling);
7906                 if (e->value_mask & XCB_CONFIG_WINDOW_STACK_MODE)
7907                         DPRINTF("StackMode: %s(%u) ",
7908                             get_stack_mode_name(e->stack_mode), e->stack_mode);
7909                 DPRINTF("}\n");
7910         }
7911 #endif
7912
7913         if (new) {
7914                 if (e->value_mask & XCB_CONFIG_WINDOW_X) {
7915                         mask |= XCB_CONFIG_WINDOW_X;
7916                         wc[i++] = e->x;
7917                 }
7918                 if (e->value_mask & XCB_CONFIG_WINDOW_Y) {
7919                         mask |= XCB_CONFIG_WINDOW_Y;
7920                         wc[i++] = e->y;
7921                 }
7922                 if (e->value_mask & XCB_CONFIG_WINDOW_WIDTH) {
7923                         mask |= XCB_CONFIG_WINDOW_WIDTH;
7924                         wc[i++] = e->width;
7925                 }
7926                 if (e->value_mask & XCB_CONFIG_WINDOW_HEIGHT) {
7927                         mask |= XCB_CONFIG_WINDOW_HEIGHT;
7928                         wc[i++] = e->height;
7929                 }
7930                 if (e->value_mask & XCB_CONFIG_WINDOW_BORDER_WIDTH) {
7931                         mask |= XCB_CONFIG_WINDOW_BORDER_WIDTH;
7932                         wc[i++] = e->border_width;
7933                 }
7934                 if (e->value_mask & XCB_CONFIG_WINDOW_SIBLING) {
7935                         mask |= XCB_CONFIG_WINDOW_SIBLING;
7936                         wc[i++] = e->sibling;
7937                 }
7938                 if (e->value_mask & XCB_CONFIG_WINDOW_STACK_MODE) {
7939                         mask |= XCB_CONFIG_WINDOW_STACK_MODE;
7940                         wc[i++] = e->stack_mode;
7941                 }
7942
7943                 if (mask != 0) {
7944                         xcb_configure_window(conn, e->window, mask, wc);
7945                         xcb_flush(conn);
7946                 }
7947         } else if ((!win->manual || win->quirks & SWM_Q_ANYWHERE) &&
7948             !(win->ewmh_flags & EWMH_F_FULLSCREEN)) {
7949                 if (win->ws->r)
7950                         r = win->ws->r;
7951                 else if (win->ws->old_r)
7952                         r = win->ws->old_r;
7953
7954                 /* windows are centered unless ANYWHERE quirk is set. */
7955                 if (win->quirks & SWM_Q_ANYWHERE) {
7956                         if (e->value_mask & XCB_CONFIG_WINDOW_X) {
7957                                 win->g_float.x = e->x;
7958                                 if (r)
7959                                         win->g_float.x -= X(r);
7960                         }
7961
7962                         if (e->value_mask & XCB_CONFIG_WINDOW_Y) {
7963                                 win->g_float.y = e->y;
7964                                 if (r)
7965                                         win->g_float.y -= Y(r);
7966                         }
7967                 }
7968
7969                 if (e->value_mask & XCB_CONFIG_WINDOW_WIDTH)
7970                         win->g_float.w = e->width;
7971
7972                 if (e->value_mask & XCB_CONFIG_WINDOW_HEIGHT)
7973                         win->g_float.h = e->height;
7974
7975                 win->g_floatvalid = 1;
7976
7977                 if (win->floating && r && (win->transient ||
7978                     win->ws->cur_layout != &layouts[SWM_MAX_STACK])) {
7979                         WIDTH(win) = win->g_float.w;
7980                         HEIGHT(win) = win->g_float.h;
7981
7982                         if (r) {
7983                                 stack_floater(win, r);
7984                                 focus_flush();
7985                         }
7986                 } else {
7987                         config_win(win, e);
7988                         xcb_flush(conn);
7989                 }
7990         } else {
7991                 config_win(win, e);
7992                 xcb_flush(conn);
7993         }
7994
7995         DNPRINTF(SWM_D_EVENT, "configurerequest: done.\n");
7996 }
7997
7998 void
7999 configurenotify(xcb_configure_notify_event_t *e)
8000 {
8001         struct ws_win           *win;
8002
8003         DNPRINTF(SWM_D_EVENT, "configurenotify: win 0x%x, event win: 0x%x, "
8004             "(x,y) WxH: (%d,%d) %ux%u, border: %u, above_sibling: 0x%x, "
8005             "override_redirect: %s\n", e->window, e->event, e->x, e->y,
8006             e->width, e->height, e->border_width, e->above_sibling,
8007             YESNO(e->override_redirect));
8008
8009         win = find_window(e->window);
8010         if (win) {
8011                 xcb_icccm_get_wm_normal_hints_reply(conn,
8012                     xcb_icccm_get_wm_normal_hints(conn, win->id),
8013                     &win->sh, NULL);
8014                 adjust_font(win);
8015                 if (font_adjusted) {
8016                         stack();
8017                         xcb_flush(conn);
8018                 }
8019         }
8020 }
8021
8022 void
8023 destroynotify(xcb_destroy_notify_event_t *e)
8024 {
8025         struct ws_win           *win;
8026
8027         DNPRINTF(SWM_D_EVENT, "destroynotify: window: 0x%x\n", e->window);
8028
8029         if ((win = find_window(e->window)) == NULL) {
8030                 if ((win = find_unmanaged_window(e->window)) == NULL)
8031                         return;
8032                 free_window(win);
8033                 return;
8034         }
8035
8036         if (focus_mode != SWM_FOCUS_FOLLOW) {
8037                 /* If we were focused, make sure we focus on something else. */
8038                 if (win == win->ws->focus)
8039                         win->ws->focus_pending = get_focus_prev(win);
8040         }
8041
8042         unmanage_window(win);
8043         stack();
8044
8045         if (focus_mode != SWM_FOCUS_FOLLOW) {
8046                 if (win->ws->focus_pending) {
8047                         focus_win(win->ws->focus_pending);
8048                         win->ws->focus_pending = NULL;
8049                 }
8050         }
8051
8052         free_window(win);
8053
8054         focus_flush();
8055 }
8056
8057 #ifdef SWM_DEBUG
8058 char *
8059 get_notify_detail_label(uint8_t detail)
8060 {
8061         char *label;
8062
8063         switch (detail) {
8064         case XCB_NOTIFY_DETAIL_ANCESTOR:
8065                 label = "Ancestor";
8066                 break;
8067         case XCB_NOTIFY_DETAIL_VIRTUAL:
8068                 label = "Virtual";
8069                 break;
8070         case XCB_NOTIFY_DETAIL_INFERIOR:
8071                 label = "Inferior";
8072                 break;
8073         case XCB_NOTIFY_DETAIL_NONLINEAR:
8074                 label = "Nonlinear";
8075                 break;
8076         case XCB_NOTIFY_DETAIL_NONLINEAR_VIRTUAL:
8077                 label = "NonlinearVirtual";
8078                 break;
8079         case XCB_NOTIFY_DETAIL_POINTER:
8080                 label = "Pointer";
8081                 break;
8082         case XCB_NOTIFY_DETAIL_POINTER_ROOT:
8083                 label = "PointerRoot";
8084                 break;
8085         case XCB_NOTIFY_DETAIL_NONE:
8086                 label = "None";
8087                 break;
8088         default:
8089                 label = "Unknown";
8090         }
8091
8092         return label;
8093 }
8094
8095 char *
8096 get_notify_mode_label(uint8_t mode)
8097 {
8098         char *label;
8099
8100         switch (mode) {
8101         case XCB_NOTIFY_MODE_NORMAL:
8102                 label = "Normal";
8103                 break;
8104         case XCB_NOTIFY_MODE_GRAB:
8105                 label = "Grab";
8106                 break;
8107         case XCB_NOTIFY_MODE_UNGRAB:
8108                 label = "Ungrab";
8109                 break;
8110         case XCB_NOTIFY_MODE_WHILE_GRABBED:
8111                 label = "WhileGrabbed";
8112                 break;
8113         default:
8114                 label = "Unknown";
8115         }
8116
8117         return label;
8118 }
8119 #endif
8120
8121 void
8122 enternotify(xcb_enter_notify_event_t *e)
8123 {
8124         struct ws_win           *win;
8125         struct swm_region       *r;
8126
8127         DNPRINTF(SWM_D_FOCUS, "enternotify: time: %u, win (x,y): 0x%x "
8128             "(%d,%d), mode: %s(%d), detail: %s(%d), root (x,y): 0x%x (%d,%d), "
8129             "child: 0x%x, same_screen_focus: %s, state: %d\n",
8130             e->time, e->event, e->event_x, e->event_y,
8131             get_notify_mode_label(e->mode), e->mode,
8132             get_notify_detail_label(e->detail), e->detail,
8133             e->root, e->root_x, e->root_y, e->child,
8134             YESNO(e->same_screen_focus), e->state);
8135
8136         if (focus_mode == SWM_FOCUS_MANUAL &&
8137             e->mode == XCB_NOTIFY_MODE_NORMAL) {
8138                 DNPRINTF(SWM_D_EVENT, "enternotify: manual focus; ignoring.\n");
8139                 return;
8140         }
8141
8142         last_event_time = e->time;
8143
8144         if ((win = find_window(e->event)) == NULL) {
8145                 if (e->event == e->root) {
8146                         /* If no windows on pointer region, then focus root. */
8147                         r = root_to_region(e->root, SWM_CK_POINTER);
8148                         if (r == NULL) {
8149                                 DNPRINTF(SWM_D_EVENT, "enterntoify: "
8150                                     "NULL region; ignoring.\n");
8151                                 return;
8152                         }
8153
8154                         focus_region(r);
8155                 } else {
8156                         DNPRINTF(SWM_D_EVENT, "enternotify: window is NULL; "
8157                             "ignoring\n");
8158                         return;
8159                 }
8160         } else {
8161                 focus_win(get_focus_magic(win));
8162         }
8163
8164         focus_flush();
8165 }
8166
8167 #ifdef SWM_DEBUG
8168 void
8169 leavenotify(xcb_leave_notify_event_t *e)
8170 {
8171         DNPRINTF(SWM_D_FOCUS, "leavenotify: time: %u, win (x,y): 0x%x "
8172             "(%d,%d), mode: %s(%d), detail: %s(%d), root (x,y): 0x%x (%d,%d), "
8173             "child: 0x%x, same_screen_focus: %s, state: %d\n",
8174             e->time, e->event, e->event_x, e->event_y,
8175             get_notify_mode_label(e->mode), e->mode,
8176             get_notify_detail_label(e->detail), e->detail,
8177             e->root, e->root_x, e->root_y, e->child,
8178             YESNO(e->same_screen_focus), e->state);
8179 }
8180 #endif
8181
8182 void
8183 mapnotify(xcb_map_notify_event_t *e)
8184 {
8185         struct ws_win           *win;
8186
8187         DNPRINTF(SWM_D_EVENT, "mapnotify: window: 0x%x\n", e->window);
8188
8189         if ((win = find_window(e->window)) == NULL)
8190                 win = manage_window(e->window, 1);
8191
8192         win->mapped = 1;
8193         set_win_state(win, XCB_ICCCM_WM_STATE_NORMAL);
8194
8195         if (focus_mode != SWM_FOCUS_FOLLOW) {
8196                 if (win->ws->focus_pending == win) {
8197                         focus_win(win);
8198                         win->ws->focus_pending = NULL;
8199                         focus_flush();
8200                 }
8201         }
8202
8203         xcb_flush(conn);
8204 }
8205
8206 void
8207 mappingnotify(xcb_mapping_notify_event_t *e)
8208 {
8209         xcb_refresh_keyboard_mapping(syms, e);
8210
8211         if (e->request == XCB_MAPPING_KEYBOARD)
8212                 grabkeys();
8213 }
8214
8215 void
8216 maprequest(xcb_map_request_event_t *e)
8217 {
8218         struct ws_win           *win;
8219         xcb_get_window_attributes_reply_t *war;
8220
8221         DNPRINTF(SWM_D_EVENT, "maprequest: win 0x%x\n",
8222             e->window);
8223
8224         war = xcb_get_window_attributes_reply(conn,
8225             xcb_get_window_attributes(conn, e->window),
8226             NULL);
8227         if (war == NULL) {
8228                 DNPRINTF(SWM_D_EVENT, "maprequest: window lost.\n");
8229                 goto out;
8230         }
8231
8232         if (war->override_redirect) {
8233                 DNPRINTF(SWM_D_EVENT, "maprequest: override_redirect; "
8234                     "skipping.\n");
8235                 goto out;
8236         }
8237
8238         win = manage_window(e->window,
8239             (war->map_state == XCB_MAP_STATE_VIEWABLE));
8240
8241         /* The new window should get focus; prepare. */
8242         if (focus_mode != SWM_FOCUS_FOLLOW)
8243                 win->ws->focus_pending = get_focus_magic(win);
8244
8245         /* All windows need to be mapped if they are in the current workspace.*/
8246         if (win->ws->r)
8247                 stack();
8248
8249         /* Ignore EnterNotify to handle the mapnotify without interference. */
8250         if (focus_mode == SWM_FOCUS_DEFAULT)
8251                 event_drain(XCB_ENTER_NOTIFY);
8252 out:
8253         free(war);
8254         DNPRINTF(SWM_D_EVENT, "maprequest: done.\n");
8255 }
8256
8257 void
8258 motionnotify(xcb_motion_notify_event_t *e)
8259 {
8260         struct swm_region       *r;
8261         int                     i, num_screens;
8262
8263         DNPRINTF(SWM_D_FOCUS, "motionnotify: time: %u, win (x,y): 0x%x "
8264             "(%d,%d), detail: %s(%d), root (x,y): 0x%x (%d,%d), "
8265             "child: 0x%x, same_screen_focus: %s, state: %d\n",
8266             e->time, e->event, e->event_x, e->event_y,
8267             get_notify_detail_label(e->detail), e->detail,
8268             e->root, e->root_x, e->root_y, e->child,
8269             YESNO(e->same_screen), e->state);
8270
8271         last_event_time = e->time;
8272
8273         num_screens = xcb_setup_roots_length(xcb_get_setup(conn));
8274         for (i = 0; i < num_screens; i++)
8275                 if (screens[i].root == e->root)
8276                         break;
8277
8278         TAILQ_FOREACH(r, &screens[i].rl, entry)
8279                 if (X(r) <= e->root_x && e->root_x < MAX_X(r) &&
8280                     Y(r) <= e->root_y && e->root_y < MAX_Y(r))
8281                         break;
8282
8283         focus_region(r);
8284 }
8285
8286 #ifdef SWM_DEBUG
8287 char *
8288 get_atom_name(xcb_atom_t atom)
8289 {
8290         char                            *name = NULL;
8291 #if 0
8292         /*
8293          * This should be disabled during most debugging since
8294          * xcb_get_* causes an xcb_flush.
8295          */
8296         size_t                          len;
8297         xcb_get_atom_name_reply_t       *r;
8298
8299         r = xcb_get_atom_name_reply(conn,
8300             xcb_get_atom_name(conn, atom),
8301             NULL);
8302         if (r) {
8303                 len = xcb_get_atom_name_name_length(r);
8304                 if (len > 0) {
8305                         name = malloc(len + 1);
8306                         if (name) {
8307                                 memcpy(name, xcb_get_atom_name_name(r), len);
8308                                 name[len] = '\0';
8309                         }
8310                 }
8311                 free(r);
8312         }
8313 #else
8314         (void)atom;
8315 #endif
8316         return (name);
8317 }
8318 #endif
8319
8320 void
8321 propertynotify(xcb_property_notify_event_t *e)
8322 {
8323         struct ws_win           *win;
8324 #ifdef SWM_DEBUG
8325         char                    *name;
8326
8327         name = get_atom_name(e->atom);
8328         DNPRINTF(SWM_D_EVENT, "propertynotify: window: 0x%x, atom: %s(%u), "
8329             "time: %#x, state: %u\n", e->window, name, e->atom, e->time,
8330             e->state);
8331         free(name);
8332 #endif
8333         win = find_window(e->window);
8334         if (win == NULL)
8335                 return;
8336
8337         last_event_time = e->time;
8338
8339         if (e->atom == a_swm_iconic) {
8340                 if (e->state == XCB_PROPERTY_NEW_VALUE) {
8341                         if (focus_mode != SWM_FOCUS_FOLLOW)
8342                                 win->ws->focus_pending = get_focus_prev(win);
8343
8344                         unfocus_win(win);
8345                         unmap_window(win);
8346
8347                         if (win->ws->r) {
8348                                 stack();
8349                                 if (focus_mode != SWM_FOCUS_FOLLOW) {
8350                                         focus_win(win->ws->focus_pending);
8351                                         win->ws->focus_pending = NULL;
8352                                 }
8353                                 focus_flush();
8354                         }
8355                 } else if (e->state == XCB_PROPERTY_DELETE) {
8356                         /* The window is no longer iconic, restack ws. */
8357                         if (focus_mode != SWM_FOCUS_FOLLOW)
8358                                 win->ws->focus_pending = get_focus_magic(win);
8359
8360                         stack();
8361
8362                         /* Flush EnterNotify for mapnotify, if needed. */
8363                         focus_flush();
8364                 }
8365         } else if (e->atom == a_state) {
8366                 /* State just changed, make sure it gets focused if mapped. */
8367                 if (e->state == XCB_PROPERTY_NEW_VALUE) {
8368                         if (focus_mode != SWM_FOCUS_FOLLOW) {
8369                                 if (win->mapped &&
8370                                     win->ws->focus_pending == win) {
8371                                         focus_win(win->ws->focus_pending);
8372                                         win->ws->focus_pending = NULL;
8373                                 }
8374                         }
8375                 }
8376         } else if (e->atom == XCB_ATOM_WM_CLASS ||
8377             e->atom == XCB_ATOM_WM_NAME) {
8378                 bar_draw();
8379         }
8380
8381         xcb_flush(conn);
8382 }
8383
8384 void
8385 unmapnotify(xcb_unmap_notify_event_t *e)
8386 {
8387         struct ws_win           *win;
8388         struct workspace        *ws;
8389
8390         DNPRINTF(SWM_D_EVENT, "unmapnotify: window: 0x%x\n", e->window);
8391
8392         /* If we aren't managing the window, then ignore. */
8393         win = find_window(e->window);
8394         if (win == NULL || win->id != e->window)
8395                 return;
8396
8397         ws = win->ws;
8398
8399         if (getstate(e->window) != XCB_ICCCM_WM_STATE_ICONIC)
8400                 set_win_state(win, XCB_ICCCM_WM_STATE_ICONIC);
8401
8402         if (win->mapped) {
8403                 /* window unmapped itself */
8404                 /* do unmap/unfocus/restack and unmanage */
8405                 win->mapped = 0;
8406
8407                 /* If win was focused, make sure to focus on something else. */
8408                 if (win == ws->focus) {
8409                         if (focus_mode != SWM_FOCUS_FOLLOW) {
8410                                 ws->focus_pending = get_focus_prev(win);
8411                                 DNPRINTF(SWM_D_EVENT, "unmapnotify: "
8412                                     "focus_pending: 0x%x\n",
8413                                     WINID(ws->focus_pending));
8414                         }
8415
8416                         unfocus_win(win);
8417                 }
8418
8419                 unmanage_window(win);
8420
8421                 if (ws->r)
8422                         stack();
8423
8424                 if (focus_mode == SWM_FOCUS_FOLLOW) {
8425                         if (ws->r)
8426                                 focus_win(get_pointer_win(ws->r->s->root));
8427                 } else {
8428                         if (ws->focus_pending) {
8429                                 focus_win(ws->focus_pending);
8430                                 ws->focus_pending = NULL;
8431                         }
8432                 }
8433         }
8434
8435         if (getstate(e->window) == XCB_ICCCM_WM_STATE_NORMAL)
8436                 set_win_state(win, XCB_ICCCM_WM_STATE_ICONIC);
8437
8438         focus_flush();
8439 }
8440
8441 #if 0
8442 void
8443 visibilitynotify(xcb_visibility_notify_event_t *e)
8444 {
8445         DNPRINTF(SWM_D_EVENT, "visibilitynotify: window: 0x%x\n",
8446             e->window);
8447 }
8448 #endif
8449
8450 void
8451 clientmessage(xcb_client_message_event_t *e)
8452 {
8453         struct ws_win *win;
8454         xcb_map_request_event_t mre;
8455 #ifdef SWM_DEBUG
8456         char                    *name;
8457
8458         name = get_atom_name(e->type);
8459         DNPRINTF(SWM_D_EVENT, "clientmessage: window: 0x%x, atom: %s(%u)\n",
8460             e->window, name, e->type);
8461         free(name);
8462 #endif
8463         win = find_window(e->window);
8464
8465         if (win == NULL) {
8466                 if (e->type == ewmh[_NET_ACTIVE_WINDOW].atom) {
8467                         /* Manage the window with maprequest. */
8468                         DNPRINTF(SWM_D_EVENT, "clientmessage: request focus on "
8469                             "unmanaged window.\n");
8470                         mre.window = e->window;
8471                         maprequest(&mre);
8472                 }
8473                 return;
8474         }
8475
8476         if (e->type == ewmh[_NET_ACTIVE_WINDOW].atom) {
8477                 DNPRINTF(SWM_D_EVENT, "clientmessage: _NET_ACTIVE_WINDOW\n");
8478                 focus_win(win);
8479         }
8480         if (e->type == ewmh[_NET_CLOSE_WINDOW].atom) {
8481                 DNPRINTF(SWM_D_EVENT, "clientmessage: _NET_CLOSE_WINDOW\n");
8482                 if (win->can_delete)
8483                         client_msg(win, a_delete, 0);
8484                 else
8485                         xcb_kill_client(conn, win->id);
8486         }
8487         if (e->type == ewmh[_NET_MOVERESIZE_WINDOW].atom) {
8488                 DNPRINTF(SWM_D_EVENT,
8489                     "clientmessage: _NET_MOVERESIZE_WINDOW\n");
8490                 if (win->floating) {
8491                         if (e->data.data32[0] & (1<<8)) /* x */
8492                                 X(win) = e->data.data32[1];
8493                         if (e->data.data32[0] & (1<<9)) /* y */
8494                                 Y(win) = e->data.data32[2];
8495                         if (e->data.data32[0] & (1<<10)) /* width */
8496                                 WIDTH(win) = e->data.data32[3];
8497                         if (e->data.data32[0] & (1<<11)) /* height */
8498                                 HEIGHT(win) = e->data.data32[4];
8499
8500                         update_window(win);
8501                 }
8502                 else {
8503                         /* TODO: Change stack sizes */
8504                         /* notify no change was made. */
8505                         config_win(win, NULL);
8506                 }
8507         }
8508         if (e->type == ewmh[_NET_WM_STATE].atom) {
8509                 DNPRINTF(SWM_D_EVENT, "clientmessage: _NET_WM_STATE\n");
8510                 ewmh_update_win_state(win, e->data.data32[1], e->data.data32[0]);
8511                 if (e->data.data32[2])
8512                         ewmh_update_win_state(win, e->data.data32[2],
8513                             e->data.data32[0]);
8514
8515                 stack();
8516         }
8517
8518         focus_flush();
8519 }
8520
8521 void
8522 check_conn(void)
8523 {
8524         int      errcode = xcb_connection_has_error(conn);
8525 #ifdef XCB_CONN_ERROR
8526         char    *s;
8527         switch (errcode) {
8528         case XCB_CONN_ERROR:
8529                 s = "Socket error, pipe error or other stream error.";
8530                 break;
8531         case XCB_CONN_CLOSED_EXT_NOTSUPPORTED:
8532                 s = "Extension not supported.";
8533                 break;
8534         case XCB_CONN_CLOSED_MEM_INSUFFICIENT:
8535                 s = "Insufficient memory.";
8536                 break;
8537         case XCB_CONN_CLOSED_REQ_LEN_EXCEED:
8538                 s = "Request length was exceeded.";
8539                 break;
8540         case XCB_CONN_CLOSED_PARSE_ERR:
8541                 s = "Error parsing display string.";
8542                 break;
8543         default:
8544                 s = "Unknown error.";
8545         }
8546         if (errcode)
8547                 errx(errcode, "X CONNECTION ERROR: %s", s);
8548 #else
8549         if (errcode)
8550                 errx(errcode, "X CONNECTION ERROR");
8551 #endif
8552 }
8553
8554 int
8555 enable_wm(void)
8556 {
8557         int                     num_screens, i;
8558         const uint32_t          val = XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT |
8559             XCB_EVENT_MASK_ENTER_WINDOW;
8560         xcb_screen_t            *sc;
8561         xcb_void_cookie_t       wac;
8562         xcb_generic_error_t     *error;
8563
8564         /* this causes an error if some other window manager is running */
8565         num_screens = xcb_setup_roots_length(xcb_get_setup(conn));
8566         for (i = 0; i < num_screens; i++) {
8567                 if ((sc = get_screen(i)) == NULL)
8568                         errx(1, "ERROR: can't get screen %d.", i);
8569                 DNPRINTF(SWM_D_INIT, "enable_wm: screen %d, root: 0x%x\n",
8570                     i, sc->root);
8571                 wac = xcb_change_window_attributes_checked(conn, sc->root,
8572                     XCB_CW_EVENT_MASK, &val);
8573                 if ((error = xcb_request_check(conn, wac))) {
8574                         DNPRINTF(SWM_D_INIT, "enable_wm: error_code: %u\n",
8575                             error->error_code);
8576                         free(error);
8577                         return 1;
8578                 }
8579
8580                 /* click to focus on empty region */
8581                 xcb_grab_button(conn, 1, sc->root, BUTTONMASK,
8582                     XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC, XCB_WINDOW_NONE,
8583                     XCB_CURSOR_NONE, XCB_BUTTON_INDEX_1, XCB_BUTTON_MASK_ANY);
8584         }
8585
8586         return 0;
8587 }
8588
8589 void
8590 new_region(struct swm_screen *s, int x, int y, int w, int h)
8591 {
8592         struct swm_region       *r, *n;
8593         struct workspace        *ws = NULL;
8594         int                     i;
8595         uint32_t                wa[1];
8596
8597         DNPRINTF(SWM_D_MISC, "new region: screen[%d]:%dx%d+%d+%d\n",
8598              s->idx, w, h, x, y);
8599
8600         /* remove any conflicting regions */
8601         n = TAILQ_FIRST(&s->rl);
8602         while (n) {
8603                 r = n;
8604                 n = TAILQ_NEXT(r, entry);
8605                 if (X(r) < (x + w) && (X(r) + WIDTH(r)) > x &&
8606                     Y(r) < (y + h) && (Y(r) + HEIGHT(r)) > y) {
8607                         if (r->ws->r != NULL)
8608                                 r->ws->old_r = r->ws->r;
8609                         r->ws->r = NULL;
8610                         bar_cleanup(r);
8611                         xcb_destroy_window(conn, r->id);
8612                         TAILQ_REMOVE(&s->rl, r, entry);
8613                         TAILQ_INSERT_TAIL(&s->orl, r, entry);
8614                 }
8615         }
8616
8617         /* search old regions for one to reuse */
8618
8619         /* size + location match */
8620         TAILQ_FOREACH(r, &s->orl, entry)
8621                 if (X(r) == x && Y(r) == y &&
8622                     HEIGHT(r) == h && WIDTH(r) == w)
8623                         break;
8624
8625         /* size match */
8626         TAILQ_FOREACH(r, &s->orl, entry)
8627                 if (HEIGHT(r) == h && WIDTH(r) == w)
8628                         break;
8629
8630         if (r != NULL) {
8631                 TAILQ_REMOVE(&s->orl, r, entry);
8632                 /* try to use old region's workspace */
8633                 if (r->ws->r == NULL)
8634                         ws = r->ws;
8635         } else
8636                 if ((r = calloc(1, sizeof(struct swm_region))) == NULL)
8637                         err(1, "new_region: calloc: failed to allocate memory "
8638                             "for screen");
8639
8640         /* if we don't have a workspace already, find one */
8641         if (ws == NULL) {
8642                 for (i = 0; i < workspace_limit; i++)
8643                         if (s->ws[i].r == NULL) {
8644                                 ws = &s->ws[i];
8645                                 break;
8646                         }
8647         }
8648
8649         if (ws == NULL)
8650                 errx(1, "new_region: no free workspaces");
8651
8652         X(r) = x;
8653         Y(r) = y;
8654         WIDTH(r) = w;
8655         HEIGHT(r) = h;
8656         r->s = s;
8657         r->ws = ws;
8658         r->ws_prior = NULL;
8659         ws->r = r;
8660         outputs++;
8661         TAILQ_INSERT_TAIL(&s->rl, r, entry);
8662
8663         /* Invisible region window to detect pointer events on empty regions. */
8664         r->id = xcb_generate_id(conn);
8665         wa[0] = XCB_EVENT_MASK_POINTER_MOTION |
8666             XCB_EVENT_MASK_POINTER_MOTION_HINT;
8667
8668         xcb_create_window(conn, XCB_COPY_FROM_PARENT, r->id, r->s->root,
8669             X(r), Y(r), WIDTH(r), HEIGHT(r), 0, XCB_WINDOW_CLASS_INPUT_ONLY,
8670             XCB_COPY_FROM_PARENT, XCB_CW_EVENT_MASK, wa);
8671
8672         xcb_map_window(conn, r->id);
8673 }
8674
8675 void
8676 scan_xrandr(int i)
8677 {
8678 #ifdef SWM_XRR_HAS_CRTC
8679         int                                             c;
8680         int                                             ncrtc = 0;
8681 #endif /* SWM_XRR_HAS_CRTC */
8682         struct swm_region                               *r;
8683         int                                             num_screens;
8684         xcb_randr_get_screen_resources_current_cookie_t src;
8685         xcb_randr_get_screen_resources_current_reply_t  *srr;
8686         xcb_randr_get_crtc_info_cookie_t                cic;
8687         xcb_randr_get_crtc_info_reply_t                 *cir = NULL;
8688         xcb_randr_crtc_t                                *crtc;
8689         xcb_screen_t                                    *screen;
8690
8691         DNPRINTF(SWM_D_MISC, "scan_xrandr: screen: %d\n", i);
8692
8693         if ((screen = get_screen(i)) == NULL)
8694                 errx(1, "ERROR: can't get screen %d.", i);
8695
8696         num_screens = xcb_setup_roots_length(xcb_get_setup(conn));
8697         if (i >= num_screens)
8698                 errx(1, "scan_xrandr: invalid screen");
8699
8700         /* remove any old regions */
8701         while ((r = TAILQ_FIRST(&screens[i].rl)) != NULL) {
8702                 r->ws->old_r = r->ws->r = NULL;
8703                 bar_cleanup(r);
8704                 xcb_destroy_window(conn, r->id);
8705                 TAILQ_REMOVE(&screens[i].rl, r, entry);
8706                 TAILQ_INSERT_TAIL(&screens[i].orl, r, entry);
8707
8708                 if (r->s->r_focus == r)
8709                         r->s->r_focus = NULL;
8710         }
8711         outputs = 0;
8712
8713         /* map virtual screens onto physical screens */
8714 #ifdef SWM_XRR_HAS_CRTC
8715         if (xrandr_support) {
8716                 src = xcb_randr_get_screen_resources_current(conn,
8717                     screens[i].root);
8718                 srr = xcb_randr_get_screen_resources_current_reply(conn, src,
8719                     NULL);
8720                 if (srr == NULL) {
8721                         new_region(&screens[i], 0, 0,
8722                             screen->width_in_pixels,
8723                             screen->height_in_pixels);
8724                         goto out;
8725                 } else
8726                         ncrtc = srr->num_crtcs;
8727
8728                 crtc = xcb_randr_get_screen_resources_current_crtcs(srr);
8729                 for (c = 0; c < ncrtc; c++) {
8730                         cic = xcb_randr_get_crtc_info(conn, crtc[c],
8731                             XCB_CURRENT_TIME);
8732                         cir = xcb_randr_get_crtc_info_reply(conn, cic, NULL);
8733                         if (cir == NULL)
8734                                 continue;
8735                         if (cir->num_outputs == 0) {
8736                                 free(cir);
8737                                 continue;
8738                         }
8739
8740                         if (cir->mode == 0)
8741                                 new_region(&screens[i], 0, 0,
8742                                     screen->width_in_pixels,
8743                                     screen->height_in_pixels);
8744                         else
8745                                 new_region(&screens[i],
8746                                     cir->x, cir->y, cir->width, cir->height);
8747                         free(cir);
8748                 }
8749                 free(srr);
8750         }
8751 #endif /* SWM_XRR_HAS_CRTC */
8752
8753         /* If detection failed, create a single region that spans the screen. */
8754         if (TAILQ_EMPTY(&screens[i].rl))
8755                 new_region(&screens[i], 0, 0, screen->width_in_pixels,
8756                     screen->height_in_pixels);
8757
8758 out:
8759         DNPRINTF(SWM_D_MISC, "scan_xrandr: done.\n");
8760 }
8761
8762 void
8763 screenchange(xcb_randr_screen_change_notify_event_t *e)
8764 {
8765         struct swm_region               *r;
8766         int                             i, num_screens;
8767
8768         DNPRINTF(SWM_D_EVENT, "screenchange: root: 0x%x\n", e->root);
8769
8770         num_screens = xcb_setup_roots_length(xcb_get_setup(conn));
8771         /* silly event doesn't include the screen index */
8772         for (i = 0; i < num_screens; i++)
8773                 if (screens[i].root == e->root)
8774                         break;
8775         if (i >= num_screens)
8776                 errx(1, "screenchange: screen not found");
8777
8778         /* brute force for now, just re-enumerate the regions */
8779         scan_xrandr(i);
8780
8781 #ifdef SWM_DEBUG
8782         print_win_geom(e->root);
8783 #endif
8784         /* add bars to all regions */
8785         for (i = 0; i < num_screens; i++) {
8786                 TAILQ_FOREACH(r, &screens[i].rl, entry)
8787                         bar_setup(r);
8788
8789                 if (screens[0].r_focus == NULL) {
8790                         /* Focus on first region. */
8791                         r = TAILQ_FIRST(&screens[0].rl);
8792                         if (r)
8793                                 focus_region(r);
8794                 }
8795         }
8796
8797         stack();
8798         bar_draw();
8799         focus_flush();
8800 }
8801
8802 void
8803 grab_windows(void)
8804 {
8805         struct swm_region       *r = NULL;
8806         xcb_window_t            *wins = NULL, trans;
8807         int                     no;
8808         int                     i, j, num_screens;
8809         uint16_t                state, manage, mapped;
8810
8811         xcb_query_tree_cookie_t                 qtc;
8812         xcb_query_tree_reply_t                  *qtr;
8813         xcb_get_window_attributes_cookie_t      gac;
8814         xcb_get_window_attributes_reply_t       *gar;
8815         xcb_get_property_cookie_t               pc;
8816
8817         DNPRINTF(SWM_D_INIT, "grab_windows: begin\n");
8818         num_screens = xcb_setup_roots_length(xcb_get_setup(conn));
8819         for (i = 0; i < num_screens; i++) {
8820                 qtc = xcb_query_tree(conn, screens[i].root);
8821                 qtr = xcb_query_tree_reply(conn, qtc, NULL);
8822                 if (!qtr)
8823                         continue;
8824                 wins = xcb_query_tree_children(qtr);
8825                 no = xcb_query_tree_children_length(qtr);
8826                 /* attach windows to a region */
8827                 /* normal windows */
8828                 DNPRINTF(SWM_D_INIT, "grab_windows: grab top level windows.\n");
8829                 for (j = 0; j < no; j++) {
8830                         TAILQ_FOREACH(r, &screens[i].rl, entry) {
8831                                 if (r->id == wins[j]) {
8832                                         DNPRINTF(SWM_D_INIT, "grab_windows: "
8833                                             "skip %#x; region input window.\n",
8834                                             wins[j]);
8835                                         break;
8836                                 }
8837                         }
8838
8839                         if (r)
8840                                 continue;
8841
8842                         gac = xcb_get_window_attributes(conn, wins[j]);
8843                         gar = xcb_get_window_attributes_reply(conn, gac, NULL);
8844                         if (!gar) {
8845                                 DNPRINTF(SWM_D_INIT, "grab_windows: skip %#x; "
8846                                     "doesn't exist.\n", wins[j]);
8847                                 continue;
8848                         }
8849
8850                         if (gar->override_redirect) {
8851                                 DNPRINTF(SWM_D_INIT, "grab_windows: skip %#x; "
8852                                     "override_redirect set.\n", wins[j]);
8853                                 free(gar);
8854                                 continue;
8855                         }
8856
8857                         pc = xcb_icccm_get_wm_transient_for(conn, wins[j]);
8858                         if (xcb_icccm_get_wm_transient_for_reply(conn, pc,
8859                             &trans, NULL)) {
8860                                 DNPRINTF(SWM_D_INIT, "grab_windows: skip %#x; "
8861                                     "is transient for %#x.\n", wins[j], trans);
8862                                 free(gar);
8863                                 continue;
8864                         }
8865
8866                         state = getstate(wins[j]);
8867                         manage = state != XCB_ICCCM_WM_STATE_WITHDRAWN;
8868                         mapped = gar->map_state != XCB_MAP_STATE_UNMAPPED;
8869                         if (mapped || manage)
8870                                 manage_window(wins[j], mapped);
8871                         free(gar);
8872                 }
8873                 /* transient windows */
8874                 DNPRINTF(SWM_D_INIT, "grab_windows: grab transient windows.\n");
8875                 for (j = 0; j < no; j++) {
8876                         gac = xcb_get_window_attributes(conn, wins[j]);
8877                         gar = xcb_get_window_attributes_reply(conn, gac, NULL);
8878                         if (!gar) {
8879                                 DNPRINTF(SWM_D_INIT, "grab_windows: skip %#x; "
8880                                     "doesn't exist.\n", wins[j]);
8881                                 continue;
8882                         }
8883
8884                         if (gar->override_redirect) {
8885                                 DNPRINTF(SWM_D_INIT, "grab_windows: skip %#x; "
8886                                     "override_redirect set.\n", wins[j]);
8887                                 free(gar);
8888                                 continue;
8889                         }
8890
8891                         state = getstate(wins[j]);
8892                         manage = state != XCB_ICCCM_WM_STATE_WITHDRAWN;
8893                         mapped = gar->map_state != XCB_MAP_STATE_UNMAPPED;
8894                         pc = xcb_icccm_get_wm_transient_for(conn, wins[j]);
8895                         if (xcb_icccm_get_wm_transient_for_reply(conn, pc,
8896                             &trans, NULL) && manage)
8897                                 manage_window(wins[j], mapped);
8898                         free(gar);
8899                 }
8900                 free(qtr);
8901         }
8902         DNPRINTF(SWM_D_INIT, "grab_windows: done.\n");
8903 }
8904
8905 void
8906 setup_screens(void)
8907 {
8908         int                     i, j, k, num_screens;
8909         struct workspace        *ws;
8910         uint32_t                gcv[1], wa[1];
8911         const xcb_query_extension_reply_t *qep;
8912         xcb_screen_t                            *screen;
8913         xcb_randr_query_version_cookie_t        c;
8914         xcb_randr_query_version_reply_t         *r;
8915
8916         num_screens = xcb_setup_roots_length(xcb_get_setup(conn));
8917         if ((screens = calloc(num_screens,
8918              sizeof(struct swm_screen))) == NULL)
8919                 err(1, "setup_screens: calloc: failed to allocate memory for "
8920                     "screens");
8921
8922         /* initial Xrandr setup */
8923         xrandr_support = 0;
8924         qep = xcb_get_extension_data(conn, &xcb_randr_id);
8925         if (qep->present) {
8926                 c = xcb_randr_query_version(conn, 1, 1);
8927                 r = xcb_randr_query_version_reply(conn, c, NULL);
8928                 if (r) {
8929                         if (r->major_version >= 1) {
8930                                 xrandr_support = 1;
8931                                 xrandr_eventbase = qep->first_event;
8932                         }
8933                         free(r);
8934                 }
8935         }
8936
8937         wa[0] = cursors[XC_LEFT_PTR].cid;
8938
8939         /* map physical screens */
8940         for (i = 0; i < num_screens; i++) {
8941                 DNPRINTF(SWM_D_WS, "setup_screens: init screen: %d\n", i);
8942                 screens[i].idx = i;
8943                 screens[i].r_focus = NULL;
8944
8945                 TAILQ_INIT(&screens[i].rl);
8946                 TAILQ_INIT(&screens[i].orl);
8947                 if ((screen = get_screen(i)) == NULL)
8948                         errx(1, "ERROR: can't get screen %d.", i);
8949                 screens[i].root = screen->root;
8950
8951                 /* set default colors */
8952                 setscreencolor("red", i + 1, SWM_S_COLOR_FOCUS);
8953                 setscreencolor("rgb:88/88/88", i + 1, SWM_S_COLOR_UNFOCUS);
8954                 setscreencolor("rgb:00/80/80", i + 1, SWM_S_COLOR_BAR_BORDER);
8955                 setscreencolor("rgb:00/40/40", i + 1,
8956                     SWM_S_COLOR_BAR_BORDER_UNFOCUS);
8957                 setscreencolor("black", i + 1, SWM_S_COLOR_BAR);
8958                 setscreencolor("rgb:a0/a0/a0", i + 1, SWM_S_COLOR_BAR_FONT);
8959
8960                 /* create graphics context on screen */
8961                 screens[i].bar_gc = xcb_generate_id(conn);
8962                 gcv[0] = 0;
8963                 xcb_create_gc(conn, screens[i].bar_gc, screens[i].root,
8964                     XCB_GC_GRAPHICS_EXPOSURES, gcv);
8965
8966                 /* set default cursor */
8967                 xcb_change_window_attributes(conn, screens[i].root,
8968                     XCB_CW_CURSOR, wa);
8969
8970                 /* init all workspaces */
8971                 /* XXX these should be dynamically allocated too */
8972                 for (j = 0; j < SWM_WS_MAX; j++) {
8973                         ws = &screens[i].ws[j];
8974                         ws->idx = j;
8975                         ws->name = NULL;
8976                         ws->bar_enabled = 1;
8977                         ws->focus = NULL;
8978                         ws->focus_prev = NULL;
8979                         ws->focus_pending = NULL;
8980                         ws->r = NULL;
8981                         ws->old_r = NULL;
8982                         TAILQ_INIT(&ws->winlist);
8983                         TAILQ_INIT(&ws->unmanagedlist);
8984
8985                         for (k = 0; layouts[k].l_stack != NULL; k++)
8986                                 if (layouts[k].l_config != NULL)
8987                                         layouts[k].l_config(ws,
8988                                             SWM_ARG_ID_STACKINIT);
8989                         ws->cur_layout = &layouts[0];
8990                         ws->cur_layout->l_string(ws);
8991                 }
8992
8993                 scan_xrandr(i);
8994
8995                 if (xrandr_support)
8996                         xcb_randr_select_input(conn, screens[i].root,
8997                             XCB_RANDR_NOTIFY_MASK_SCREEN_CHANGE);
8998         }
8999 }
9000
9001 void
9002 setup_globals(void)
9003 {
9004         if ((bar_fonts = strdup(SWM_BAR_FONTS)) == NULL)
9005                 err(1, "setup_globals: strdup: failed to allocate memory.");
9006
9007         if ((clock_format = strdup("%a %b %d %R %Z %Y")) == NULL)
9008                 err(1, "setup_globals: strdup: failed to allocate memory.");
9009
9010         if ((syms = xcb_key_symbols_alloc(conn)) == NULL)
9011                 errx(1, "unable to allocate key symbols");
9012
9013         a_state = get_atom_from_string("WM_STATE");
9014         a_prot = get_atom_from_string("WM_PROTOCOLS");
9015         a_delete = get_atom_from_string("WM_DELETE_WINDOW");
9016         a_takefocus = get_atom_from_string("WM_TAKE_FOCUS");
9017         a_wmname = get_atom_from_string("WM_NAME");
9018         a_netwmname = get_atom_from_string("_NET_WM_NAME");
9019         a_utf8_string = get_atom_from_string("UTF8_STRING");
9020         a_string = get_atom_from_string("STRING");
9021         a_swm_iconic = get_atom_from_string("_SWM_ICONIC");
9022         a_swm_ws = get_atom_from_string("_SWM_WS");
9023 }
9024
9025 void
9026 workaround(void)
9027 {
9028         int                     i, num_screens;
9029         xcb_atom_t              netwmcheck;
9030         xcb_window_t            root, win;
9031
9032         /* work around sun jdk bugs, code from wmname */
9033         netwmcheck = get_atom_from_string("_NET_SUPPORTING_WM_CHECK");
9034
9035         num_screens = xcb_setup_roots_length(xcb_get_setup(conn));
9036         for (i = 0; i < num_screens; i++) {
9037                 root = screens[i].root;
9038
9039                 win = xcb_generate_id(conn);
9040                 xcb_create_window(conn, XCB_COPY_FROM_PARENT, win, root,
9041                     0, 0, 1, 1, 0, XCB_WINDOW_CLASS_INPUT_OUTPUT,
9042                     XCB_COPY_FROM_PARENT, 0, NULL);
9043
9044                 xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root,
9045                     netwmcheck, XCB_ATOM_WINDOW, 32, 1, &win);
9046                 xcb_change_property(conn, XCB_PROP_MODE_REPLACE, win,
9047                     netwmcheck, XCB_ATOM_WINDOW, 32, 1, &win);
9048                 xcb_change_property(conn, XCB_PROP_MODE_REPLACE, win,
9049                     a_netwmname, a_utf8_string, 8, strlen("LG3D"), "LG3D");
9050         }
9051 }
9052
9053 void
9054 shutdown_cleanup(void)
9055 {
9056         int i, num_screens;
9057
9058         /* disable alarm because the following code may not be interrupted */
9059         alarm(0);
9060         if (signal(SIGALRM, SIG_IGN) == SIG_ERR)
9061                 err(1, "can't disable alarm");
9062
9063         bar_extra_stop();
9064         unmap_all();
9065
9066         cursors_cleanup();
9067
9068         teardown_ewmh();
9069
9070         num_screens = xcb_setup_roots_length(xcb_get_setup(conn));
9071         for (i = 0; i < num_screens; ++i) {
9072                 if (screens[i].bar_gc != 0)
9073                         xcb_free_gc(conn, screens[i].bar_gc);
9074                 if (!bar_font_legacy)
9075                         XftColorFree(display, DefaultVisual(display, i),
9076                             DefaultColormap(display, i), &bar_font_color);
9077         }
9078
9079         if (bar_font_legacy)
9080                 XFreeFontSet(display, bar_fs);
9081         else {
9082                 XftFontClose(display, bar_font);
9083         }
9084
9085         xcb_key_symbols_free(syms);
9086         xcb_flush(conn);
9087         xcb_disconnect(conn);
9088 }
9089
9090 void
9091 event_error(xcb_generic_error_t *e)
9092 {
9093         (void)e;
9094
9095         DNPRINTF(SWM_D_EVENT, "event_error: %s(%u) from %s(%u), sequence: %u, "
9096             "resource_id: %u, minor_code: %u\n",
9097             xcb_event_get_error_label(e->error_code), e->error_code,
9098             xcb_event_get_request_label(e->major_code), e->major_code,
9099             e->sequence, e->resource_id, e->minor_code);
9100 }
9101
9102 void
9103 event_handle(xcb_generic_event_t *evt)
9104 {
9105         uint8_t type = XCB_EVENT_RESPONSE_TYPE(evt);
9106
9107         DNPRINTF(SWM_D_EVENT, "XCB Event: %s(%d), seq %u\n",
9108             xcb_event_get_label(XCB_EVENT_RESPONSE_TYPE(evt)),
9109             XCB_EVENT_RESPONSE_TYPE(evt), evt->sequence);
9110
9111         switch (type) {
9112 #define EVENT(type, callback) case type: callback((void *)evt); return
9113         EVENT(0, event_error);
9114         EVENT(XCB_BUTTON_PRESS, buttonpress);
9115         /*EVENT(XCB_BUTTON_RELEASE, buttonpress);*/
9116         /*EVENT(XCB_CIRCULATE_NOTIFY, );*/
9117         /*EVENT(XCB_CIRCULATE_REQUEST, );*/
9118         EVENT(XCB_CLIENT_MESSAGE, clientmessage);
9119         /*EVENT(XCB_COLORMAP_NOTIFY, );*/
9120         EVENT(XCB_CONFIGURE_NOTIFY, configurenotify);
9121         EVENT(XCB_CONFIGURE_REQUEST, configurerequest);
9122         /*EVENT(XCB_CREATE_NOTIFY, );*/
9123         EVENT(XCB_DESTROY_NOTIFY, destroynotify);
9124         EVENT(XCB_ENTER_NOTIFY, enternotify);
9125         EVENT(XCB_EXPOSE, expose);
9126 #ifdef SWM_DEBUG
9127         EVENT(XCB_FOCUS_IN, focusin);
9128         EVENT(XCB_FOCUS_OUT, focusout);
9129 #endif
9130         /*EVENT(XCB_GRAPHICS_EXPOSURE, );*/
9131         /*EVENT(XCB_GRAVITY_NOTIFY, );*/
9132         EVENT(XCB_KEY_PRESS, keypress);
9133         /*EVENT(XCB_KEY_RELEASE, keypress);*/
9134         /*EVENT(XCB_KEYMAP_NOTIFY, );*/
9135 #ifdef SWM_DEBUG
9136         EVENT(XCB_LEAVE_NOTIFY, leavenotify);
9137 #endif
9138         EVENT(XCB_MAP_NOTIFY, mapnotify);
9139         EVENT(XCB_MAP_REQUEST, maprequest);
9140         EVENT(XCB_MAPPING_NOTIFY, mappingnotify);
9141         EVENT(XCB_MOTION_NOTIFY, motionnotify);
9142         /*EVENT(XCB_NO_EXPOSURE, );*/
9143         EVENT(XCB_PROPERTY_NOTIFY, propertynotify);
9144         /*EVENT(XCB_REPARENT_NOTIFY, );*/
9145         /*EVENT(XCB_RESIZE_REQUEST, );*/
9146         /*EVENT(XCB_SELECTION_CLEAR, );*/
9147         /*EVENT(XCB_SELECTION_NOTIFY, );*/
9148         /*EVENT(XCB_SELECTION_REQUEST, );*/
9149         EVENT(XCB_UNMAP_NOTIFY, unmapnotify);
9150         /*EVENT(XCB_VISIBILITY_NOTIFY, visibilitynotify);*/
9151 #undef EVENT
9152         }
9153         if (type - xrandr_eventbase == XCB_RANDR_SCREEN_CHANGE_NOTIFY)
9154                 screenchange((void *)evt);
9155 }
9156
9157 int
9158 main(int argc, char *argv[])
9159 {
9160         struct swm_region       *r;
9161         char                    conf[PATH_MAX], *cfile = NULL;
9162         struct stat             sb;
9163         int                     xfd, i, num_screens, startup = 1;
9164         struct sigaction        sact;
9165         xcb_generic_event_t     *evt;
9166         struct timeval          tv;
9167         fd_set                  rd;
9168         int                     rd_max;
9169         int                     stdin_ready = 0;
9170         int                     num_readable;
9171
9172         /* suppress unused warning since var is needed */
9173         (void)argc;
9174
9175         time_started = time(NULL);
9176
9177         start_argv = argv;
9178         warnx("Welcome to spectrwm V%s Build: %s", SPECTRWM_VERSION, buildstr);
9179         if (!setlocale(LC_CTYPE, "") || !setlocale(LC_TIME, ""))
9180                 warnx("no locale support");
9181
9182         /* handle some signals */
9183         bzero(&sact, sizeof(sact));
9184         sigemptyset(&sact.sa_mask);
9185         sact.sa_flags = 0;
9186         sact.sa_handler = sighdlr;
9187         sigaction(SIGINT, &sact, NULL);
9188         sigaction(SIGQUIT, &sact, NULL);
9189         sigaction(SIGTERM, &sact, NULL);
9190         sigaction(SIGHUP, &sact, NULL);
9191
9192         sact.sa_handler = sighdlr;
9193         sact.sa_flags = SA_NOCLDSTOP;
9194         sigaction(SIGCHLD, &sact, NULL);
9195
9196         if (!(display = XOpenDisplay(0)))
9197                 errx(1, "can not open display");
9198
9199         conn = XGetXCBConnection(display);
9200         if (xcb_connection_has_error(conn))
9201                 errx(1, "can not get XCB connection");
9202
9203         XSetEventQueueOwner(display, XCBOwnsEventQueue);
9204
9205         xcb_prefetch_extension_data(conn, &xcb_randr_id);
9206         xfd = xcb_get_file_descriptor(conn);
9207
9208         /* look for local and global conf file */
9209         pwd = getpwuid(getuid());
9210         if (pwd == NULL)
9211                 errx(1, "invalid user: %d", getuid());
9212
9213         xcb_grab_server(conn);
9214         xcb_aux_sync(conn);
9215
9216         /* flush all events */
9217         while ((evt = xcb_poll_for_event(conn))) {
9218                 if (XCB_EVENT_RESPONSE_TYPE(evt) == 0)
9219                         event_handle(evt);
9220                 free(evt);
9221         }
9222
9223         if (enable_wm() != 0)
9224                 errx(1, "another window manager is currently running");
9225
9226         /* Load Xcursors and/or cursorfont glyph cursors. */
9227         cursors_load();
9228
9229         xcb_aux_sync(conn);
9230
9231         setup_globals();
9232         setup_screens();
9233         setup_keys();
9234         setup_quirks();
9235         setup_spawn();
9236
9237         /* load config */
9238         for (i = 0; ; i++) {
9239                 conf[0] = '\0';
9240                 switch (i) {
9241                 case 0:
9242                         /* ~ */
9243                         snprintf(conf, sizeof conf, "%s/.%s",
9244                             pwd->pw_dir, SWM_CONF_FILE);
9245                         break;
9246                 case 1:
9247                         /* global */
9248                         snprintf(conf, sizeof conf, "/etc/%s",
9249                             SWM_CONF_FILE);
9250                         break;
9251                 case 2:
9252                         /* ~ compat */
9253                         snprintf(conf, sizeof conf, "%s/.%s",
9254                             pwd->pw_dir, SWM_CONF_FILE_OLD);
9255                         break;
9256                 case 3:
9257                         /* global compat */
9258                         snprintf(conf, sizeof conf, "/etc/%s",
9259                             SWM_CONF_FILE_OLD);
9260                         break;
9261                 default:
9262                         goto noconfig;
9263                 }
9264
9265                 if (strlen(conf) && stat(conf, &sb) != -1)
9266                         if (S_ISREG(sb.st_mode)) {
9267                                 cfile = conf;
9268                                 break;
9269                         }
9270         }
9271 noconfig:
9272
9273         /* load conf (if any) */
9274         if (cfile)
9275                 conf_load(cfile, SWM_CONF_DEFAULT);
9276
9277         setup_ewmh();
9278         /* set some values to work around bad programs */
9279         workaround();
9280         /* grab existing windows (before we build the bars) */
9281         grab_windows();
9282
9283         if (getenv("SWM_STARTED") == NULL)
9284                 setenv("SWM_STARTED", "YES", 1);
9285
9286         /* setup all bars */
9287         num_screens = xcb_setup_roots_length(xcb_get_setup(conn));
9288         for (i = 0; i < num_screens; i++)
9289                 TAILQ_FOREACH(r, &screens[i].rl, entry)
9290                         bar_setup(r);
9291
9292         grabkeys();
9293         stack();
9294         bar_draw();
9295
9296         xcb_ungrab_server(conn);
9297         xcb_flush(conn);
9298
9299         rd_max = xfd > STDIN_FILENO ? xfd : STDIN_FILENO;
9300
9301         while (running) {
9302                 while ((evt = xcb_poll_for_event(conn))) {
9303                         if (!running)
9304                                 goto done;
9305                         event_handle(evt);
9306                         free(evt);
9307                 }
9308
9309                 /* If just (re)started, set default focus if needed. */
9310                 if (startup) {
9311                         startup = 0;
9312
9313                         if (focus_mode != SWM_FOCUS_FOLLOW) {
9314                                 r = TAILQ_FIRST(&screens[0].rl);
9315                                 if (r) {
9316                                         focus_region(r);
9317                                         focus_flush();
9318                                 }
9319                                 continue;
9320                         }
9321                 }
9322
9323                 FD_ZERO(&rd);
9324
9325                 if (bar_extra)
9326                         FD_SET(STDIN_FILENO, &rd);
9327
9328                 FD_SET(xfd, &rd);
9329                 tv.tv_sec = 1;
9330                 tv.tv_usec = 0;
9331                 num_readable = select(rd_max + 1, &rd, NULL, NULL, &tv);
9332                 if (num_readable == -1 && errno != EINTR) {
9333                         DNPRINTF(SWM_D_MISC, "select failed");
9334                 } else if (num_readable > 0 && FD_ISSET(STDIN_FILENO, &rd)) {
9335                         stdin_ready = 1;
9336                 }
9337
9338                 if (restart_wm)
9339                         restart(NULL, NULL);
9340
9341                 if (search_resp)
9342                         search_do_resp();
9343
9344                 if (!running)
9345                         goto done;
9346
9347                 if (stdin_ready) {
9348                         stdin_ready = 0;
9349                         if (bar_extra_update()) {
9350                                 bar_draw();
9351                                 xcb_flush(conn);
9352                         }
9353                 }
9354         }
9355 done:
9356         shutdown_cleanup();
9357
9358         return (0);
9359 }