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