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