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