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