JasonWoof Got questions, comments, patches, etc.? Contact Jason Woofenden
add more verbiage to layout
[spectrwm.git] / scrotwm.c
1 /* $scrotwm$ */
2 /*
3  * Copyright (c) 2009-2010-2011 Marco Peereboom <marco@peereboom.us>
4  * Copyright (c) 2009-2010-2011 Ryan McBride <mcbride@countersiege.com>
5  * Copyright (c) 2009 Darrin Chandler <dwchandler@stilyagin.com>
6  * Copyright (c) 2009 Pierre-Yves Ritschard <pyr@spootnik.org>
7  * Copyright (c) 2011 Jason L. Wright <jason@thought.net>
8  *
9  * Permission to use, copy, modify, and distribute this software for any
10  * purpose with or without fee is hereby granted, provided that the above
11  * copyright notice and this permission notice appear in all copies.
12  *
13  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
14  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
15  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
16  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
17  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
18  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
19  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20  */
21 /*
22  * Much code and ideas taken from dwm under the following license:
23  * MIT/X Consortium License
24  *
25  * 2006-2008 Anselm R Garbe <garbeam at gmail dot com>
26  * 2006-2007 Sander van Dijk <a dot h dot vandijk at gmail dot com>
27  * 2006-2007 Jukka Salmi <jukka at salmi dot ch>
28  * 2007 Premysl Hruby <dfenze at gmail dot com>
29  * 2007 Szabolcs Nagy <nszabolcs at gmail dot com>
30  * 2007 Christof Musik <christof at sendfax dot de>
31  * 2007-2008 Enno Gottox Boland <gottox at s01 dot de>
32  * 2007-2008 Peter Hartlich <sgkkr at hartlich dot com>
33  * 2008 Martin Hurton <martin dot hurton at gmail dot com>
34  *
35  * Permission is hereby granted, free of charge, to any person obtaining a
36  * copy of this software and associated documentation files (the "Software"),
37  * to deal in the Software without restriction, including without limitation
38  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
39  * and/or sell copies of the Software, and to permit persons to whom the
40  * Software is furnished to do so, subject to the following conditions:
41  *
42  * The above copyright notice and this permission notice shall be included in
43  * all copies or substantial portions of the Software.
44  *
45  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
46  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
47  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
48  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
49  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
50  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
51  * DEALINGS IN THE SOFTWARE.
52  */
53
54 static const char       *cvstag =
55     "$scrotwm$";
56
57 #define SWM_VERSION     "0.9.32"
58
59 #include <stdio.h>
60 #include <stdlib.h>
61 #include <err.h>
62 #include <errno.h>
63 #include <fcntl.h>
64 #include <locale.h>
65 #include <unistd.h>
66 #include <time.h>
67 #include <signal.h>
68 #include <string.h>
69 #include <util.h>
70 #include <pwd.h>
71 #include <paths.h>
72 #include <ctype.h>
73
74 #include <sys/types.h>
75 #include <sys/time.h>
76 #include <sys/stat.h>
77 #include <sys/wait.h>
78 #include <sys/queue.h>
79 #include <sys/param.h>
80 #include <sys/select.h>
81
82 #include <X11/cursorfont.h>
83 #include <X11/keysym.h>
84 #include <X11/Xatom.h>
85 #include <X11/Xlib.h>
86 #include <X11/Xproto.h>
87 #include <X11/Xutil.h>
88 #include <X11/extensions/Xrandr.h>
89
90 #ifdef __OSX__
91 #include <osx.h>
92 #endif
93
94 #if RANDR_MAJOR < 1
95 #  error XRandR versions less than 1.0 are not supported
96 #endif
97
98 #if RANDR_MAJOR >= 1
99 #if RANDR_MINOR >= 2
100 #define SWM_XRR_HAS_CRTC
101 #endif
102 #endif
103
104 /*#define SWM_DEBUG*/
105 #ifdef SWM_DEBUG
106 #define DPRINTF(x...)           do { if (swm_debug) fprintf(stderr, x); } while (0)
107 #define DNPRINTF(n,x...)        do { if (swm_debug & n) fprintf(stderr, x); } while (0)
108 #define SWM_D_MISC              0x0001
109 #define SWM_D_EVENT             0x0002
110 #define SWM_D_WS                0x0004
111 #define SWM_D_FOCUS             0x0008
112 #define SWM_D_MOVE              0x0010
113 #define SWM_D_STACK             0x0020
114 #define SWM_D_MOUSE             0x0040
115 #define SWM_D_PROP              0x0080
116 #define SWM_D_CLASS             0x0100
117 #define SWM_D_KEY               0x0200
118 #define SWM_D_QUIRK             0x0400
119 #define SWM_D_SPAWN             0x0800
120 #define SWM_D_EVENTQ            0x1000
121 #define SWM_D_CONF              0x2000
122
123 u_int32_t               swm_debug = 0
124                             | SWM_D_MISC
125                             | SWM_D_EVENT
126                             | SWM_D_WS
127                             | SWM_D_FOCUS
128                             | SWM_D_MOVE
129                             | SWM_D_STACK
130                             | SWM_D_MOUSE
131                             | SWM_D_PROP
132                             | SWM_D_CLASS
133                             | SWM_D_KEY
134                             | SWM_D_QUIRK
135                             | SWM_D_SPAWN
136                             | SWM_D_EVENTQ
137                             | SWM_D_CONF
138                             ;
139 #else
140 #define DPRINTF(x...)
141 #define DNPRINTF(n,x...)
142 #endif
143
144 #define LENGTH(x)               (sizeof x / sizeof x[0])
145 #define MODKEY                  Mod1Mask
146 #define CLEANMASK(mask)         (mask & ~(numlockmask | LockMask))
147 #define BUTTONMASK              (ButtonPressMask|ButtonReleaseMask)
148 #define MOUSEMASK               (BUTTONMASK|PointerMotionMask)
149 #define SWM_PROPLEN             (16)
150 #define SWM_FUNCNAME_LEN        (32)
151 #define SWM_KEYS_LEN            (255)
152 #define SWM_QUIRK_LEN           (64)
153 #define X(r)                    (r)->g.x
154 #define Y(r)                    (r)->g.y
155 #define WIDTH(r)                (r)->g.w
156 #define HEIGHT(r)               (r)->g.h
157 #define SWM_MAX_FONT_STEPS      (3)
158 #define WINID(w)                (w ? w->id : 0)
159
160 #define SWM_FOCUS_DEFAULT       (0)
161 #define SWM_FOCUS_SYNERGY       (1)
162 #define SWM_FOCUS_FOLLOW        (2)
163
164 #ifndef SWM_LIB
165 #define SWM_LIB                 "/usr/local/lib/libswmhack.so"
166 #endif
167
168 char                    **start_argv;
169 Atom                    astate;
170 Atom                    aprot;
171 Atom                    adelete;
172 Atom                    takefocus;
173 Atom                    a_wmname;
174 Atom                    a_utf8_string;
175 Atom                    a_string;
176 Atom                    a_swm_iconic;
177 volatile sig_atomic_t   running = 1;
178 volatile sig_atomic_t   restart_wm = 0;
179 int                     outputs = 0;
180 int                     last_focus_event = FocusOut;
181 int                     (*xerrorxlib)(Display *, XErrorEvent *);
182 int                     other_wm;
183 int                     ss_enabled = 0;
184 int                     xrandr_support;
185 int                     xrandr_eventbase;
186 unsigned int            numlockmask = 0;
187 Display                 *display;
188
189 int                     cycle_empty = 0;
190 int                     cycle_visible = 0;
191 int                     term_width = 0;
192 int                     font_adjusted = 0;
193 unsigned int            mod_key = MODKEY;
194
195 /* dmenu search */
196 struct swm_region       *search_r;
197 int                     select_list_pipe[2];
198 int                     select_resp_pipe[2];
199 pid_t                   searchpid;
200 volatile sig_atomic_t   search_resp;
201
202 /* dialog windows */
203 double                  dialog_ratio = .6;
204 /* status bar */
205 #define SWM_BAR_MAX     (256)
206 char                    *bar_argv[] = { NULL, NULL };
207 int                     bar_pipe[2];
208 char                    bar_ext[SWM_BAR_MAX];
209 char                    bar_vertext[SWM_BAR_MAX];
210 int                     bar_version = 0;
211 sig_atomic_t            bar_alarm = 0;
212 int                     bar_delay = 30;
213 int                     bar_enabled = 1;
214 int                     bar_border_width = 1;
215 int                     bar_at_bottom = 0;
216 int                     bar_extra = 1;
217 int                     bar_extra_running = 0;
218 int                     bar_verbose = 1;
219 int                     bar_height = 0;
220 int                     stack_enabled = 1;
221 int                     clock_enabled = 1;
222 char                    *clock_format = NULL;
223 int                     title_name_enabled = 0;
224 int                     title_class_enabled = 0;
225 int                     window_name_enabled = 0;
226 int                     focus_mode = SWM_FOCUS_DEFAULT;
227 int                     disable_border = 0;
228 int                     border_width = 1;
229 pid_t                   bar_pid;
230 GC                      bar_gc;
231 XGCValues               bar_gcv;
232 int                     bar_fidx = 0;
233 XFontStruct             *bar_fs;
234 char                    *bar_fonts[] = { NULL, NULL, NULL, NULL };/* XXX Make fully dynamic */
235 char                    *spawn_term[] = { NULL, NULL };         /* XXX Make fully dynamic */
236
237 #define SWM_MENU_FN     (2)
238 #define SWM_MENU_NB     (4)
239 #define SWM_MENU_NF     (6)
240 #define SWM_MENU_SB     (8)
241 #define SWM_MENU_SF     (10)
242
243 /* layout manager data */
244 struct swm_geometry {
245         int                     x;
246         int                     y;
247         int                     w;
248         int                     h;
249 };
250
251 struct swm_screen;
252 struct workspace;
253
254 /* virtual "screens" */
255 struct swm_region {
256         TAILQ_ENTRY(swm_region) entry;
257         struct swm_geometry     g;
258         struct workspace        *ws;    /* current workspace on this region */
259         struct workspace        *ws_prior; /* prior workspace on this region */
260         struct swm_screen       *s;     /* screen idx */
261         Window                  bar_window;
262 };
263 TAILQ_HEAD(swm_region_list, swm_region);
264
265 struct ws_win {
266         TAILQ_ENTRY(ws_win)     entry;
267         Window                  id;
268         Window                  transient;
269         struct ws_win           *child_trans;   /* transient child window */
270         struct swm_geometry     g;              /* current geometry */
271         struct swm_geometry     g_float;        /* geometry when floating */
272         struct swm_geometry     rg_float;       /* region geom when floating */
273         int                     g_floatvalid;   /* flag: geometry in g_float is valid */
274         int                     floatmaxed;     /* flag: floater was maxed in max_stack */
275         int                     floating;
276         int                     manual;
277         int                     iconic;
278         unsigned int            ewmh_flags;
279         int                     font_size_boundary[SWM_MAX_FONT_STEPS];
280         int                     font_steps;
281         int                     last_inc;
282         int                     can_delete;
283         int                     take_focus;
284         int                     java;
285         unsigned long           quirks;
286         struct workspace        *ws;    /* always valid */
287         struct swm_screen       *s;     /* always valid, never changes */
288         XWindowAttributes       wa;
289         XSizeHints              sh;
290         XClassHint              ch;
291         XWMHints                *hints;
292 };
293 TAILQ_HEAD(ws_win_list, ws_win);
294
295 /* pid goo */
296 struct pid_e {
297         TAILQ_ENTRY(pid_e)      entry;
298         long                    pid;
299         int                     ws;
300 };
301 TAILQ_HEAD(pid_list, pid_e);
302 struct pid_list                 pidlist = TAILQ_HEAD_INITIALIZER(pidlist);
303
304 /* layout handlers */
305 void    stack(void);
306 void    vertical_config(struct workspace *, int);
307 void    vertical_stack(struct workspace *, struct swm_geometry *);
308 void    horizontal_config(struct workspace *, int);
309 void    horizontal_stack(struct workspace *, struct swm_geometry *);
310 void    max_stack(struct workspace *, struct swm_geometry *);
311
312 struct ws_win *find_window(Window);
313
314 void    grabbuttons(struct ws_win *, int);
315 void    new_region(struct swm_screen *, int, int, int, int);
316 void    unmanage_window(struct ws_win *);
317 long    getstate(Window);
318
319 struct layout {
320         void            (*l_stack)(struct workspace *, struct swm_geometry *);
321         void            (*l_config)(struct workspace *, int);
322         u_int32_t       flags;
323 #define SWM_L_FOCUSPREV         (1<<0)
324 #define SWM_L_MAPONFOCUS        (1<<1)
325         char            *name;
326 } layouts[] =  {
327         /* stack,               configure */
328         { vertical_stack,       vertical_config,        0,      "[|]" },
329         { horizontal_stack,     horizontal_config,      0,      "[-]" },
330         { max_stack,            NULL,
331           SWM_L_MAPONFOCUS | SWM_L_FOCUSPREV,                   "[ ]" },
332         { NULL,                 NULL,                   0,      NULL  },
333 };
334
335 /* position of max_stack mode in the layouts array, index into layouts! */
336 #define SWM_V_STACK             (0)
337 #define SWM_H_STACK             (1)
338 #define SWM_MAX_STACK           (2)
339
340 #define SWM_H_SLICE             (32)
341 #define SWM_V_SLICE             (32)
342
343 /* define work spaces */
344 struct workspace {
345         int                     idx;            /* workspace index */
346         int                     always_raise;   /* raise windows on focus */
347         struct layout           *cur_layout;    /* current layout handlers */
348         struct ws_win           *focus;         /* may be NULL */
349         struct ws_win           *focus_prev;    /* may be NULL */
350         struct swm_region       *r;             /* may be NULL */
351         struct swm_region       *old_r;         /* may be NULL */
352         struct ws_win_list      winlist;        /* list of windows in ws */
353         struct ws_win_list      unmanagedlist;  /* list of dead windows in ws */
354
355         /* stacker state */
356         struct {
357                                 int horizontal_msize;
358                                 int horizontal_mwin;
359                                 int horizontal_stacks;
360                                 int vertical_msize;
361                                 int vertical_mwin;
362                                 int vertical_stacks;
363         } l_state;
364 };
365
366 enum    { SWM_S_COLOR_BAR, SWM_S_COLOR_BAR_BORDER, SWM_S_COLOR_BAR_FONT,
367           SWM_S_COLOR_FOCUS, SWM_S_COLOR_UNFOCUS, SWM_S_COLOR_MAX };
368
369 /* physical screen mapping */
370 #define SWM_WS_MAX              (10)
371 struct swm_screen {
372         int                     idx;    /* screen index */
373         struct swm_region_list  rl;     /* list of regions on this screen */
374         struct swm_region_list  orl;    /* list of old regions */
375         Window                  root;
376         struct workspace        ws[SWM_WS_MAX];
377
378         /* colors */
379         struct {
380                 unsigned long   color;
381                 char            *name;
382         } c[SWM_S_COLOR_MAX];
383 };
384 struct swm_screen       *screens;
385 int                     num_screens;
386
387 /* args to functions */
388 union arg {
389         int                     id;
390 #define SWM_ARG_ID_FOCUSNEXT    (0)
391 #define SWM_ARG_ID_FOCUSPREV    (1)
392 #define SWM_ARG_ID_FOCUSMAIN    (2)
393 #define SWM_ARG_ID_FOCUSCUR     (4)
394 #define SWM_ARG_ID_SWAPNEXT     (10)
395 #define SWM_ARG_ID_SWAPPREV     (11)
396 #define SWM_ARG_ID_SWAPMAIN     (12)
397 #define SWM_ARG_ID_MOVELAST     (13)
398 #define SWM_ARG_ID_MASTERSHRINK (20)
399 #define SWM_ARG_ID_MASTERGROW   (21)
400 #define SWM_ARG_ID_MASTERADD    (22)
401 #define SWM_ARG_ID_MASTERDEL    (23)
402 #define SWM_ARG_ID_STACKRESET   (30)
403 #define SWM_ARG_ID_STACKINIT    (31)
404 #define SWM_ARG_ID_CYCLEWS_UP   (40)
405 #define SWM_ARG_ID_CYCLEWS_DOWN (41)
406 #define SWM_ARG_ID_CYCLESC_UP   (42)
407 #define SWM_ARG_ID_CYCLESC_DOWN (43)
408 #define SWM_ARG_ID_STACKINC     (50)
409 #define SWM_ARG_ID_STACKDEC     (51)
410 #define SWM_ARG_ID_SS_ALL       (60)
411 #define SWM_ARG_ID_SS_WINDOW    (61)
412 #define SWM_ARG_ID_DONTCENTER   (70)
413 #define SWM_ARG_ID_CENTER       (71)
414 #define SWM_ARG_ID_KILLWINDOW   (80)
415 #define SWM_ARG_ID_DELETEWINDOW (81)
416         char                    **argv;
417 };
418
419 void    focus(struct swm_region *, union arg *);
420 void    focus_magic(struct ws_win *);
421
422 /* quirks */
423 struct quirk {
424         char                    *class;
425         char                    *name;
426         unsigned long           quirk;
427 #define SWM_Q_FLOAT             (1<<0)  /* float this window */
428 #define SWM_Q_TRANSSZ           (1<<1)  /* transiend window size too small */
429 #define SWM_Q_ANYWHERE          (1<<2)  /* don't position this window */
430 #define SWM_Q_XTERM_FONTADJ     (1<<3)  /* adjust xterm fonts when resizing */
431 #define SWM_Q_FULLSCREEN        (1<<4)  /* remove border */
432 #define SWM_Q_FOCUSPREV         (1<<5)  /* focus on caller */
433 };
434 int                             quirks_size = 0, quirks_length = 0;
435 struct quirk                    *quirks = NULL;
436
437 /*
438  * Supported EWMH hints should be added to
439  * both the enum and the ewmh array
440  */
441 enum { _NET_ACTIVE_WINDOW, _NET_MOVERESIZE_WINDOW, _NET_CLOSE_WINDOW,
442     _NET_WM_WINDOW_TYPE, _NET_WM_WINDOW_TYPE_DOCK,
443     _NET_WM_WINDOW_TYPE_TOOLBAR, _NET_WM_WINDOW_TYPE_UTILITY,
444     _NET_WM_WINDOW_TYPE_SPLASH, _NET_WM_WINDOW_TYPE_DIALOG,
445     _NET_WM_WINDOW_TYPE_NORMAL, _NET_WM_STATE,
446     _NET_WM_STATE_MAXIMIZED_HORZ, _NET_WM_STATE_MAXIMIZED_VERT,
447     _NET_WM_STATE_SKIP_TASKBAR, _NET_WM_STATE_SKIP_PAGER,
448     _NET_WM_STATE_HIDDEN, _NET_WM_STATE_ABOVE, _SWM_WM_STATE_MANUAL,
449     _NET_WM_STATE_FULLSCREEN, _NET_WM_ALLOWED_ACTIONS, _NET_WM_ACTION_MOVE,
450     _NET_WM_ACTION_RESIZE, _NET_WM_ACTION_FULLSCREEN, _NET_WM_ACTION_CLOSE,
451     SWM_EWMH_HINT_MAX };
452
453 struct ewmh_hint {
454         char    *name;
455         Atom     atom;
456 } ewmh[SWM_EWMH_HINT_MAX] =     {
457     /* must be in same order as in the enum */
458     {"_NET_ACTIVE_WINDOW", None},
459     {"_NET_MOVERESIZE_WINDOW", None},
460     {"_NET_CLOSE_WINDOW", None},
461     {"_NET_WM_WINDOW_TYPE", None},
462     {"_NET_WM_WINDOW_TYPE_DOCK", None},
463     {"_NET_WM_WINDOW_TYPE_TOOLBAR", None},
464     {"_NET_WM_WINDOW_TYPE_UTILITY", None},
465     {"_NET_WM_WINDOW_TYPE_SPLASH", None},
466     {"_NET_WM_WINDOW_TYPE_DIALOG", None},
467     {"_NET_WM_WINDOW_TYPE_NORMAL", None},
468     {"_NET_WM_STATE", None},
469     {"_NET_WM_STATE_MAXIMIZED_HORZ", None},
470     {"_NET_WM_STATE_MAXIMIZED_VERT", None},
471     {"_NET_WM_STATE_SKIP_TASKBAR", None},
472     {"_NET_WM_STATE_SKIP_PAGER", None},
473     {"_NET_WM_STATE_HIDDEN", None},
474     {"_NET_WM_STATE_ABOVE", None},
475     {"_SWM_WM_STATE_MANUAL", None},
476     {"_NET_WM_STATE_FULLSCREEN", None},
477     {"_NET_WM_ALLOWED_ACTIONS", None},
478     {"_NET_WM_ACTION_MOVE", None},
479     {"_NET_WM_ACTION_RESIZE", None},
480     {"_NET_WM_ACTION_FULLSCREEN", None},
481     {"_NET_WM_ACTION_CLOSE", None},
482 };
483
484 void    store_float_geom(struct ws_win *win, struct swm_region *r);
485 int     floating_toggle_win(struct ws_win *win);
486 void    spawn_select(struct swm_region *, union arg *, char *, int *);
487
488 int
489 get_property(Window id, Atom atom, long count, Atom type,
490     unsigned long *n, unsigned char **data)
491 {
492         int                     format, status;
493         unsigned long           tmp, extra;
494         unsigned long           *nitems;
495         Atom                    real;
496
497         nitems = n != NULL ? n : &tmp;
498         status = XGetWindowProperty(display, id, atom, 0L, count, False, type,
499             &real, &format, nitems, &extra, data);
500
501         if (status != Success)
502                 return False;
503         if (real != type)
504                 return False;
505
506         return True;
507 }
508
509 void
510 update_iconic(struct ws_win *win, int newv)
511 {
512         int32_t v = newv;
513         Atom iprop;
514
515         win->iconic = newv;
516
517         iprop = XInternAtom(display, "_SWM_ICONIC", False);
518         if (!iprop)
519                 return;
520         if (newv)
521                 XChangeProperty(display, win->id, iprop, XA_INTEGER, 32,
522                     PropModeReplace, (unsigned char *)&v, 1);
523         else
524                 XDeleteProperty(display, win->id, iprop);
525 }
526
527 int
528 get_iconic(struct ws_win *win)
529 {
530         int32_t v = 0;
531         int retfmt, status;
532         Atom iprop, rettype;
533         unsigned long nitems, extra;
534         unsigned char *prop = NULL;
535
536         iprop = XInternAtom(display, "_SWM_ICONIC", False);
537         if (!iprop)
538                 goto out;
539         status = XGetWindowProperty(display, win->id, iprop, 0L, 1L,
540             False, XA_INTEGER, &rettype, &retfmt, &nitems, &extra, &prop);
541         if (status != Success)
542                 goto out;
543         if (rettype != XA_INTEGER || retfmt != 32)
544                 goto out;
545         if (nitems != 1)
546                 goto out;
547         v = *((int32_t *)prop);
548
549 out:
550         if (prop != NULL)
551                 XFree(prop);
552         return (v);
553 }
554
555 void
556 setup_ewmh(void)
557 {
558         int                     i,j;
559         Atom                    sup_list;
560
561         sup_list = XInternAtom(display, "_NET_SUPPORTED", False);
562
563         for (i = 0; i < LENGTH(ewmh); i++)
564                 ewmh[i].atom = XInternAtom(display, ewmh[i].name, False);
565
566         for (i = 0; i < ScreenCount(display); i++) {
567                 /* Support check window will be created by workaround(). */
568
569                 /* Report supported atoms */
570                 XDeleteProperty(display, screens[i].root, sup_list);
571                 for (j = 0; j < LENGTH(ewmh); j++)
572                         XChangeProperty(display, screens[i].root,
573                             sup_list, XA_ATOM, 32,
574                             PropModeAppend, (unsigned char *)&ewmh[j].atom,1);
575         }
576 }
577
578 void
579 teardown_ewmh(void)
580 {
581         int                     i, success;
582         unsigned char           *data = NULL;
583         unsigned long           n;
584         Atom                    sup_check, sup_list;
585         Window                  id;
586
587         sup_check = XInternAtom(display, "_NET_SUPPORTING_WM_CHECK", False);
588         sup_list = XInternAtom(display, "_NET_SUPPORTED", False);
589
590         for (i = 0; i < ScreenCount(display); i++) {
591                 /* Get the support check window and destroy it */
592                 success = get_property(screens[i].root, sup_check, 1, XA_WINDOW,
593                     &n, &data);
594
595                 if (success) {
596                         id = data[0];
597                         XDestroyWindow(display, id);
598                         XDeleteProperty(display, screens[i].root, sup_check);
599                         XDeleteProperty(display, screens[i].root, sup_list);
600                 }
601
602                 XFree(data);
603         }
604 }
605
606 void
607 ewmh_autoquirk(struct ws_win *win)
608 {
609         int                     success, i;
610         unsigned long           *data = NULL;
611         unsigned long           n;
612         Atom                    type;
613
614         success = get_property(win->id, ewmh[_NET_WM_WINDOW_TYPE].atom, (~0L),
615             XA_ATOM, &n, (unsigned char **)&data);
616
617         if (!success) {
618                 XFree(data);
619                 return;
620         }
621
622         for (i = 0; i < n; i++) {
623                 type = data[i];
624                 if (type == ewmh[_NET_WM_WINDOW_TYPE_NORMAL].atom)
625                         break;
626                 if (type == ewmh[_NET_WM_WINDOW_TYPE_DOCK].atom ||
627                     type == ewmh[_NET_WM_WINDOW_TYPE_TOOLBAR].atom ||
628                     type == ewmh[_NET_WM_WINDOW_TYPE_UTILITY].atom) {
629                         win->floating = 1;
630                         win->quirks = SWM_Q_FLOAT | SWM_Q_ANYWHERE;
631                         break;
632                 }
633                 if (type == ewmh[_NET_WM_WINDOW_TYPE_SPLASH].atom ||
634                     type == ewmh[_NET_WM_WINDOW_TYPE_DIALOG].atom) {
635                         win->floating = 1;
636                         win->quirks = SWM_Q_FLOAT;
637                         break;
638                 }
639         }
640
641         XFree(data);
642 }
643
644 #define SWM_EWMH_ACTION_COUNT_MAX       (6)
645 #define EWMH_F_FULLSCREEN               (1<<0)
646 #define EWMH_F_ABOVE                    (1<<1)
647 #define EWMH_F_HIDDEN                   (1<<2)
648 #define EWMH_F_SKIP_PAGER               (1<<3)
649 #define EWMH_F_SKIP_TASKBAR             (1<<4)
650 #define SWM_F_MANUAL                    (1<<5)
651
652 int
653 ewmh_set_win_fullscreen(struct ws_win *win, int fs)
654 {
655         struct swm_geometry     rg;
656
657         if (!win->ws->r)
658                 return 0;
659
660         if (!win->floating)
661                 return 0;
662
663         DNPRINTF(SWM_D_MISC, "ewmh_set_win_fullscreen: win 0x%lx fs: %d\n",
664             win->id, fs);
665
666         rg = win->ws->r->g;
667
668         if (fs) {
669                 store_float_geom(win, win->ws->r);
670
671                 win->g.x = rg.x;
672                 win->g.y = rg.y;
673                 win->g.w = rg.w;
674                 win->g.h = rg.h;
675         } else {
676                 if (win->g_floatvalid) {
677                         /* refloat at last floating relative position */
678                         win->g.x = win->g_float.x - win->rg_float.x + rg.x;
679                         win->g.y = win->g_float.y - win->rg_float.y + rg.y;
680                         win->g.w = win->g_float.w;
681                         win->g.h = win->g_float.h;
682                 }
683         }
684
685         return 1;
686 }
687
688 void
689 ewmh_update_actions(struct ws_win *win)
690 {
691         Atom                    actions[SWM_EWMH_ACTION_COUNT_MAX];
692         int                     n = 0;
693
694         if (win == NULL)
695                 return;
696
697         actions[n++] = ewmh[_NET_WM_ACTION_CLOSE].atom;
698
699         if (win->floating) {
700                 actions[n++] = ewmh[_NET_WM_ACTION_MOVE].atom;
701                 actions[n++] = ewmh[_NET_WM_ACTION_RESIZE].atom;
702         }
703
704         XChangeProperty(display, win->id, ewmh[_NET_WM_ALLOWED_ACTIONS].atom,
705             XA_ATOM, 32, PropModeReplace, (unsigned char *)actions, n);
706 }
707
708 #define _NET_WM_STATE_REMOVE    0    /* remove/unset property */
709 #define _NET_WM_STATE_ADD       1    /* add/set property */
710 #define _NET_WM_STATE_TOGGLE    2    /* toggle property */
711
712 void
713 ewmh_update_win_state(struct ws_win *win, long state, long action)
714 {
715         unsigned int            mask = 0;
716         unsigned int            changed = 0;
717         unsigned int            orig_flags;
718
719         if (win == NULL)
720                 return;
721
722         if (state == ewmh[_NET_WM_STATE_FULLSCREEN].atom)
723                 mask = EWMH_F_FULLSCREEN;
724         if (state == ewmh[_NET_WM_STATE_ABOVE].atom)
725                 mask = EWMH_F_ABOVE;
726         if (state == ewmh[_SWM_WM_STATE_MANUAL].atom)
727                 mask = SWM_F_MANUAL;
728         if (state == ewmh[_NET_WM_STATE_SKIP_PAGER].atom)
729                 mask = EWMH_F_SKIP_PAGER;
730         if (state == ewmh[_NET_WM_STATE_SKIP_TASKBAR].atom)
731                 mask = EWMH_F_SKIP_TASKBAR;
732
733
734         orig_flags = win->ewmh_flags;
735
736         switch (action) {
737         case _NET_WM_STATE_REMOVE:
738                 win->ewmh_flags &= ~mask;
739                 break;
740         case _NET_WM_STATE_ADD:
741                 win->ewmh_flags |= mask;
742                 break;
743         case _NET_WM_STATE_TOGGLE:
744                 win->ewmh_flags ^= mask;
745                 break;
746         }
747
748         changed = (win->ewmh_flags & mask) ^ (orig_flags & mask) ? 1 : 0;
749
750         if (state == ewmh[_NET_WM_STATE_ABOVE].atom)
751                 if (changed)
752                         if (!floating_toggle_win(win))
753                                 win->ewmh_flags = orig_flags; /* revert */
754         if (state == ewmh[_SWM_WM_STATE_MANUAL].atom)
755                 if (changed)
756                         win->manual = (win->ewmh_flags & SWM_F_MANUAL) != 0;
757         if (state == ewmh[_NET_WM_STATE_FULLSCREEN].atom)
758                 if (changed)
759                         if (!ewmh_set_win_fullscreen(win,
760                             win->ewmh_flags & EWMH_F_FULLSCREEN))
761                                 win->ewmh_flags = orig_flags; /* revert */
762
763         XDeleteProperty(display, win->id, ewmh[_NET_WM_STATE].atom);
764
765         if (win->ewmh_flags & EWMH_F_FULLSCREEN)
766                 XChangeProperty(display, win->id, ewmh[_NET_WM_STATE].atom,
767                     XA_ATOM, 32, PropModeAppend,
768                     (unsigned char *)&ewmh[_NET_WM_STATE_FULLSCREEN].atom, 1);
769         if (win->ewmh_flags & EWMH_F_SKIP_PAGER)
770                 XChangeProperty(display, win->id, ewmh[_NET_WM_STATE].atom,
771                     XA_ATOM, 32, PropModeAppend,
772                     (unsigned char *)&ewmh[_NET_WM_STATE_SKIP_PAGER].atom, 1);
773         if (win->ewmh_flags & EWMH_F_SKIP_TASKBAR)
774                 XChangeProperty(display, win->id, ewmh[_NET_WM_STATE].atom,
775                     XA_ATOM, 32, PropModeAppend,
776                     (unsigned char *)&ewmh[_NET_WM_STATE_SKIP_TASKBAR].atom, 1);
777         if (win->ewmh_flags & EWMH_F_ABOVE)
778                 XChangeProperty(display, win->id, ewmh[_NET_WM_STATE].atom,
779                     XA_ATOM, 32, PropModeAppend,
780                     (unsigned char *)&ewmh[_NET_WM_STATE_ABOVE].atom, 1);
781         if (win->ewmh_flags & SWM_F_MANUAL)
782                 XChangeProperty(display, win->id, ewmh[_NET_WM_STATE].atom,
783                     XA_ATOM, 32, PropModeAppend,
784                     (unsigned char *)&ewmh[_SWM_WM_STATE_MANUAL].atom, 1);
785 }
786
787 void
788 ewmh_get_win_state(struct ws_win *win)
789 {
790         int                     success, i;
791         unsigned long           n;
792         Atom                    *states;
793
794         if (win == NULL)
795                 return;
796
797         win->ewmh_flags = 0;
798         if (win->floating)
799                 win->ewmh_flags |= EWMH_F_ABOVE;
800         if (win->manual)
801                 win->ewmh_flags |= SWM_F_MANUAL;
802
803         success = get_property(win->id, ewmh[_NET_WM_STATE].atom,
804             (~0L), XA_ATOM, &n, (unsigned char **)&states);
805
806         if (!success)
807                 return;
808
809         for (i = 0; i < n; i++)
810                 ewmh_update_win_state(win, states[i], _NET_WM_STATE_ADD);
811
812         XFree(states);
813 }
814
815 /* events */
816 #ifdef SWM_DEBUG
817 void
818 dumpevent(XEvent *e)
819 {
820         char                    *name = NULL;
821
822         switch (e->type) {
823         case KeyPress:
824                 name = "KeyPress";
825                 break;
826         case KeyRelease:
827                 name = "KeyRelease";
828                 break;
829         case ButtonPress:
830                 name = "ButtonPress";
831                 break;
832         case ButtonRelease:
833                 name = "ButtonRelease";
834                 break;
835         case MotionNotify:
836                 name = "MotionNotify";
837                 break;
838         case EnterNotify:
839                 name = "EnterNotify";
840                 break;
841         case LeaveNotify:
842                 name = "LeaveNotify";
843                 break;
844         case FocusIn:
845                 name = "FocusIn";
846                 break;
847         case FocusOut:
848                 name = "FocusOut";
849                 break;
850         case KeymapNotify:
851                 name = "KeymapNotify";
852                 break;
853         case Expose:
854                 name = "Expose";
855                 break;
856         case GraphicsExpose:
857                 name = "GraphicsExpose";
858                 break;
859         case NoExpose:
860                 name = "NoExpose";
861                 break;
862         case VisibilityNotify:
863                 name = "VisibilityNotify";
864                 break;
865         case CreateNotify:
866                 name = "CreateNotify";
867                 break;
868         case DestroyNotify:
869                 name = "DestroyNotify";
870                 break;
871         case UnmapNotify:
872                 name = "UnmapNotify";
873                 break;
874         case MapNotify:
875                 name = "MapNotify";
876                 break;
877         case MapRequest:
878                 name = "MapRequest";
879                 break;
880         case ReparentNotify:
881                 name = "ReparentNotify";
882                 break;
883         case ConfigureNotify:
884                 name = "ConfigureNotify";
885                 break;
886         case ConfigureRequest:
887                 name = "ConfigureRequest";
888                 break;
889         case GravityNotify:
890                 name = "GravityNotify";
891                 break;
892         case ResizeRequest:
893                 name = "ResizeRequest";
894                 break;
895         case CirculateNotify:
896                 name = "CirculateNotify";
897                 break;
898         case CirculateRequest:
899                 name = "CirculateRequest";
900                 break;
901         case PropertyNotify:
902                 name = "PropertyNotify";
903                 break;
904         case SelectionClear:
905                 name = "SelectionClear";
906                 break;
907         case SelectionRequest:
908                 name = "SelectionRequest";
909                 break;
910         case SelectionNotify:
911                 name = "SelectionNotify";
912                 break;
913         case ColormapNotify:
914                 name = "ColormapNotify";
915                 break;
916         case ClientMessage:
917                 name = "ClientMessage";
918                 break;
919         case MappingNotify:
920                 name = "MappingNotify";
921                 break;
922         }
923
924         if (name)
925                 DNPRINTF(SWM_D_EVENTQ ,"window: %lu event: %s (%d), %d "
926                     "remaining\n",
927                     e->xany.window, name, e->type, QLength(display));
928         else
929                 DNPRINTF(SWM_D_EVENTQ, "window: %lu unknown event %d, %d "
930                     "remaining\n",
931                     e->xany.window, e->type, QLength(display));
932 }
933
934 void
935 dumpwins(struct swm_region *r, union arg *args)
936 {
937         struct ws_win           *win;
938         unsigned int            state;
939         XWindowAttributes       wa;
940
941         if (r->ws == NULL) {
942                 fprintf(stderr, "invalid workspace\n");
943                 return;
944         }
945
946         fprintf(stderr, "=== managed window list ws %02d ===\n", r->ws->idx);
947
948         TAILQ_FOREACH(win, &r->ws->winlist, entry) {
949                 state = getstate(win->id);
950                 if (!XGetWindowAttributes(display, win->id, &wa))
951                         fprintf(stderr, "window: %lu failed "
952                             "XGetWindowAttributes\n", win->id);
953                 fprintf(stderr, "window: %lu map_state: %d state: %d "
954                     "transient: %lu\n",
955                     win->id, wa.map_state, state, win->transient);
956         }
957
958         fprintf(stderr, "===== unmanaged window list =====\n");
959         TAILQ_FOREACH(win, &r->ws->unmanagedlist, entry) {
960                 state = getstate(win->id);
961                 if (!XGetWindowAttributes(display, win->id, &wa))
962                         fprintf(stderr, "window: %lu failed "
963                             "XGetWindowAttributes\n", win->id);
964                 fprintf(stderr, "window: %lu map_state: %d state: %d "
965                     "transient: %lu\n",
966                     win->id, wa.map_state, state, win->transient);
967         }
968
969         fprintf(stderr, "=================================\n");
970 }
971 #else
972 #define dumpevent(e)
973 void
974 dumpwins(struct swm_region *r, union arg *args)
975 {
976 }
977 #endif /* SWM_DEBUG */
978
979 void                    expose(XEvent *);
980 void                    keypress(XEvent *);
981 void                    buttonpress(XEvent *);
982 void                    configurerequest(XEvent *);
983 void                    configurenotify(XEvent *);
984 void                    destroynotify(XEvent *);
985 void                    enternotify(XEvent *);
986 void                    focusevent(XEvent *);
987 void                    mapnotify(XEvent *);
988 void                    mappingnotify(XEvent *);
989 void                    maprequest(XEvent *);
990 void                    propertynotify(XEvent *);
991 void                    unmapnotify(XEvent *);
992 void                    visibilitynotify(XEvent *);
993 void                    clientmessage(XEvent *);
994
995 void                    (*handler[LASTEvent])(XEvent *) = {
996                                 [Expose] = expose,
997                                 [KeyPress] = keypress,
998                                 [ButtonPress] = buttonpress,
999                                 [ConfigureRequest] = configurerequest,
1000                                 [ConfigureNotify] = configurenotify,
1001                                 [DestroyNotify] = destroynotify,
1002                                 [EnterNotify] = enternotify,
1003                                 [FocusIn] = focusevent,
1004                                 [FocusOut] = focusevent,
1005                                 [MapNotify] = mapnotify,
1006                                 [MappingNotify] = mappingnotify,
1007                                 [MapRequest] = maprequest,
1008                                 [PropertyNotify] = propertynotify,
1009                                 [UnmapNotify] = unmapnotify,
1010                                 [VisibilityNotify] = visibilitynotify,
1011                                 [ClientMessage] = clientmessage,
1012 };
1013
1014 void
1015 sighdlr(int sig)
1016 {
1017         int                     saved_errno, status;
1018         pid_t                   pid;
1019
1020         saved_errno = errno;
1021
1022         switch (sig) {
1023         case SIGCHLD:
1024                 while ((pid = waitpid(WAIT_ANY, &status, WNOHANG)) != 0) {
1025                         if (pid == -1) {
1026                                 if (errno == EINTR)
1027                                         continue;
1028 #ifdef SWM_DEBUG
1029                                 if (errno != ECHILD)
1030                                         warn("sighdlr: waitpid");
1031 #endif /* SWM_DEBUG */
1032                                 break;
1033                         }
1034                         if (pid == searchpid)
1035                                 search_resp = 1;
1036
1037 #ifdef SWM_DEBUG
1038                         if (WIFEXITED(status)) {
1039                                 if (WEXITSTATUS(status) != 0)
1040                                         warnx("sighdlr: child exit status: %d",
1041                                             WEXITSTATUS(status));
1042                         } else
1043                                 warnx("sighdlr: child is terminated "
1044                                     "abnormally");
1045 #endif /* SWM_DEBUG */
1046                 }
1047                 break;
1048
1049         case SIGHUP:
1050                 restart_wm = 1;
1051                 break;
1052         case SIGINT:
1053         case SIGTERM:
1054         case SIGQUIT:
1055                 running = 0;
1056                 break;
1057         }
1058
1059         errno = saved_errno;
1060 }
1061
1062 struct pid_e *
1063 find_pid(long pid)
1064 {
1065         struct pid_e            *p = NULL;
1066
1067         DNPRINTF(SWM_D_MISC, "find_pid: %lu\n", pid);
1068
1069         if (pid == 0)
1070                 return (NULL);
1071
1072         TAILQ_FOREACH(p, &pidlist, entry) {
1073                 if (p->pid == pid)
1074                         return (p);
1075         }
1076
1077         return (NULL);
1078 }
1079
1080 unsigned long
1081 name_to_color(char *colorname)
1082 {
1083         Colormap                cmap;
1084         Status                  status;
1085         XColor                  screen_def, exact_def;
1086         unsigned long           result = 0;
1087         char                    cname[32] = "#";
1088
1089         cmap = DefaultColormap(display, screens[0].idx);
1090         status = XAllocNamedColor(display, cmap, colorname,
1091             &screen_def, &exact_def);
1092         if (!status) {
1093                 strlcat(cname, colorname + 2, sizeof cname - 1);
1094                 status = XAllocNamedColor(display, cmap, cname, &screen_def,
1095                     &exact_def);
1096         }
1097         if (status)
1098                 result = screen_def.pixel;
1099         else
1100                 fprintf(stderr, "color '%s' not found.\n", colorname);
1101
1102         return (result);
1103 }
1104
1105 void
1106 setscreencolor(char *val, int i, int c)
1107 {
1108         if (i > 0 && i <= ScreenCount(display)) {
1109                 screens[i - 1].c[c].color = name_to_color(val);
1110                 free(screens[i - 1].c[c].name);
1111                 if ((screens[i - 1].c[c].name = strdup(val)) == NULL)
1112                         errx(1, "strdup");
1113         } else if (i == -1) {
1114                 for (i = 0; i < ScreenCount(display); i++) {
1115                         screens[i].c[c].color = name_to_color(val);
1116                         free(screens[i].c[c].name);
1117                         if ((screens[i].c[c].name = strdup(val)) == NULL)
1118                                 errx(1, "strdup");
1119                 }
1120         } else
1121                 errx(1, "invalid screen index: %d out of bounds (maximum %d)\n",
1122                     i, ScreenCount(display));
1123 }
1124
1125 void
1126 custom_region(char *val)
1127 {
1128         unsigned int                    sidx, x, y, w, h;
1129
1130         if (sscanf(val, "screen[%u]:%ux%u+%u+%u", &sidx, &w, &h, &x, &y) != 5)
1131                 errx(1, "invalid custom region, "
1132                     "should be 'screen[<n>]:<n>x<n>+<n>+<n>\n");
1133         if (sidx < 1 || sidx > ScreenCount(display))
1134                 errx(1, "invalid screen index: %d out of bounds (maximum %d)\n",
1135                     sidx, ScreenCount(display));
1136         sidx--;
1137
1138         if (w < 1 || h < 1)
1139                 errx(1, "region %ux%u+%u+%u too small\n", w, h, x, y);
1140
1141         if (x  < 0 || x > DisplayWidth(display, sidx) ||
1142             y < 0 || y > DisplayHeight(display, sidx) ||
1143             w + x > DisplayWidth(display, sidx) ||
1144             h + y > DisplayHeight(display, sidx)) {
1145                 fprintf(stderr, "ignoring region %ux%u+%u+%u "
1146                     "- not within screen boundaries "
1147                     "(%ux%u)\n", w, h, x, y,
1148                     DisplayWidth(display, sidx), DisplayHeight(display, sidx));
1149                 return;
1150         }
1151
1152         new_region(&screens[sidx], x, y, w, h);
1153 }
1154
1155 void
1156 socket_setnonblock(int fd)
1157 {
1158         int                     flags;
1159
1160         if ((flags = fcntl(fd, F_GETFL, 0)) == -1)
1161                 err(1, "fcntl F_GETFL");
1162         flags |= O_NONBLOCK;
1163         if ((flags = fcntl(fd, F_SETFL, flags)) == -1)
1164                 err(1, "fcntl F_SETFL");
1165 }
1166
1167 void
1168 bar_print(struct swm_region *r, char *s)
1169 {
1170         XClearWindow(display, r->bar_window);
1171         XSetForeground(display, bar_gc, r->s->c[SWM_S_COLOR_BAR_FONT].color);
1172         XDrawString(display, r->bar_window, bar_gc, 4, bar_fs->ascent, s,
1173             strlen(s));
1174 }
1175
1176 void
1177 bar_extra_stop(void)
1178 {
1179         if (bar_pipe[0]) {
1180                 close(bar_pipe[0]);
1181                 bzero(bar_pipe, sizeof bar_pipe);
1182         }
1183         if (bar_pid) {
1184                 kill(bar_pid, SIGTERM);
1185                 bar_pid = 0;
1186         }
1187         strlcpy(bar_ext, "", sizeof bar_ext);
1188         bar_extra = 0;
1189 }
1190
1191 void
1192 bar_class_name(char *s, ssize_t sz, struct ws_win *cur_focus)
1193 {
1194         int                     do_class, do_name;
1195         Status                  status;
1196         XClassHint              *xch = NULL;
1197
1198         if ((title_name_enabled == 1 || title_class_enabled == 1) &&
1199             cur_focus != NULL) {
1200                 if ((xch = XAllocClassHint()) == NULL)
1201                         goto out;
1202                 status = XGetClassHint(display, cur_focus->id, xch);
1203                 if (status == BadWindow || status == BadAlloc)
1204                         goto out;
1205                 do_class = (title_class_enabled && xch->res_class != NULL);
1206                 do_name = (title_name_enabled && xch->res_name != NULL);
1207                 if (do_class)
1208                         strlcat(s, xch->res_class, sz);
1209                 if (do_class && do_name)
1210                         strlcat(s, ":", sz);
1211                 if (do_name)
1212                         strlcat(s, xch->res_name, sz);
1213                 strlcat(s, "    ", sz);
1214         }
1215 out:
1216         if (xch)
1217                 XFree(xch);
1218 }
1219
1220 void
1221 bar_window_name(char *s, ssize_t sz, struct ws_win *cur_focus)
1222 {
1223         char                    *title;
1224
1225         if (window_name_enabled && cur_focus != NULL) {
1226                 XFetchName(display, cur_focus->id, &title);
1227                 if (title) {
1228                         if (cur_focus->floating)
1229                                 strlcat(s, "(f) ", sz);
1230                         strlcat(s, title, sz);
1231                         strlcat(s, " ", sz);
1232                         XFree(title);
1233                 }
1234         }
1235 }
1236
1237 void
1238 bar_update(void)
1239 {
1240         time_t                  tmt;
1241         struct tm               tm;
1242         struct swm_region       *r;
1243         int                     i, x;
1244         size_t                  len;
1245         char                    s[SWM_BAR_MAX];
1246         char                    cn[SWM_BAR_MAX];
1247         char                    loc[SWM_BAR_MAX];
1248         char                    *b;
1249         char                    *stack = "";
1250
1251         if (bar_enabled == 0)
1252                 return;
1253         if (bar_extra && bar_extra_running) {
1254                 /* ignore short reads; it'll correct itself */
1255                 while ((b = fgetln(stdin, &len)) != NULL)
1256                         if (b && b[len - 1] == '\n') {
1257                                 b[len - 1] = '\0';
1258                                 strlcpy(bar_ext, b, sizeof bar_ext);
1259                         }
1260                 if (b == NULL && errno != EAGAIN) {
1261                         fprintf(stderr, "bar_extra failed: errno: %d %s\n",
1262                             errno, strerror(errno));
1263                         bar_extra_stop();
1264                 }
1265         } else
1266                 strlcpy(bar_ext, "", sizeof bar_ext);
1267
1268         if (clock_enabled == 0)
1269                 strlcpy(s, "", sizeof s);
1270         else {
1271                 time(&tmt);
1272                 localtime_r(&tmt, &tm);
1273                 strftime(s, sizeof s, clock_format, &tm);
1274                 strlcat(s, "    ", sizeof s);
1275         }
1276
1277         for (i = 0; i < ScreenCount(display); i++) {
1278                 x = 1;
1279                 TAILQ_FOREACH(r, &screens[i].rl, entry) {
1280                         strlcpy(cn, "", sizeof cn);
1281                         if (r && r->ws) {
1282                                 bar_class_name(cn, sizeof cn, r->ws->focus);
1283                                 bar_window_name(cn, sizeof cn, r->ws->focus);
1284                         }
1285
1286                         if (stack_enabled)
1287                                 stack = r->ws->cur_layout->name;
1288
1289                         snprintf(loc, sizeof loc, "%d:%d %s   %s%s    %s    %s",
1290                             x++, r->ws->idx + 1, stack, s, cn, bar_ext,
1291                             bar_vertext);
1292                         bar_print(r, loc);
1293                 }
1294         }
1295         alarm(bar_delay);
1296 }
1297
1298 void
1299 bar_signal(int sig)
1300 {
1301         bar_alarm = 1;
1302 }
1303
1304 void
1305 bar_toggle(struct swm_region *r, union arg *args)
1306 {
1307         struct swm_region       *tmpr;
1308         int                     i, sc = ScreenCount(display);
1309
1310         DNPRINTF(SWM_D_MISC, "bar_toggle\n");
1311
1312         if (bar_enabled)
1313                 for (i = 0; i < sc; i++)
1314                         TAILQ_FOREACH(tmpr, &screens[i].rl, entry)
1315                                 XUnmapWindow(display, tmpr->bar_window);
1316         else
1317                 for (i = 0; i < sc; i++)
1318                         TAILQ_FOREACH(tmpr, &screens[i].rl, entry)
1319                                 XMapRaised(display, tmpr->bar_window);
1320
1321         bar_enabled = !bar_enabled;
1322
1323         stack();
1324         /* must be after stack */
1325         bar_update();
1326 }
1327
1328 void
1329 bar_refresh(void)
1330 {
1331         XSetWindowAttributes    wa;
1332         struct swm_region       *r;
1333         int                     i;
1334
1335         /* do this here because the conf file is in memory */
1336         if (bar_extra && bar_extra_running == 0 && bar_argv[0]) {
1337                 /* launch external status app */
1338                 bar_extra_running = 1;
1339                 if (pipe(bar_pipe) == -1)
1340                         err(1, "pipe error");
1341                 socket_setnonblock(bar_pipe[0]);
1342                 socket_setnonblock(bar_pipe[1]); /* XXX hmmm, really? */
1343                 if (dup2(bar_pipe[0], 0) == -1)
1344                         errx(1, "dup2");
1345                 if (dup2(bar_pipe[1], 1) == -1)
1346                         errx(1, "dup2");
1347                 if (signal(SIGPIPE, SIG_IGN) == SIG_ERR)
1348                         err(1, "could not disable SIGPIPE");
1349                 switch (bar_pid = fork()) {
1350                 case -1:
1351                         err(1, "cannot fork");
1352                         break;
1353                 case 0: /* child */
1354                         close(bar_pipe[0]);
1355                         execvp(bar_argv[0], bar_argv);
1356                         err(1, "%s external app failed", bar_argv[0]);
1357                         break;
1358                 default: /* parent */
1359                         close(bar_pipe[1]);
1360                         break;
1361                 }
1362         }
1363
1364         bzero(&wa, sizeof wa);
1365         for (i = 0; i < ScreenCount(display); i++)
1366                 TAILQ_FOREACH(r, &screens[i].rl, entry) {
1367                         wa.border_pixel =
1368                             screens[i].c[SWM_S_COLOR_BAR_BORDER].color;
1369                         wa.background_pixel =
1370                             screens[i].c[SWM_S_COLOR_BAR].color;
1371                         XChangeWindowAttributes(display, r->bar_window,
1372                             CWBackPixel | CWBorderPixel, &wa);
1373                 }
1374         bar_update();
1375 }
1376
1377 void
1378 bar_setup(struct swm_region *r)
1379 {
1380         int                     i, x, y;
1381
1382         if (bar_fs) {
1383                 XFreeFont(display, bar_fs);
1384                 bar_fs = NULL;
1385         }
1386
1387         for (i = 0; bar_fonts[i] != NULL; i++) {
1388                 bar_fs = XLoadQueryFont(display, bar_fonts[i]);
1389                 if (bar_fs) {
1390                         bar_fidx = i;
1391                         break;
1392                 }
1393         }
1394         if (bar_fonts[i] == NULL)
1395                         errx(1, "couldn't load font");
1396         if (bar_fs == NULL)
1397                 errx(1, "couldn't create font structure");
1398
1399         bar_height = bar_fs->ascent + bar_fs->descent + 1 +
1400             2 * bar_border_width;
1401         x = X(r);
1402         y = bar_at_bottom ? (Y(r) + HEIGHT(r) - bar_height) : Y(r);
1403
1404         r->bar_window = XCreateSimpleWindow(display,
1405             r->s->root, x, y, WIDTH(r) - 2 * bar_border_width,
1406             bar_height - 2 * bar_border_width,
1407             bar_border_width, r->s->c[SWM_S_COLOR_BAR_BORDER].color,
1408             r->s->c[SWM_S_COLOR_BAR].color);
1409         bar_gc = XCreateGC(display, r->bar_window, 0, &bar_gcv);
1410         XSetFont(display, bar_gc, bar_fs->fid);
1411         XSelectInput(display, r->bar_window, VisibilityChangeMask);
1412         if (bar_enabled)
1413                 XMapRaised(display, r->bar_window);
1414         DNPRINTF(SWM_D_MISC, "bar_setup: bar_window %lu\n", r->bar_window);
1415
1416         if (signal(SIGALRM, bar_signal) == SIG_ERR)
1417                 err(1, "could not install bar_signal");
1418         bar_refresh();
1419 }
1420
1421 void
1422 drain_enter_notify(void)
1423 {
1424         int                     i = 0;
1425         XEvent                  cne;
1426
1427         while (XCheckMaskEvent(display, EnterWindowMask, &cne))
1428                 i++;
1429
1430         DNPRINTF(SWM_D_MISC, "drain_enter_notify: drained %d\n", i);
1431 }
1432
1433 void
1434 set_win_state(struct ws_win *win, long state)
1435 {
1436         long                    data[] = {state, None};
1437
1438         DNPRINTF(SWM_D_EVENT, "set_win_state: window: %lu\n", win->id);
1439
1440         if (win == NULL)
1441                 return;
1442
1443         XChangeProperty(display, win->id, astate, astate, 32, PropModeReplace,
1444             (unsigned char *)data, 2);
1445 }
1446
1447 long
1448 getstate(Window w)
1449 {
1450         long                    result = -1;
1451         unsigned char           *p = NULL;
1452         unsigned long           n;
1453
1454         if (!get_property(w, astate, 2L, astate, &n, &p))
1455                 return (-1);
1456         if (n != 0)
1457                 result = *((long *)p);
1458         XFree(p);
1459         return (result);
1460 }
1461
1462 void
1463 version(struct swm_region *r, union arg *args)
1464 {
1465         bar_version = !bar_version;
1466         if (bar_version)
1467                 snprintf(bar_vertext, sizeof bar_vertext, "Version: %s CVS: %s",
1468                     SWM_VERSION, cvstag);
1469         else
1470                 strlcpy(bar_vertext, "", sizeof bar_vertext);
1471         bar_update();
1472 }
1473
1474 void
1475 client_msg(struct ws_win *win, Atom a)
1476 {
1477         XClientMessageEvent     cm;
1478
1479         if (win == NULL)
1480                 return;
1481
1482         bzero(&cm, sizeof cm);
1483         cm.type = ClientMessage;
1484         cm.window = win->id;
1485         cm.message_type = aprot;
1486         cm.format = 32;
1487         cm.data.l[0] = a;
1488         cm.data.l[1] = CurrentTime;
1489         XSendEvent(display, win->id, False, 0L, (XEvent *)&cm);
1490 }
1491
1492 void
1493 config_win(struct ws_win *win, XConfigureRequestEvent  *ev)
1494 {
1495         XConfigureEvent         ce;
1496
1497         if (win == NULL)
1498                 return;
1499
1500         if (ev == NULL) {
1501                 DNPRINTF(SWM_D_MISC,
1502                     "config_win: win %lu x %d y %d w %d h %d\n",
1503                     win->id, win->g.x, win->g.y, win->g.w, win->g.h);
1504
1505                 ce.type = ConfigureNotify;
1506                 ce.display = display;
1507                 ce.event = win->id;
1508                 ce.window = win->id;
1509                 ce.x = win->g.x;
1510                 ce.y = win->g.y;
1511                 ce.width = win->g.w;
1512                 ce.height = win->g.h;
1513                 ce.border_width = border_width;
1514                 ce.above = None;
1515                 ce.override_redirect = False;
1516         } else {
1517                 DNPRINTF(SWM_D_MISC,
1518                     "config_win: ev win %lu x %d y %d w %d h %d\n",
1519                     ev->window, ev->x, ev->y, ev->width, ev->height);
1520                 ce.type = ConfigureNotify;
1521                 ce.display = ev->display;
1522                 ce.event = ev->window;
1523                 ce.window = ev->window;
1524                 ce.x = ev->x;
1525                 ce.y = ev->y;
1526                 ce.width = ev->width;
1527                 ce.height = ev->height;
1528                 ce.border_width = ev->border_width;
1529                 ce.above = ev->above;
1530                 ce.override_redirect = False;
1531         }
1532
1533         XSendEvent(display, win->id, False, StructureNotifyMask, (XEvent *)&ce);
1534 }
1535
1536 int
1537 count_win(struct workspace *ws, int count_transient)
1538 {
1539         struct ws_win           *win;
1540         int                     count = 0;
1541
1542         TAILQ_FOREACH(win, &ws->winlist, entry) {
1543                 if (count_transient == 0 && win->floating)
1544                         continue;
1545                 if (count_transient == 0 && win->transient)
1546                         continue;
1547                 if (win->iconic)
1548                         continue;
1549                 count++;
1550         }
1551         DNPRINTF(SWM_D_MISC, "count_win: %d\n", count);
1552
1553         return (count);
1554 }
1555
1556 void
1557 quit(struct swm_region *r, union arg *args)
1558 {
1559         DNPRINTF(SWM_D_MISC, "quit\n");
1560         running = 0;
1561 }
1562
1563 void
1564 unmap_window(struct ws_win *win)
1565 {
1566         if (win == NULL)
1567                 return;
1568
1569         /* don't unmap again */
1570         if (getstate(win->id) == IconicState)
1571                 return;
1572
1573         set_win_state(win, IconicState);
1574
1575         XUnmapWindow(display, win->id);
1576         XSetWindowBorder(display, win->id,
1577             win->s->c[SWM_S_COLOR_UNFOCUS].color);
1578 }
1579
1580 void
1581 unmap_all(void)
1582 {
1583         struct ws_win           *win;
1584         int                     i, j;
1585
1586         for (i = 0; i < ScreenCount(display); i++)
1587                 for (j = 0; j < SWM_WS_MAX; j++)
1588                         TAILQ_FOREACH(win, &screens[i].ws[j].winlist, entry)
1589                                 unmap_window(win);
1590 }
1591
1592 void
1593 fake_keypress(struct ws_win *win, int keysym, int modifiers)
1594 {
1595         XKeyEvent event;
1596
1597         if (win == NULL)
1598                 return;
1599
1600         event.display = display;        /* Ignored, but what the hell */
1601         event.window = win->id;
1602         event.root = win->s->root;
1603         event.subwindow = None;
1604         event.time = CurrentTime;
1605         event.x = win->g.x;
1606         event.y = win->g.y;
1607         event.x_root = 1;
1608         event.y_root = 1;
1609         event.same_screen = True;
1610         event.keycode = XKeysymToKeycode(display, keysym);
1611         event.state = modifiers;
1612
1613         event.type = KeyPress;
1614         XSendEvent(event.display, event.window, True,
1615             KeyPressMask, (XEvent *)&event);
1616
1617         event.type = KeyRelease;
1618         XSendEvent(event.display, event.window, True,
1619             KeyPressMask, (XEvent *)&event);
1620
1621 }
1622
1623 void
1624 restart(struct swm_region *r, union arg *args)
1625 {
1626         DNPRINTF(SWM_D_MISC, "restart: %s\n", start_argv[0]);
1627
1628         /* disable alarm because the following code may not be interrupted */
1629         alarm(0);
1630         if (signal(SIGALRM, SIG_IGN) == SIG_ERR)
1631                 errx(1, "can't disable alarm");
1632
1633         bar_extra_stop();
1634         bar_extra = 1;
1635         unmap_all();
1636         XCloseDisplay(display);
1637         execvp(start_argv[0], start_argv);
1638         fprintf(stderr, "execvp failed\n");
1639         perror(" failed");
1640         quit(NULL, NULL);
1641 }
1642
1643 struct swm_region *
1644 root_to_region(Window root)
1645 {
1646         struct swm_region       *r = NULL;
1647         Window                  rr, cr;
1648         int                     i, x, y, wx, wy;
1649         unsigned int            mask;
1650
1651         for (i = 0; i < ScreenCount(display); i++)
1652                 if (screens[i].root == root)
1653                         break;
1654
1655         if (XQueryPointer(display, screens[i].root,
1656             &rr, &cr, &x, &y, &wx, &wy, &mask) != False) {
1657                 /* choose a region based on pointer location */
1658                 TAILQ_FOREACH(r, &screens[i].rl, entry)
1659                         if (x >= X(r) && x <= X(r) + WIDTH(r) &&
1660                             y >= Y(r) && y <= Y(r) + HEIGHT(r))
1661                                 break;
1662         }
1663
1664         if (r == NULL)
1665                 r = TAILQ_FIRST(&screens[i].rl);
1666
1667         return (r);
1668 }
1669
1670 struct ws_win *
1671 find_unmanaged_window(Window id)
1672 {
1673         struct ws_win           *win;
1674         int                     i, j;
1675
1676         for (i = 0; i < ScreenCount(display); i++)
1677                 for (j = 0; j < SWM_WS_MAX; j++)
1678                         TAILQ_FOREACH(win, &screens[i].ws[j].unmanagedlist,
1679                             entry)
1680                                 if (id == win->id)
1681                                         return (win);
1682         return (NULL);
1683 }
1684
1685 struct ws_win *
1686 find_window(Window id)
1687 {
1688         struct ws_win           *win;
1689         Window                  wrr, wpr, *wcr = NULL;
1690         int                     i, j;
1691         unsigned int            nc;
1692
1693         for (i = 0; i < ScreenCount(display); i++)
1694                 for (j = 0; j < SWM_WS_MAX; j++)
1695                         TAILQ_FOREACH(win, &screens[i].ws[j].winlist, entry)
1696                                 if (id == win->id)
1697                                         return (win);
1698
1699         /* if we were looking for the parent return that window instead */
1700         if (XQueryTree(display, id, &wrr, &wpr, &wcr, &nc) == 0)
1701                 return (NULL);
1702         if (wcr)
1703                 XFree(wcr);
1704
1705         /* ignore not found and root */
1706         if (wpr == 0 || wrr == wpr)
1707                 return (NULL);
1708
1709         /* look for parent */
1710         for (i = 0; i < ScreenCount(display); i++)
1711                 for (j = 0; j < SWM_WS_MAX; j++)
1712                         TAILQ_FOREACH(win, &screens[i].ws[j].winlist, entry)
1713                                 if (wpr == win->id)
1714                                         return (win);
1715
1716         return (NULL);
1717 }
1718
1719 void
1720 spawn(int ws_idx, union arg *args, int close_fd)
1721 {
1722         int                     fd;
1723         char                    *ret = NULL;
1724
1725         DNPRINTF(SWM_D_MISC, "spawn: %s\n", args->argv[0]);
1726
1727         if (display)
1728                 close(ConnectionNumber(display));
1729
1730         setenv("LD_PRELOAD", SWM_LIB, 1);
1731
1732         if (asprintf(&ret, "%d", ws_idx) == -1) {
1733                 perror("_SWM_WS");
1734                 _exit(1);
1735         }
1736         setenv("_SWM_WS", ret, 1);
1737         free(ret);
1738         ret = NULL;
1739
1740         if (asprintf(&ret, "%d", getpid()) == -1) {
1741                 perror("_SWM_PID");
1742                 _exit(1);
1743         }
1744         setenv("_SWM_PID", ret, 1);
1745         free(ret);
1746         ret = NULL;
1747
1748         if (setsid() == -1) {
1749                 perror("setsid");
1750                 _exit(1);
1751         }
1752
1753         if (close_fd) {
1754                 /*
1755                  * close stdin and stdout to prevent interaction between apps
1756                  * and the baraction script
1757                  * leave stderr open to record errors
1758                 */
1759                 if ((fd = open(_PATH_DEVNULL, O_RDWR, 0)) == -1) {
1760                         perror("open");
1761                         _exit(1);
1762                 }
1763                 dup2(fd, STDIN_FILENO);
1764                 dup2(fd, STDOUT_FILENO);
1765                 if (fd > 2)
1766                         close(fd);
1767         }
1768
1769         execvp(args->argv[0], args->argv);
1770
1771         perror("execvp");
1772         _exit(1);
1773 }
1774
1775 void
1776 spawnterm(struct swm_region *r, union arg *args)
1777 {
1778         DNPRINTF(SWM_D_MISC, "spawnterm\n");
1779
1780         if (fork() == 0) {
1781                 if (term_width)
1782                         setenv("_SWM_XTERM_FONTADJ", "", 1);
1783                 spawn(r->ws->idx, args, 1);
1784         }
1785 }
1786
1787 void
1788 kill_refs(struct ws_win *win)
1789 {
1790         int                     i, x;
1791         struct swm_region       *r;
1792         struct workspace        *ws;
1793
1794         if (win == NULL)
1795                 return;
1796
1797         for (i = 0; i < ScreenCount(display); i++)
1798                 TAILQ_FOREACH(r, &screens[i].rl, entry)
1799                         for (x = 0; x < SWM_WS_MAX; x++) {
1800                                 ws = &r->s->ws[x];
1801                                 if (win == ws->focus)
1802                                         ws->focus = NULL;
1803                                 if (win == ws->focus_prev)
1804                                         ws->focus_prev = NULL;
1805                         }
1806 }
1807
1808 int
1809 validate_win(struct ws_win *testwin)
1810 {
1811         struct ws_win           *win;
1812         struct workspace        *ws;
1813         struct swm_region       *r;
1814         int                     i, x, foundit = 0;
1815
1816         if (testwin == NULL)
1817                 return (0);
1818
1819         for (i = 0, foundit = 0; i < ScreenCount(display); i++)
1820                 TAILQ_FOREACH(r, &screens[i].rl, entry)
1821                         for (x = 0; x < SWM_WS_MAX; x++) {
1822                                 ws = &r->s->ws[x];
1823                                 TAILQ_FOREACH(win, &ws->winlist, entry)
1824                                         if (win == testwin)
1825                                                 return (0);
1826                         }
1827         return (1);
1828 }
1829
1830 int
1831 validate_ws(struct workspace *testws)
1832 {
1833         struct swm_region       *r;
1834         struct workspace        *ws;
1835         int                     foundit, i, x;
1836
1837         /* validate all ws */
1838         for (i = 0, foundit = 0; i < ScreenCount(display); i++)
1839                 TAILQ_FOREACH(r, &screens[i].rl, entry)
1840                         for (x = 0; x < SWM_WS_MAX; x++) {
1841                                 ws = &r->s->ws[x];
1842                                 if (ws == testws)
1843                                         return (0);
1844                         }
1845         return (1);
1846 }
1847
1848 void
1849 unfocus_win(struct ws_win *win)
1850 {
1851         XEvent                  cne;
1852         Window                  none = None;
1853
1854         DNPRINTF(SWM_D_FOCUS, "unfocus_win: id: %lu\n", WINID(win));
1855
1856         if (win == NULL)
1857                 return;
1858         if (win->ws == NULL)
1859                 return;
1860
1861         if (validate_ws(win->ws))
1862                 return; /* XXX this gets hit with thunderbird, needs fixing */
1863
1864         if (win->ws->r == NULL)
1865                 return;
1866
1867         if (validate_win(win)) {
1868                 kill_refs(win);
1869                 return;
1870         }
1871
1872         if (win->ws->focus == win) {
1873                 win->ws->focus = NULL;
1874                 win->ws->focus_prev = win;
1875         }
1876
1877         if (validate_win(win->ws->focus)) {
1878                 kill_refs(win->ws->focus);
1879                 win->ws->focus = NULL;
1880         }
1881         if (validate_win(win->ws->focus_prev)) {
1882                 kill_refs(win->ws->focus_prev);
1883                 win->ws->focus_prev = NULL;
1884         }
1885
1886         /* drain all previous unfocus events */
1887         while (XCheckTypedEvent(display, FocusOut, &cne) == True)
1888                 ;
1889
1890         grabbuttons(win, 0);
1891         XSetWindowBorder(display, win->id,
1892             win->ws->r->s->c[SWM_S_COLOR_UNFOCUS].color);
1893
1894         XChangeProperty(display, win->s->root,
1895             ewmh[_NET_ACTIVE_WINDOW].atom, XA_WINDOW, 32,
1896             PropModeReplace, (unsigned char *)&none,1);
1897 }
1898
1899 void
1900 unfocus_all(void)
1901 {
1902         struct ws_win           *win;
1903         int                     i, j;
1904
1905         DNPRINTF(SWM_D_FOCUS, "unfocus_all\n");
1906
1907         for (i = 0; i < ScreenCount(display); i++)
1908                 for (j = 0; j < SWM_WS_MAX; j++)
1909                         TAILQ_FOREACH(win, &screens[i].ws[j].winlist, entry)
1910                                 unfocus_win(win);
1911 }
1912
1913 void
1914 focus_win(struct ws_win *win)
1915 {
1916         XEvent                  cne;
1917         Window                  cur_focus;
1918         int                     rr;
1919         struct ws_win           *cfw = NULL;
1920
1921
1922         DNPRINTF(SWM_D_FOCUS, "focus_win: id: %lu\n", win ? win->id : 0);
1923
1924         if (win == NULL)
1925                 return;
1926         if (win->ws == NULL)
1927                 return;
1928
1929         if (validate_ws(win->ws))
1930                 return; /* XXX this gets hit with thunderbird, needs fixing */
1931
1932         if (validate_win(win)) {
1933                 kill_refs(win);
1934                 return;
1935         }
1936
1937         if (validate_win(win)) {
1938                 kill_refs(win);
1939                 return;
1940         }
1941
1942         XGetInputFocus(display, &cur_focus, &rr);
1943         if ((cfw = find_window(cur_focus)) != NULL)
1944                 unfocus_win(cfw);
1945         else {
1946                 /* use larger hammer since the window was killed somehow */
1947                 TAILQ_FOREACH(cfw, &win->ws->winlist, entry)
1948                         if (cfw->ws && cfw->ws->r && cfw->ws->r->s)
1949                                 XSetWindowBorder(display, cfw->id,
1950                                     cfw->ws->r->s->c[SWM_S_COLOR_UNFOCUS].color);
1951         }
1952
1953         win->ws->focus = win;
1954
1955         if (win->ws->r != NULL) {
1956                 /* drain all previous focus events */
1957                 while (XCheckTypedEvent(display, FocusIn, &cne) == True)
1958                         ;
1959
1960                 if (win->java == 0)
1961                         XSetInputFocus(display, win->id,
1962                             RevertToParent, CurrentTime);
1963                 grabbuttons(win, 1);
1964                 XSetWindowBorder(display, win->id,
1965                     win->ws->r->s->c[SWM_S_COLOR_FOCUS].color);
1966                 if (win->ws->cur_layout->flags & SWM_L_MAPONFOCUS ||
1967                     win->ws->always_raise)
1968                         XMapRaised(display, win->id);
1969
1970                 XChangeProperty(display, win->s->root,
1971                     ewmh[_NET_ACTIVE_WINDOW].atom, XA_WINDOW, 32,
1972                     PropModeReplace, (unsigned char *)&win->id,1);
1973         }
1974
1975         if (window_name_enabled)
1976                 bar_update();
1977 }
1978
1979 void
1980 switchws(struct swm_region *r, union arg *args)
1981 {
1982         int                     wsid = args->id, unmap_old = 0;
1983         struct swm_region       *this_r, *other_r;
1984         struct ws_win           *win;
1985         struct workspace        *new_ws, *old_ws;
1986         union arg               a;
1987
1988         if (!(r && r->s))
1989                 return;
1990
1991         this_r = r;
1992         old_ws = this_r->ws;
1993         new_ws = &this_r->s->ws[wsid];
1994
1995         DNPRINTF(SWM_D_WS, "switchws screen[%d]:%dx%d+%d+%d: "
1996             "%d -> %d\n", r->s->idx, WIDTH(r), HEIGHT(r), X(r), Y(r),
1997             old_ws->idx, wsid);
1998
1999         if (new_ws == NULL || old_ws == NULL)
2000                 return;
2001         if (new_ws == old_ws)
2002                 return;
2003
2004         other_r = new_ws->r;
2005         if (other_r == NULL) {
2006                 /* the other workspace is hidden, hide this one */
2007                 old_ws->r = NULL;
2008                 unmap_old = 1;
2009         } else {
2010                 /* the other ws is visible in another region, exchange them */
2011                 other_r->ws_prior = new_ws;
2012                 other_r->ws = old_ws;
2013                 old_ws->r = other_r;
2014         }
2015         this_r->ws_prior = old_ws;
2016         this_r->ws = new_ws;
2017         new_ws->r = this_r;
2018
2019         /* this is needed so that we can click on a window after a restart */
2020         unfocus_all();
2021
2022         stack();
2023         a.id = SWM_ARG_ID_FOCUSCUR;
2024         focus(new_ws->r, &a);
2025
2026         bar_update();
2027
2028         /* unmap old windows */
2029         if (unmap_old)
2030                 TAILQ_FOREACH(win, &old_ws->winlist, entry)
2031                         unmap_window(win);
2032
2033         if (focus_mode == SWM_FOCUS_DEFAULT)
2034                 drain_enter_notify();
2035 }
2036
2037 void
2038 cyclews(struct swm_region *r, union arg *args)
2039 {
2040         union                   arg a;
2041         struct swm_screen       *s = r->s;
2042
2043         DNPRINTF(SWM_D_WS, "cyclews id %d "
2044             "in screen[%d]:%dx%d+%d+%d ws %d\n", args->id,
2045             r->s->idx, WIDTH(r), HEIGHT(r), X(r), Y(r), r->ws->idx);
2046
2047         a.id = r->ws->idx;
2048         do {
2049                 switch (args->id) {
2050                 case SWM_ARG_ID_CYCLEWS_UP:
2051                         if (a.id < SWM_WS_MAX - 1)
2052                                 a.id++;
2053                         else
2054                                 a.id = 0;
2055                         break;
2056                 case SWM_ARG_ID_CYCLEWS_DOWN:
2057                         if (a.id > 0)
2058                                 a.id--;
2059                         else
2060                                 a.id = SWM_WS_MAX - 1;
2061                         break;
2062                 default:
2063                         return;
2064                 };
2065
2066                 if (cycle_empty == 0 && TAILQ_EMPTY(&s->ws[a.id].winlist))
2067                         continue;
2068                 if (cycle_visible == 0 && s->ws[a.id].r != NULL)
2069                         continue;
2070
2071                 switchws(r, &a);
2072         } while (a.id != r->ws->idx);
2073 }
2074
2075 void
2076 priorws(struct swm_region *r, union arg *args)
2077 {
2078         union arg               a;
2079
2080         DNPRINTF(SWM_D_WS, "priorws id %d "
2081             "in screen[%d]:%dx%d+%d+%d ws %d\n", args->id,
2082             r->s->idx, WIDTH(r), HEIGHT(r), X(r), Y(r), r->ws->idx);
2083
2084         if (r->ws_prior == NULL)
2085                 return;
2086
2087         a.id = r->ws_prior->idx;
2088         switchws(r, &a);
2089 }
2090
2091 void
2092 cyclescr(struct swm_region *r, union arg *args)
2093 {
2094         struct swm_region       *rr = NULL;
2095         union arg               a;
2096         int                     i, x, y;
2097
2098         /* do nothing if we don't have more than one screen */
2099         if (!(ScreenCount(display) > 1 || outputs > 1))
2100                 return;
2101
2102         i = r->s->idx;
2103         switch (args->id) {
2104         case SWM_ARG_ID_CYCLESC_UP:
2105                 rr = TAILQ_NEXT(r, entry);
2106                 if (rr == NULL)
2107                         rr = TAILQ_FIRST(&screens[i].rl);
2108                 break;
2109         case SWM_ARG_ID_CYCLESC_DOWN:
2110                 rr = TAILQ_PREV(r, swm_region_list, entry);
2111                 if (rr == NULL)
2112                         rr = TAILQ_LAST(&screens[i].rl, swm_region_list);
2113                 break;
2114         default:
2115                 return;
2116         };
2117         if (rr == NULL)
2118                 return;
2119
2120         /* move mouse to region */
2121         x = rr->g.x + 1;
2122         y = rr->g.y + 1 + (bar_enabled ? bar_height : 0);
2123         XWarpPointer(display, None, rr->s[i].root, 0, 0, 0, 0, x, y);
2124
2125         a.id = SWM_ARG_ID_FOCUSCUR;
2126         focus(rr, &a);
2127
2128         if (rr->ws->focus) {
2129                 /* move to focus window */
2130                 x = rr->ws->focus->g.x + 1;
2131                 y = rr->ws->focus->g.y + 1;
2132                 XWarpPointer(display, None, rr->s[i].root, 0, 0, 0, 0, x, y);
2133         }
2134 }
2135
2136 void
2137 sort_windows(struct ws_win_list *wl)
2138 {
2139         struct ws_win           *win, *parent, *nxt;
2140
2141         if (wl == NULL)
2142                 return;
2143
2144         for (win = TAILQ_FIRST(wl); win != TAILQ_END(wl); win = nxt) {
2145                 nxt = TAILQ_NEXT(win, entry);
2146                 if (win->transient) {
2147                         parent = find_window(win->transient);
2148                         if (parent == NULL) {
2149                                 fprintf(stderr, "not possible bug\n");
2150                                 continue;
2151                         }
2152                         TAILQ_REMOVE(wl, win, entry);
2153                         TAILQ_INSERT_AFTER(wl, parent, win, entry);
2154                 }
2155         }
2156
2157 }
2158
2159 void
2160 swapwin(struct swm_region *r, union arg *args)
2161 {
2162         struct ws_win           *target, *source;
2163         struct ws_win           *cur_focus;
2164         struct ws_win_list      *wl;
2165
2166
2167         DNPRINTF(SWM_D_WS, "swapwin id %d "
2168             "in screen %d region %dx%d+%d+%d ws %d\n", args->id,
2169             r->s->idx, WIDTH(r), HEIGHT(r), X(r), Y(r), r->ws->idx);
2170
2171         cur_focus = r->ws->focus;
2172         if (cur_focus == NULL)
2173                 return;
2174
2175         source = cur_focus;
2176         wl = &source->ws->winlist;
2177
2178         switch (args->id) {
2179         case SWM_ARG_ID_SWAPPREV:
2180                 if (source->transient)
2181                         source = find_window(source->transient);
2182                 target = TAILQ_PREV(source, ws_win_list, entry);
2183                 if (target && target->transient)
2184                         target = find_window(target->transient);
2185                 TAILQ_REMOVE(wl, source, entry);
2186                 if (target == NULL)
2187                         TAILQ_INSERT_TAIL(wl, source, entry);
2188                 else
2189                         TAILQ_INSERT_BEFORE(target, source, entry);
2190                 break;
2191         case SWM_ARG_ID_SWAPNEXT:
2192                 target = TAILQ_NEXT(source, entry);
2193                 /* move the parent and let the sort handle the move */
2194                 if (source->transient)
2195                         source = find_window(source->transient);
2196                 TAILQ_REMOVE(wl, source, entry);
2197                 if (target == NULL)
2198                         TAILQ_INSERT_HEAD(wl, source, entry);
2199                 else
2200                         TAILQ_INSERT_AFTER(wl, target, source, entry);
2201                 break;
2202         case SWM_ARG_ID_SWAPMAIN:
2203                 target = TAILQ_FIRST(wl);
2204                 if (target == source) {
2205                         if (source->ws->focus_prev != NULL &&
2206                             source->ws->focus_prev != target)
2207
2208                                 source = source->ws->focus_prev;
2209                         else
2210                                 return;
2211                 }
2212                 if (target == NULL || source == NULL)
2213                         return;
2214                 source->ws->focus_prev = target;
2215                 TAILQ_REMOVE(wl, target, entry);
2216                 TAILQ_INSERT_BEFORE(source, target, entry);
2217                 TAILQ_REMOVE(wl, source, entry);
2218                 TAILQ_INSERT_HEAD(wl, source, entry);
2219                 break;
2220         case SWM_ARG_ID_MOVELAST:
2221                 TAILQ_REMOVE(wl, source, entry);
2222                 TAILQ_INSERT_TAIL(wl, source, entry);
2223                 break;
2224         default:
2225                 DNPRINTF(SWM_D_MOVE, "invalid id: %d\n", args->id);
2226                 return;
2227         }
2228
2229         sort_windows(wl);
2230
2231         stack();
2232 }
2233
2234 void
2235 focus_prev(struct ws_win *win)
2236 {
2237         struct ws_win           *winfocus = NULL, *winlostfocus = NULL;
2238         struct ws_win           *cur_focus = NULL;
2239         struct ws_win_list      *wl = NULL;
2240         struct workspace        *ws = NULL;
2241
2242         DNPRINTF(SWM_D_FOCUS, "focus_prev: id %lu\n", WINID(win));
2243
2244         if (!(win && win->ws))
2245                 return;
2246
2247         ws = win->ws;
2248         wl = &ws->winlist;
2249         cur_focus = ws->focus;
2250         winlostfocus = cur_focus;
2251
2252         /* pickle, just focus on whatever */
2253         if (cur_focus == NULL) {
2254                 /* use prev_focus if valid */
2255                 if (ws->focus_prev && ws->focus_prev != cur_focus &&
2256                     find_window(WINID(ws->focus_prev)))
2257                         winfocus = ws->focus_prev;
2258                 if (winfocus == NULL)
2259                         winfocus = TAILQ_FIRST(wl);
2260                 goto done;
2261         }
2262
2263         /* if transient focus on parent */
2264         if (cur_focus->transient) {
2265                 winfocus = find_window(cur_focus->transient);
2266                 goto done;
2267         }
2268
2269         /* if in max_stack try harder */
2270         if ((win->quirks & SWM_Q_FOCUSPREV) ||
2271             (ws->cur_layout->flags & SWM_L_FOCUSPREV)) {
2272                 if (cur_focus != ws->focus_prev)
2273                         winfocus = ws->focus_prev;
2274                 else if (cur_focus != ws->focus)
2275                         winfocus = ws->focus;
2276                 else
2277                         winfocus = TAILQ_PREV(win, ws_win_list, entry);
2278                 if (winfocus)
2279                         goto done;
2280         }
2281
2282         if (cur_focus == win)
2283                 winfocus = TAILQ_PREV(win, ws_win_list, entry);
2284         if (winfocus == NULL)
2285                 winfocus = TAILQ_LAST(wl, ws_win_list);
2286         if (winfocus == NULL || winfocus == win)
2287                 winfocus = TAILQ_NEXT(cur_focus, entry);
2288 done:
2289         if (winfocus == winlostfocus || winfocus == NULL)
2290                 return;
2291
2292         focus_magic(winfocus);
2293 }
2294
2295 void
2296 focus(struct swm_region *r, union arg *args)
2297 {
2298         struct ws_win           *winfocus = NULL, *winlostfocus = NULL, *head;
2299         struct ws_win           *cur_focus = NULL;
2300         struct ws_win_list      *wl = NULL;
2301         struct workspace        *ws = NULL;
2302
2303         if (!(r && r->ws))
2304                 return;
2305
2306         DNPRINTF(SWM_D_FOCUS, "focus: id %d\n", args->id);
2307
2308         /* treat FOCUS_CUR special */
2309         if (args->id == SWM_ARG_ID_FOCUSCUR) {
2310                 if (r->ws->focus && r->ws->focus->iconic == 0)
2311                         winfocus = r->ws->focus;
2312                 else if (r->ws->focus_prev && r->ws->focus_prev->iconic == 0)
2313                         winfocus = r->ws->focus_prev;
2314                 else
2315                         TAILQ_FOREACH(winfocus, &r->ws->winlist, entry)
2316                                 if (winfocus->iconic == 0)
2317                                         break;
2318
2319                 focus_magic(winfocus);
2320                 return;
2321         }
2322
2323         if ((cur_focus = r->ws->focus) == NULL)
2324                 return;
2325         ws = r->ws;
2326         wl = &ws->winlist;
2327
2328         winlostfocus = cur_focus;
2329
2330         switch (args->id) {
2331         case SWM_ARG_ID_FOCUSPREV:
2332                 head = TAILQ_PREV(cur_focus, ws_win_list, entry);
2333                 if (head == NULL)
2334                         head = TAILQ_LAST(wl, ws_win_list);
2335                 winfocus = head;
2336                 if (WINID(winfocus) == cur_focus->transient) {
2337                         head = TAILQ_PREV(winfocus, ws_win_list, entry);
2338                         if (head == NULL)
2339                                 head = TAILQ_LAST(wl, ws_win_list);
2340                         winfocus = head;
2341                 }
2342
2343                 /* skip iconics */
2344                 if (winfocus && winfocus->iconic) {
2345                         TAILQ_FOREACH_REVERSE(winfocus, wl, ws_win_list, entry)
2346                                 if (winfocus->iconic == 0)
2347                                         break;
2348                 }
2349                 break;
2350
2351         case SWM_ARG_ID_FOCUSNEXT:
2352                 head = TAILQ_NEXT(cur_focus, entry);
2353                 if (head == NULL)
2354                         head = TAILQ_FIRST(wl);
2355                 winfocus = head;
2356
2357                 /* skip iconics */
2358                 if (winfocus && winfocus->iconic) {
2359                         TAILQ_FOREACH(winfocus, wl, entry)
2360                                 if (winfocus->iconic == 0)
2361                                         break;
2362                 }
2363                 break;
2364
2365         case SWM_ARG_ID_FOCUSMAIN:
2366                 winfocus = TAILQ_FIRST(wl);
2367                 if (winfocus == cur_focus)
2368                         winfocus = cur_focus->ws->focus_prev;
2369                 break;
2370
2371         default:
2372                 return;
2373         }
2374         if (winfocus == winlostfocus || winfocus == NULL)
2375                 return;
2376
2377         focus_magic(winfocus);
2378 }
2379
2380 void
2381 cycle_layout(struct swm_region *r, union arg *args)
2382 {
2383         struct workspace        *ws = r->ws;
2384         struct ws_win           *winfocus;
2385         union arg               a;
2386
2387         DNPRINTF(SWM_D_EVENT, "cycle_layout: workspace: %d\n", ws->idx);
2388
2389         winfocus = ws->focus;
2390
2391         ws->cur_layout++;
2392         if (ws->cur_layout->l_stack == NULL)
2393                 ws->cur_layout = &layouts[0];
2394
2395         stack();
2396         if (focus_mode == SWM_FOCUS_DEFAULT)
2397                 drain_enter_notify();
2398         a.id = SWM_ARG_ID_FOCUSCUR;
2399         focus(r, &a);
2400         bar_update();
2401 }
2402
2403 void
2404 stack_config(struct swm_region *r, union arg *args)
2405 {
2406         struct workspace        *ws = r->ws;
2407
2408         DNPRINTF(SWM_D_STACK, "stack_config for workspace %d (id %d\n",
2409             args->id, ws->idx);
2410
2411         if (ws->cur_layout->l_config != NULL)
2412                 ws->cur_layout->l_config(ws, args->id);
2413
2414         if (args->id != SWM_ARG_ID_STACKINIT)
2415                 stack();
2416 }
2417
2418 void
2419 stack(void) {
2420         struct swm_geometry     g;
2421         struct swm_region       *r;
2422         int                     i, j;
2423
2424         DNPRINTF(SWM_D_STACK, "stack\n");
2425
2426         for (i = 0; i < ScreenCount(display); i++) {
2427                 j = 0;
2428                 TAILQ_FOREACH(r, &screens[i].rl, entry) {
2429                         DNPRINTF(SWM_D_STACK, "stacking workspace %d "
2430                             "(screen %d, region %d)\n", r->ws->idx, i, j++);
2431
2432                         /* start with screen geometry, adjust for bar */
2433                         g = r->g;
2434                         g.w -= 2 * border_width;
2435                         g.h -= 2 * border_width;
2436                         if (bar_enabled) {
2437                                 if (!bar_at_bottom)
2438                                         g.y += bar_height;
2439                                 g.h -= bar_height;
2440                         }
2441                         r->ws->cur_layout->l_stack(r->ws, &g);
2442                         /* save r so we can track region changes */
2443                         r->ws->old_r = r;
2444                 }
2445         }
2446         if (font_adjusted)
2447                 font_adjusted--;
2448
2449         if (focus_mode == SWM_FOCUS_DEFAULT)
2450                 drain_enter_notify();
2451 }
2452
2453 void
2454 store_float_geom(struct ws_win *win, struct swm_region *r)
2455 {
2456         /* retain window geom and region geom */
2457         win->g_float.x = win->g.x;
2458         win->g_float.y = win->g.y;
2459         win->g_float.w = win->g.w;
2460         win->g_float.h = win->g.h;
2461         win->rg_float.x = r->g.x;
2462         win->rg_float.y = r->g.y;
2463         win->rg_float.w = r->g.w;
2464         win->rg_float.h = r->g.h;
2465         win->g_floatvalid = 1;
2466 }
2467
2468 void
2469 stack_floater(struct ws_win *win, struct swm_region *r)
2470 {
2471         unsigned int            mask;
2472         XWindowChanges          wc;
2473
2474         if (win == NULL)
2475                 return;
2476
2477         bzero(&wc, sizeof wc);
2478         mask = CWX | CWY | CWBorderWidth | CWWidth | CWHeight;
2479
2480         /*
2481          * to allow windows to change their size (e.g. mplayer fs) only retrieve
2482          * geom on ws switches or return from max mode
2483          */
2484
2485         if (win->floatmaxed || (r != r->ws->old_r && win->g_floatvalid
2486             && !(win->ewmh_flags & EWMH_F_FULLSCREEN))) {
2487                 /*
2488                  * use stored g and rg to set relative position and size
2489                  * as in old region or before max stack mode
2490                  */
2491                 win->g.x = win->g_float.x - win->rg_float.x + r->g.x;
2492                 win->g.y = win->g_float.y - win->rg_float.y + r->g.y;
2493                 win->g.w = win->g_float.w;
2494                 win->g.h = win->g_float.h;
2495                 win->g_floatvalid = 0;
2496         }
2497
2498         win->floatmaxed = 0;
2499
2500         if ((win->quirks & SWM_Q_FULLSCREEN) && (win->g.w >= WIDTH(r)) &&
2501             (win->g.h >= HEIGHT(r)))
2502                 wc.border_width = 0;
2503         else
2504                 wc.border_width = border_width;
2505         if (win->transient && (win->quirks & SWM_Q_TRANSSZ)) {
2506                 win->g.w = (double)WIDTH(r) * dialog_ratio;
2507                 win->g.h = (double)HEIGHT(r) * dialog_ratio;
2508         }
2509
2510         if (!win->manual) {
2511                 /*
2512                  * floaters and transients are auto-centred unless moved
2513                  * or resized
2514                  */
2515                 win->g.x = r->g.x + (WIDTH(r) - win->g.w) /
2516                     2 - wc.border_width;
2517                 win->g.y = r->g.y + (HEIGHT(r) - win->g.h) /
2518                     2 - wc.border_width;
2519         }
2520
2521         /* win can be outside r if new r smaller than old r */
2522         /* Ensure top left corner inside r (move probs otherwise) */
2523         if (win->g.x < r->g.x - wc.border_width)
2524                 win->g.x = r->g.x - wc.border_width;
2525         if (win->g.x > r->g.x + r->g.w - 1)
2526                 win->g.x = (win->g.w > r->g.w) ? r->g.x :
2527                     (r->g.x + r->g.w - win->g.w - 2 * wc.border_width);
2528         if (win->g.y < r->g.y - wc.border_width)
2529                 win->g.y = r->g.y - wc.border_width;
2530         if (win->g.y > r->g.y + r->g.h - 1)
2531                 win->g.y = (win->g.h > r->g.h) ? r->g.y :
2532                     (r->g.y + r->g.h - win->g.h - 2 * wc.border_width);
2533
2534         wc.x = win->g.x;
2535         wc.y = win->g.y;
2536         wc.width = win->g.w;
2537         wc.height = win->g.h;
2538
2539         /*
2540          * Retain floater and transient geometry for correct positioning
2541          * when ws changes region
2542          */
2543         if (!(win->ewmh_flags & EWMH_F_FULLSCREEN))
2544                 store_float_geom(win, r);
2545
2546         DNPRINTF(SWM_D_MISC, "stack_floater: win %lu x %d y %d w %d h %d\n",
2547             win->id, wc.x, wc.y, wc.width, wc.height);
2548
2549         XConfigureWindow(display, win->id, mask, &wc);
2550 }
2551
2552 /*
2553  * Send keystrokes to terminal to decrease/increase the font size as the
2554  * window size changes.
2555  */
2556 void
2557 adjust_font(struct ws_win *win)
2558 {
2559         if (!(win->quirks & SWM_Q_XTERM_FONTADJ) ||
2560             win->floating || win->transient)
2561                 return;
2562
2563         if (win->sh.width_inc && win->last_inc != win->sh.width_inc &&
2564             win->g.w / win->sh.width_inc < term_width &&
2565             win->font_steps < SWM_MAX_FONT_STEPS) {
2566                 win->font_size_boundary[win->font_steps] =
2567                     (win->sh.width_inc * term_width) + win->sh.base_width;
2568                 win->font_steps++;
2569                 font_adjusted++;
2570                 win->last_inc = win->sh.width_inc;
2571                 fake_keypress(win, XK_KP_Subtract, ShiftMask);
2572         } else if (win->font_steps && win->last_inc != win->sh.width_inc &&
2573             win->g.w > win->font_size_boundary[win->font_steps - 1]) {
2574                 win->font_steps--;
2575                 font_adjusted++;
2576                 win->last_inc = win->sh.width_inc;
2577                 fake_keypress(win, XK_KP_Add, ShiftMask);
2578         }
2579 }
2580
2581 #define SWAPXY(g)       do {                            \
2582         int tmp;                                        \
2583         tmp = (g)->y; (g)->y = (g)->x; (g)->x = tmp;    \
2584         tmp = (g)->h; (g)->h = (g)->w; (g)->w = tmp;    \
2585 } while (0)
2586 void
2587 stack_master(struct workspace *ws, struct swm_geometry *g, int rot, int flip)
2588 {
2589         XWindowChanges          wc;
2590         XWindowAttributes       wa;
2591         struct swm_geometry     win_g, r_g = *g;
2592         struct ws_win           *win, *fs_win = 0;
2593         int                     i, j, s, stacks;
2594         int                     w_inc = 1, h_inc, w_base = 1, h_base;
2595         int                     hrh, extra = 0, h_slice, last_h = 0;
2596         int                     split, colno, winno, mwin, msize, mscale;
2597         int                     remain, missing, v_slice, reconfigure;
2598         unsigned int            mask;
2599
2600         DNPRINTF(SWM_D_STACK, "stack_master: workspace: %d\n rot=%s flip=%s",
2601             ws->idx, rot ? "yes" : "no", flip ? "yes" : "no");
2602
2603         winno = count_win(ws, 0);
2604         if (winno == 0 && count_win(ws, 1) == 0)
2605                 return;
2606
2607         TAILQ_FOREACH(win, &ws->winlist, entry)
2608                 if (win->transient == 0 && win->floating == 0
2609                     && win->iconic == 0)
2610                         break;
2611
2612         if (win == NULL)
2613                 goto notiles;
2614
2615         if (rot) {
2616                 w_inc = win->sh.width_inc;
2617                 w_base = win->sh.base_width;
2618                 mwin = ws->l_state.horizontal_mwin;
2619                 mscale = ws->l_state.horizontal_msize;
2620                 stacks = ws->l_state.horizontal_stacks;
2621                 SWAPXY(&r_g);
2622         } else {
2623                 w_inc = win->sh.height_inc;
2624                 w_base = win->sh.base_height;
2625                 mwin = ws->l_state.vertical_mwin;
2626                 mscale = ws->l_state.vertical_msize;
2627                 stacks = ws->l_state.vertical_stacks;
2628         }
2629         win_g = r_g;
2630
2631         if (stacks > winno - mwin)
2632                 stacks = winno - mwin;
2633         if (stacks < 1)
2634                 stacks = 1;
2635
2636         h_slice = r_g.h / SWM_H_SLICE;
2637         if (mwin && winno > mwin) {
2638                 v_slice = r_g.w / SWM_V_SLICE;
2639
2640                 split = mwin;
2641                 colno = split;
2642                 win_g.w = v_slice * mscale;
2643
2644                 if (w_inc > 1 && w_inc < v_slice) {
2645                         /* adjust for window's requested size increment */
2646                         remain = (win_g.w - w_base) % w_inc;
2647                         missing = w_inc - remain;
2648                         win_g.w -= remain;
2649                         extra += remain;
2650                 }
2651
2652                 msize = win_g.w;
2653                 if (flip)
2654                         win_g.x += r_g.w - msize;
2655         } else {
2656                 msize = -2;
2657                 colno = split = winno / stacks;
2658                 win_g.w = ((r_g.w - (stacks * 2 * border_width) +
2659                     2 * border_width) / stacks);
2660         }
2661         hrh = r_g.h / colno;
2662         extra = r_g.h - (colno * hrh);
2663         win_g.h = hrh - 2 * border_width;
2664
2665         /*  stack all the tiled windows */
2666         i = j = 0, s = stacks;
2667         TAILQ_FOREACH(win, &ws->winlist, entry) {
2668                 if (win->transient != 0 || win->floating != 0)
2669                         continue;
2670                 if (win->iconic != 0)
2671                         continue;
2672
2673                 if (win->ewmh_flags & EWMH_F_FULLSCREEN) {
2674                         fs_win = win;
2675                         continue;
2676                 }
2677
2678                 if (split && i == split) {
2679                         colno = (winno - mwin) / stacks;
2680                         if (s <= (winno - mwin) % stacks)
2681                                 colno++;
2682                         split = split + colno;
2683                         hrh = (r_g.h / colno);
2684                         extra = r_g.h - (colno * hrh);
2685                         if (flip)
2686                                 win_g.x = r_g.x;
2687                         else
2688                                 win_g.x += win_g.w + 2 * border_width;
2689                         win_g.w = (r_g.w - msize -
2690                             (stacks * 2 * border_width)) / stacks;
2691                         if (s == 1)
2692                                 win_g.w += (r_g.w - msize -
2693                                     (stacks * 2 * border_width)) % stacks;
2694                         s--;
2695                         j = 0;
2696                 }
2697                 win_g.h = hrh - 2 * border_width;
2698                 if (rot) {
2699                         h_inc = win->sh.width_inc;
2700                         h_base = win->sh.base_width;
2701                 } else {
2702                         h_inc = win->sh.height_inc;
2703                         h_base = win->sh.base_height;
2704                 }
2705                 if (j == colno - 1) {
2706                         win_g.h = hrh + extra;
2707                 } else if (h_inc > 1 && h_inc < h_slice) {
2708                         /* adjust for window's requested size increment */
2709                         remain = (win_g.h - h_base) % h_inc;
2710                         missing = h_inc - remain;
2711
2712                         if (missing <= extra || j == 0) {
2713                                 extra -= missing;
2714                                 win_g.h += missing;
2715                         } else {
2716                                 win_g.h -= remain;
2717                                 extra += remain;
2718                         }
2719                 }
2720
2721                 if (j == 0)
2722                         win_g.y = r_g.y;
2723                 else
2724                         win_g.y += last_h + 2 * border_width;
2725
2726                 bzero(&wc, sizeof wc);
2727                 if (disable_border && bar_enabled == 0 && winno == 1){
2728                         wc.border_width = 0;
2729                         win_g.w += 2 * border_width;
2730                         win_g.h += 2 * border_width;
2731                 } else
2732                         wc.border_width = border_width;
2733                 reconfigure = 0;
2734                 if (rot) {
2735                         if (win->g.x != win_g.y || win->g.y != win_g.x ||
2736                             win->g.w != win_g.h || win->g.h != win_g.w) {
2737                                 reconfigure = 1;
2738                                 win->g.x = wc.x = win_g.y;
2739                                 win->g.y = wc.y = win_g.x;
2740                                 win->g.w = wc.width = win_g.h;
2741                                 win->g.h = wc.height = win_g.w;
2742                         }
2743                 } else {
2744                         if (win->g.x != win_g.x || win->g.y != win_g.y ||
2745                             win->g.w != win_g.w || win->g.h != win_g.h) {
2746                                 reconfigure = 1;
2747                                 win->g.x = wc.x = win_g.x;
2748                                 win->g.y = wc.y = win_g.y;
2749                                 win->g.w = wc.width = win_g.w;
2750                                 win->g.h = wc.height = win_g.h;
2751                         }
2752                 }
2753                 if (reconfigure) {
2754                         adjust_font(win);
2755                         mask = CWX | CWY | CWWidth | CWHeight | CWBorderWidth;
2756                         XConfigureWindow(display, win->id, mask, &wc);
2757                 }
2758
2759                 if (XGetWindowAttributes(display, win->id, &wa))
2760                         if (wa.map_state == IsUnmapped)
2761                                 XMapRaised(display, win->id);
2762
2763                 last_h = win_g.h;
2764                 i++;
2765                 j++;
2766         }
2767
2768 notiles:
2769         /* now, stack all the floaters and transients */
2770         TAILQ_FOREACH(win, &ws->winlist, entry) {
2771                 if (win->transient == 0 && win->floating == 0)
2772                         continue;
2773                 if (win->iconic == 1)
2774                         continue;
2775                 if (win->ewmh_flags & EWMH_F_FULLSCREEN) {
2776                         fs_win = win;
2777                         continue;
2778                 }
2779
2780                 stack_floater(win, ws->r);
2781                 XMapRaised(display, win->id);
2782         }
2783
2784         if (fs_win) {
2785                 stack_floater(fs_win, ws->r);
2786                 XMapRaised(display, fs_win->id);
2787         }
2788 }
2789
2790 void
2791 vertical_config(struct workspace *ws, int id)
2792 {
2793         DNPRINTF(SWM_D_STACK, "vertical_resize: workspace: %d\n", ws->idx);
2794
2795         switch (id) {
2796         case SWM_ARG_ID_STACKRESET:
2797         case SWM_ARG_ID_STACKINIT:
2798                 ws->l_state.vertical_msize = SWM_V_SLICE / 2;
2799                 ws->l_state.vertical_mwin = 1;
2800                 ws->l_state.vertical_stacks = 1;
2801                 break;
2802         case SWM_ARG_ID_MASTERSHRINK:
2803                 if (ws->l_state.vertical_msize > 1)
2804                         ws->l_state.vertical_msize--;
2805                 break;
2806         case SWM_ARG_ID_MASTERGROW:
2807                 if (ws->l_state.vertical_msize < SWM_V_SLICE - 1)
2808                         ws->l_state.vertical_msize++;
2809                 break;
2810         case SWM_ARG_ID_MASTERADD:
2811                 ws->l_state.vertical_mwin++;
2812                 break;
2813         case SWM_ARG_ID_MASTERDEL:
2814                 if (ws->l_state.vertical_mwin > 0)
2815                         ws->l_state.vertical_mwin--;
2816                 break;
2817         case SWM_ARG_ID_STACKINC:
2818                 ws->l_state.vertical_stacks++;
2819                 break;
2820         case SWM_ARG_ID_STACKDEC:
2821                 if (ws->l_state.vertical_stacks > 1)
2822                         ws->l_state.vertical_stacks--;
2823                 break;
2824         default:
2825                 return;
2826         }
2827 }
2828
2829 void
2830 vertical_stack(struct workspace *ws, struct swm_geometry *g)
2831 {
2832         DNPRINTF(SWM_D_STACK, "vertical_stack: workspace: %d\n", ws->idx);
2833
2834         stack_master(ws, g, 0, 0);
2835 }
2836
2837 void
2838 horizontal_config(struct workspace *ws, int id)
2839 {
2840         DNPRINTF(SWM_D_STACK, "horizontal_config: workspace: %d\n", ws->idx);
2841
2842         switch (id) {
2843         case SWM_ARG_ID_STACKRESET:
2844         case SWM_ARG_ID_STACKINIT:
2845                 ws->l_state.horizontal_mwin = 1;
2846                 ws->l_state.horizontal_msize = SWM_H_SLICE / 2;
2847                 ws->l_state.horizontal_stacks = 1;
2848                 break;
2849         case SWM_ARG_ID_MASTERSHRINK:
2850                 if (ws->l_state.horizontal_msize > 1)
2851                         ws->l_state.horizontal_msize--;
2852                 break;
2853         case SWM_ARG_ID_MASTERGROW:
2854                 if (ws->l_state.horizontal_msize < SWM_H_SLICE - 1)
2855                         ws->l_state.horizontal_msize++;
2856                 break;
2857         case SWM_ARG_ID_MASTERADD:
2858                 ws->l_state.horizontal_mwin++;
2859                 break;
2860         case SWM_ARG_ID_MASTERDEL:
2861                 if (ws->l_state.horizontal_mwin > 0)
2862                         ws->l_state.horizontal_mwin--;
2863                 break;
2864         case SWM_ARG_ID_STACKINC:
2865                 ws->l_state.horizontal_stacks++;
2866                 break;
2867         case SWM_ARG_ID_STACKDEC:
2868                 if (ws->l_state.horizontal_stacks > 1)
2869                         ws->l_state.horizontal_stacks--;
2870                 break;
2871         default:
2872                 return;
2873         }
2874 }
2875
2876 void
2877 horizontal_stack(struct workspace *ws, struct swm_geometry *g)
2878 {
2879         DNPRINTF(SWM_D_STACK, "horizontal_stack: workspace: %d\n", ws->idx);
2880
2881         stack_master(ws, g, 1, 0);
2882 }
2883
2884 /* fullscreen view */
2885 void
2886 max_stack(struct workspace *ws, struct swm_geometry *g)
2887 {
2888         XWindowChanges          wc;
2889         struct swm_geometry     gg = *g;
2890         struct ws_win           *win, *wintrans = NULL, *parent = NULL;
2891         unsigned int            mask;
2892         int                     winno;
2893
2894         DNPRINTF(SWM_D_STACK, "max_stack: workspace: %d\n", ws->idx);
2895
2896         if (ws == NULL)
2897                 return;
2898
2899         winno = count_win(ws, 0);
2900         if (winno == 0 && count_win(ws, 1) == 0)
2901                 return;
2902
2903         TAILQ_FOREACH(win, &ws->winlist, entry) {
2904                 if (win->transient) {
2905                         wintrans = win;
2906                         parent = find_window(win->transient);
2907                         continue;
2908                 }
2909
2910                 if (win->floating && win->floatmaxed == 0 ) {
2911                         /*
2912                          * retain geometry for retrieval on exit from
2913                          * max_stack mode
2914                          */
2915                         store_float_geom(win, ws->r);
2916                         win->floatmaxed = 1;
2917                 }
2918
2919                 /* only reconfigure if necessary */
2920                 if (win->g.x != gg.x || win->g.y != gg.y || win->g.w != gg.w ||
2921                     win->g.h != gg.h) {
2922                         bzero(&wc, sizeof wc);
2923                         win->g.x = wc.x = gg.x;
2924                         win->g.y = wc.y = gg.y;
2925                         if (bar_enabled){
2926                                 wc.border_width = border_width;
2927                                 win->g.w = wc.width = gg.w;
2928                                 win->g.h = wc.height = gg.h;
2929                         } else {
2930                                 wc.border_width = 0;
2931                                 win->g.w = wc.width = gg.w + 2 * border_width;
2932                                 win->g.h = wc.height = gg.h + 2 * border_width;
2933                         }
2934                         mask = CWX | CWY | CWWidth | CWHeight | CWBorderWidth;
2935                         XConfigureWindow(display, win->id, mask, &wc);
2936                 }
2937                 /* unmap only if we don't have multi screen */
2938                 if (win != ws->focus)
2939                         if (!(ScreenCount(display) > 1 || outputs > 1))
2940                                 unmap_window(win);
2941         }
2942
2943         /* put the last transient on top */
2944         if (wintrans) {
2945                 if (parent)
2946                         XMapRaised(display, parent->id);
2947                 stack_floater(wintrans, ws->r);
2948                 focus_magic(wintrans);
2949         }
2950 }
2951
2952 void
2953 send_to_ws(struct swm_region *r, union arg *args)
2954 {
2955         int                     wsid = args->id;
2956         struct ws_win           *win = NULL, *parent;
2957         struct workspace        *ws, *nws;
2958         Atom                    ws_idx_atom = 0;
2959         unsigned char           ws_idx_str[SWM_PROPLEN];
2960         union arg               a;
2961
2962         if (r && r->ws)
2963                 win = r->ws->focus;
2964         else
2965                 return;
2966         if (win == NULL)
2967                 return;
2968         if (win->ws->idx == wsid)
2969                 return;
2970
2971         DNPRINTF(SWM_D_MOVE, "send_to_ws: win: %lu\n", win->id);
2972
2973         ws = win->ws;
2974         nws = &win->s->ws[wsid];
2975
2976         a.id = SWM_ARG_ID_FOCUSPREV;
2977         focus(r, &a);
2978         if (win->transient) {
2979                 parent = find_window(win->transient);
2980                 if (parent) {
2981                         unmap_window(parent);
2982                         TAILQ_REMOVE(&ws->winlist, parent, entry);
2983                         TAILQ_INSERT_TAIL(&nws->winlist, parent, entry);
2984                         parent->ws = nws;
2985                 }
2986         }
2987         unmap_window(win);
2988         TAILQ_REMOVE(&ws->winlist, win, entry);
2989         TAILQ_INSERT_TAIL(&nws->winlist, win, entry);
2990         win->ws = nws;
2991
2992         /* Try to update the window's workspace property */
2993         ws_idx_atom = XInternAtom(display, "_SWM_WS", False);
2994         if (ws_idx_atom &&
2995             snprintf(ws_idx_str, SWM_PROPLEN, "%d", nws->idx) < SWM_PROPLEN) {
2996                 DNPRINTF(SWM_D_PROP, "setting property _SWM_WS to %s\n",
2997                     ws_idx_str);
2998                 XChangeProperty(display, win->id, ws_idx_atom, XA_STRING, 8,
2999                     PropModeReplace, ws_idx_str, SWM_PROPLEN);
3000         }
3001
3002         stack();
3003 }
3004
3005 void
3006 raise_toggle(struct swm_region *r, union arg *args)
3007 {
3008         if (r && r->ws == NULL)
3009                 return;
3010
3011         r->ws->always_raise = !r->ws->always_raise;
3012
3013         /* bring floaters back to top */
3014         if (r->ws->always_raise == 0)
3015                 stack();
3016 }
3017
3018 void
3019 iconify(struct swm_region *r, union arg *args)
3020 {
3021         union arg a;
3022
3023         if (r->ws->focus == NULL)
3024                 return;
3025         unmap_window(r->ws->focus);
3026         update_iconic(r->ws->focus, 1);
3027         stack();
3028         if (focus_mode == SWM_FOCUS_DEFAULT)
3029                 drain_enter_notify();
3030         r->ws->focus = NULL;
3031         a.id = SWM_ARG_ID_FOCUSCUR;
3032         focus(r, &a);
3033 }
3034
3035 unsigned char *
3036 get_win_name(Display *dpy, Window win, Atom wname, Atom stype,
3037     unsigned long *slen)
3038 {
3039         int                     status, retfmt;
3040         unsigned long           nitems, nbytes, nextra;
3041         unsigned char           *prop = NULL;
3042         Atom                    rettype;
3043
3044         status = XGetWindowProperty(dpy, win, wname, 0L, 0L, False, stype,
3045             &rettype, &retfmt,  &nitems, &nbytes, &prop);
3046         if (status != Success)
3047                 return (NULL);
3048         XFree(prop);
3049
3050         status = XGetWindowProperty(dpy, win, wname, 0L, nbytes, False,
3051             stype, &rettype, &retfmt, &nitems, &nextra, &prop);
3052         if (status != Success) {
3053                 XFree(prop);
3054                 return (NULL);
3055         }
3056         if (rettype != stype) {
3057                 XFree(prop);
3058                 return (NULL);
3059         }
3060         *slen = nitems;
3061         return (prop);
3062 }
3063
3064 void
3065 uniconify(struct swm_region *r, union arg *args)
3066 {
3067         struct ws_win           *win;
3068         FILE                    *lfile;
3069         char                    *name;
3070         int                     count = 0;
3071         unsigned long           len;
3072
3073         DNPRINTF(SWM_D_MISC, "uniconify\n");
3074
3075         if (r && r->ws == NULL)
3076                 return;
3077
3078         /* make sure we have anything to uniconify */
3079         TAILQ_FOREACH(win, &r->ws->winlist, entry) {
3080                 if (win->ws == NULL)
3081                         continue; /* should never happen */
3082                 if (win->iconic == 0)
3083                         continue;
3084                 count++;
3085         }
3086         if (count == 0)
3087                 return;
3088
3089         search_r = r;
3090
3091         spawn_select(r, args, "uniconify", &searchpid);
3092
3093         if ((lfile = fdopen(select_list_pipe[1], "w")) == NULL)
3094                 return;
3095
3096         TAILQ_FOREACH(win, &r->ws->winlist, entry) {
3097                 if (win->ws == NULL)
3098                         continue; /* should never happen */
3099                 if (win->iconic == 0)
3100                         continue;
3101
3102                 name = get_win_name(display, win->id, a_wmname, a_string,
3103                     &len);
3104                 if (name == NULL)
3105                         continue;
3106                 fprintf(lfile, "%s.%lu\n", name, win->id);
3107                 XFree(name);
3108         }
3109
3110         fclose(lfile);
3111 }
3112
3113 #define MAX_RESP_LEN    1024
3114
3115 void
3116 search_do_resp(void)
3117 {
3118         ssize_t                 rbytes;
3119         struct ws_win           *win;
3120         char                    *name, *resp, *s;
3121         unsigned long           len;
3122
3123         DNPRINTF(SWM_D_MISC, "search_do_resp:\n");
3124
3125         search_resp = 0;
3126         searchpid = 0;
3127
3128         if ((resp = calloc(1, MAX_RESP_LEN + 1)) == NULL) {
3129                 fprintf(stderr, "search: calloc\n");
3130                 return;
3131         }
3132
3133         rbytes = read(select_resp_pipe[0], resp, MAX_RESP_LEN);
3134         if (rbytes <= 0) {
3135                 fprintf(stderr, "search: read error: %s\n", strerror(errno));
3136                 goto done;
3137         }
3138         resp[rbytes] = '\0';
3139         len = strlen(resp);
3140
3141         DNPRINTF(SWM_D_MISC, "search_do_resp: resp %s\n", resp);
3142         TAILQ_FOREACH(win, &search_r->ws->winlist, entry) {
3143                 if (win->iconic == 0)
3144                         continue;
3145                 name = get_win_name(display, win->id, a_wmname, a_string, &len);
3146                 if (name == NULL)
3147                         continue;
3148                 if (asprintf(&s, "%s.%lu", name, win->id) == -1) {
3149                         XFree(name);
3150                         continue;
3151                 }
3152                 XFree(name);
3153                 if (strncmp(s, resp, len) == 0) {
3154                         /* XXX this should be a callback to generalize */
3155                         update_iconic(win, 0);
3156                         free(s);
3157                         break;
3158                 }
3159                 free(s);
3160         }
3161 done:
3162         free(resp);
3163 }
3164
3165 void
3166 wkill(struct swm_region *r, union arg *args)
3167 {
3168         DNPRINTF(SWM_D_MISC, "wkill %d\n", args->id);
3169
3170         if (r->ws->focus == NULL)
3171                 return;
3172
3173         if (args->id == SWM_ARG_ID_KILLWINDOW)
3174                 XKillClient(display, r->ws->focus->id);
3175         else
3176                 if (r->ws->focus->can_delete)
3177                         client_msg(r->ws->focus, adelete);
3178 }
3179
3180
3181 int
3182 floating_toggle_win(struct ws_win *win)
3183 {
3184         struct swm_region       *r;
3185
3186         if (win == NULL)
3187                 return 0;
3188
3189         if (!win->ws->r)
3190                 return 0;
3191
3192         r = win->ws->r;
3193
3194         /* reject floating toggles in max stack mode */
3195         if (win->ws->cur_layout == &layouts[SWM_MAX_STACK])
3196                 return 0;
3197
3198         if (win->floating) {
3199                 if (!win->floatmaxed) {
3200                         /* retain position for refloat */
3201                         store_float_geom(win, r);
3202                 }
3203                 win->floating = 0;
3204         } else {
3205                 if (win->g_floatvalid) {
3206                         /* refloat at last floating relative position */
3207                         win->g.x = win->g_float.x - win->rg_float.x + r->g.x;
3208                         win->g.y = win->g_float.y - win->rg_float.y + r->g.y;
3209                         win->g.w = win->g_float.w;
3210                         win->g.h = win->g_float.h;
3211                 }
3212                 win->floating = 1;
3213         }
3214
3215         ewmh_update_actions(win);
3216
3217         return 1;
3218 }
3219
3220 void
3221 floating_toggle(struct swm_region *r, union arg *args)
3222 {
3223         struct ws_win           *win = r->ws->focus;
3224         union arg               a;
3225
3226         if (win == NULL)
3227                 return;
3228
3229         ewmh_update_win_state(win, ewmh[_NET_WM_STATE_ABOVE].atom,
3230             _NET_WM_STATE_TOGGLE);
3231
3232         stack();
3233         if (focus_mode == SWM_FOCUS_DEFAULT)
3234                 drain_enter_notify();
3235
3236         if (win == win->ws->focus) {
3237                 a.id = SWM_ARG_ID_FOCUSCUR;
3238                 focus(win->ws->r, &a);
3239         }
3240 }
3241
3242 void
3243 resize_window(struct ws_win *win, int center)
3244 {
3245         unsigned int            mask;
3246         XWindowChanges          wc;
3247         struct swm_region       *r;
3248
3249         r = root_to_region(win->wa.root);
3250         bzero(&wc, sizeof wc);
3251         mask = CWBorderWidth | CWWidth | CWHeight;
3252         wc.border_width = border_width;
3253         wc.width = win->g.w;
3254         wc.height = win->g.h;
3255         if (center == SWM_ARG_ID_CENTER) {
3256                 wc.x = (WIDTH(r) - win->g.w) / 2 - border_width;
3257                 wc.y = (HEIGHT(r) - win->g.h) / 2 - border_width;
3258                 mask |= CWX | CWY;
3259         }
3260
3261         DNPRINTF(SWM_D_STACK, "resize_window: win %lu x %d y %d w %d h %d\n",
3262             win->id, wc.x, wc.y, wc.width, wc.height);
3263
3264         XConfigureWindow(display, win->id, mask, &wc);
3265 }
3266
3267 void
3268 resize(struct ws_win *win, union arg *args)
3269 {
3270         XEvent                  ev;
3271         Time                    time = 0;
3272         struct swm_region       *r = win->ws->r;
3273         int                     relx, rely;
3274
3275
3276         DNPRINTF(SWM_D_MOUSE, "resize: win %lu floating %d trans %lu\n",
3277             win->id, win->floating, win->transient);
3278
3279         if (!(win->transient != 0 || win->floating != 0))
3280                 return;
3281
3282         /* reject resizes in max mode for floaters (transient ok) */
3283         if (win->floatmaxed)
3284                 return;
3285
3286         win->manual = 1;
3287         ewmh_update_win_state(win, ewmh[_SWM_WM_STATE_MANUAL].atom,
3288             _NET_WM_STATE_ADD);
3289
3290         stack();
3291         if (focus_mode == SWM_FOCUS_DEFAULT)
3292                 drain_enter_notify();
3293
3294         if (XGrabPointer(display, win->id, False, MOUSEMASK, GrabModeAsync,
3295             GrabModeAsync, None, None /* cursor */, CurrentTime) != GrabSuccess)
3296                 return;
3297
3298         /* place pointer at bottom left corner or nearest point inside r */
3299         if ( win->g.x + win->g.w < r->g.x + r->g.w - 1)
3300                 relx = win->g.w - 1;
3301         else
3302                 relx = r->g.x + r->g.w - win->g.x - 1;
3303
3304         if ( win->g.y + win->g.h < r->g.y + r->g.h - 1)
3305                 rely = win->g.h - 1;
3306         else
3307                 rely = r->g.y + r->g.h - win->g.y - 1;
3308
3309         XWarpPointer(display, None, win->id, 0, 0, 0, 0, relx, rely);
3310         do {
3311                 XMaskEvent(display, MOUSEMASK | ExposureMask |
3312                     SubstructureRedirectMask, &ev);
3313                 switch (ev.type) {
3314                 case ConfigureRequest:
3315                 case Expose:
3316                 case MapRequest:
3317                         handler[ev.type](&ev);
3318                         break;
3319                 case MotionNotify:
3320                         /* do not allow resize outside of region */
3321                         if (    ev.xmotion.x_root < r->g.x ||
3322                                 ev.xmotion.x_root > r->g.x + r->g.w - 1 ||
3323                                 ev.xmotion.y_root < r->g.y ||
3324                                 ev.xmotion.y_root > r->g.y + r->g.h - 1)
3325                                 continue;
3326
3327                         if (ev.xmotion.x <= 1)
3328                                 ev.xmotion.x = 1;
3329                         if (ev.xmotion.y <= 1)
3330                                 ev.xmotion.y = 1;
3331                         win->g.w = ev.xmotion.x + 1;
3332                         win->g.h = ev.xmotion.y + 1;
3333
3334                         /* not free, don't sync more than 120 times / second */
3335                         if ((ev.xmotion.time - time) > (1000 / 120) ) {
3336                                 time = ev.xmotion.time;
3337                                 XSync(display, False);
3338                                 resize_window(win, args->id);
3339                         }
3340                         break;
3341                 }
3342         } while (ev.type != ButtonRelease);
3343         if (time) {
3344                 XSync(display, False);
3345                 resize_window(win, args->id);
3346         }
3347         store_float_geom(win,r);
3348
3349         XWarpPointer(display, None, win->id, 0, 0, 0, 0, win->g.w - 1,
3350             win->g.h - 1);
3351         XUngrabPointer(display, CurrentTime);
3352
3353         /* drain events */
3354         drain_enter_notify();
3355 }
3356
3357 void
3358 move_window(struct ws_win *win)
3359 {
3360         unsigned int            mask;
3361         XWindowChanges          wc;
3362         struct swm_region       *r;
3363
3364         r = root_to_region(win->wa.root);
3365         bzero(&wc, sizeof wc);
3366         mask = CWX | CWY;
3367         wc.x = win->g.x;
3368         wc.y = win->g.y;
3369         wc.border_width = border_width;
3370
3371         DNPRINTF(SWM_D_STACK, "move_window: win %lu x %d y %d w %d h %d\n",
3372             win->id, wc.x, wc.y, wc.width, wc.height);
3373
3374         XConfigureWindow(display, win->id, mask, &wc);
3375 }
3376
3377 void
3378 move(struct ws_win *win, union arg *args)
3379 {
3380         XEvent                  ev;
3381         Time                    time = 0;
3382         struct swm_region       *r = win->ws->r;
3383
3384         DNPRINTF(SWM_D_MOUSE, "move: win %lu floating %d trans %lu\n",
3385             win->id, win->floating, win->transient);
3386
3387         /* in max_stack mode should only move transients */
3388         if (win->ws->cur_layout == &layouts[SWM_MAX_STACK] && !win->transient)
3389                 return;
3390
3391         win->manual = 1;
3392         if (win->floating == 0 && !win->transient) {
3393                 ewmh_update_win_state(win, ewmh[_NET_WM_STATE_ABOVE].atom,
3394                     _NET_WM_STATE_ADD);
3395         }
3396         ewmh_update_win_state(win, ewmh[_SWM_WM_STATE_MANUAL].atom,
3397             _NET_WM_STATE_ADD);
3398
3399         stack();
3400
3401         if (XGrabPointer(display, win->id, False, MOUSEMASK, GrabModeAsync,
3402             GrabModeAsync, None, None /* cursor */, CurrentTime) != GrabSuccess)
3403                 return;
3404         XWarpPointer(display, None, win->id, 0, 0, 0, 0, 0, 0);
3405         do {
3406                 XMaskEvent(display, MOUSEMASK | ExposureMask |
3407                     SubstructureRedirectMask, &ev);
3408                 switch (ev.type) {
3409                 case ConfigureRequest:
3410                 case Expose:
3411                 case MapRequest:
3412                         handler[ev.type](&ev);
3413                         break;
3414                 case MotionNotify:
3415                         /* don't allow to move window origin out of region */
3416                         if (    ev.xmotion.x_root < r->g.x ||
3417                                 ev.xmotion.x_root > r->g.x + r->g.w - 1 ||
3418                                 ev.xmotion.y_root < r->g.y ||
3419                                 ev.xmotion.y_root > r->g.y + r->g.h - 1)
3420                                 continue;
3421
3422                         win->g.x = ev.xmotion.x_root - border_width;
3423                         win->g.y = ev.xmotion.y_root - border_width;
3424
3425                         /* not free, don't sync more than 120 times / second */
3426                         if ((ev.xmotion.time - time) > (1000 / 120) ) {
3427                                 time = ev.xmotion.time;
3428                                 XSync(display, False);
3429                                 move_window(win);
3430                         }
3431                         break;
3432                 }
3433         } while (ev.type != ButtonRelease);
3434         if (time) {
3435                 XSync(display, False);
3436                 move_window(win);
3437         }
3438         store_float_geom(win,r);
3439         XWarpPointer(display, None, win->id, 0, 0, 0, 0, 0, 0);
3440         XUngrabPointer(display, CurrentTime);
3441
3442         /* drain events */
3443         drain_enter_notify();
3444 }
3445
3446 /* user/key callable function IDs */
3447 enum keyfuncid {
3448         kf_cycle_layout,
3449         kf_stack_reset,
3450         kf_master_shrink,
3451         kf_master_grow,
3452         kf_master_add,
3453         kf_master_del,
3454         kf_stack_inc,
3455         kf_stack_dec,
3456         kf_swap_main,
3457         kf_focus_next,
3458         kf_focus_prev,
3459         kf_swap_next,
3460         kf_swap_prev,
3461         kf_spawn_term,
3462         kf_spawn_menu,
3463         kf_quit,
3464         kf_restart,
3465         kf_focus_main,
3466         kf_ws_1,
3467         kf_ws_2,
3468         kf_ws_3,
3469         kf_ws_4,
3470         kf_ws_5,
3471         kf_ws_6,
3472         kf_ws_7,
3473         kf_ws_8,
3474         kf_ws_9,
3475         kf_ws_10,
3476         kf_ws_next,
3477         kf_ws_prev,
3478         kf_ws_prior,
3479         kf_screen_next,
3480         kf_screen_prev,
3481         kf_mvws_1,
3482         kf_mvws_2,
3483         kf_mvws_3,
3484         kf_mvws_4,
3485         kf_mvws_5,
3486         kf_mvws_6,
3487         kf_mvws_7,
3488         kf_mvws_8,
3489         kf_mvws_9,
3490         kf_mvws_10,
3491         kf_bar_toggle,
3492         kf_wind_kill,
3493         kf_wind_del,
3494         kf_screenshot_all,
3495         kf_screenshot_wind,
3496         kf_float_toggle,
3497         kf_version,
3498         kf_spawn_lock,
3499         kf_spawn_initscr,
3500         kf_spawn_custom,
3501         kf_iconify,
3502         kf_uniconify,
3503         kf_raise_toggle,
3504         kf_dumpwins, /* MUST BE LAST */
3505         kf_invalid
3506 };
3507
3508 /* key definitions */
3509 void
3510 dummykeyfunc(struct swm_region *r, union arg *args)
3511 {
3512 };
3513
3514 void
3515 legacyfunc(struct swm_region *r, union arg *args)
3516 {
3517 };
3518
3519 struct keyfunc {
3520         char                    name[SWM_FUNCNAME_LEN];
3521         void                    (*func)(struct swm_region *r, union arg *);
3522         union arg               args;
3523 } keyfuncs[kf_invalid + 1] = {
3524         /* name                 function        argument */
3525         { "cycle_layout",       cycle_layout,   {0} },
3526         { "stack_reset",        stack_config,   {.id = SWM_ARG_ID_STACKRESET} },
3527         { "master_shrink",      stack_config,   {.id = SWM_ARG_ID_MASTERSHRINK} },
3528         { "master_grow",        stack_config,   {.id = SWM_ARG_ID_MASTERGROW} },
3529         { "master_add",         stack_config,   {.id = SWM_ARG_ID_MASTERADD} },
3530         { "master_del",         stack_config,   {.id = SWM_ARG_ID_MASTERDEL} },
3531         { "stack_inc",          stack_config,   {.id = SWM_ARG_ID_STACKINC} },
3532         { "stack_dec",          stack_config,   {.id = SWM_ARG_ID_STACKDEC} },
3533         { "swap_main",          swapwin,        {.id = SWM_ARG_ID_SWAPMAIN} },
3534         { "focus_next",         focus,          {.id = SWM_ARG_ID_FOCUSNEXT} },
3535         { "focus_prev",         focus,          {.id = SWM_ARG_ID_FOCUSPREV} },
3536         { "swap_next",          swapwin,        {.id = SWM_ARG_ID_SWAPNEXT} },
3537         { "swap_prev",          swapwin,        {.id = SWM_ARG_ID_SWAPPREV} },
3538         { "spawn_term",         spawnterm,      {.argv = spawn_term} },
3539         { "spawn_menu",         legacyfunc,     {0} },
3540         { "quit",               quit,           {0} },
3541         { "restart",            restart,        {0} },
3542         { "focus_main",         focus,          {.id = SWM_ARG_ID_FOCUSMAIN} },
3543         { "ws_1",               switchws,       {.id = 0} },
3544         { "ws_2",               switchws,       {.id = 1} },
3545         { "ws_3",               switchws,       {.id = 2} },
3546         { "ws_4",               switchws,       {.id = 3} },
3547         { "ws_5",               switchws,       {.id = 4} },
3548         { "ws_6",               switchws,       {.id = 5} },
3549         { "ws_7",               switchws,       {.id = 6} },
3550         { "ws_8",               switchws,       {.id = 7} },
3551         { "ws_9",               switchws,       {.id = 8} },
3552         { "ws_10",              switchws,       {.id = 9} },
3553         { "ws_next",            cyclews,        {.id = SWM_ARG_ID_CYCLEWS_UP} },
3554         { "ws_prev",            cyclews,        {.id = SWM_ARG_ID_CYCLEWS_DOWN} },
3555         { "ws_prior",           priorws,        {0} },
3556         { "screen_next",        cyclescr,       {.id = SWM_ARG_ID_CYCLESC_UP} },
3557         { "screen_prev",        cyclescr,       {.id = SWM_ARG_ID_CYCLESC_DOWN} },
3558         { "mvws_1",             send_to_ws,     {.id = 0} },
3559         { "mvws_2",             send_to_ws,     {.id = 1} },
3560         { "mvws_3",             send_to_ws,     {.id = 2} },
3561         { "mvws_4",             send_to_ws,     {.id = 3} },
3562         { "mvws_5",             send_to_ws,     {.id = 4} },
3563         { "mvws_6",             send_to_ws,     {.id = 5} },
3564         { "mvws_7",             send_to_ws,     {.id = 6} },
3565         { "mvws_8",             send_to_ws,     {.id = 7} },
3566         { "mvws_9",             send_to_ws,     {.id = 8} },
3567         { "mvws_10",            send_to_ws,     {.id = 9} },
3568         { "bar_toggle",         bar_toggle,     {0} },
3569         { "wind_kill",          wkill,          {.id = SWM_ARG_ID_KILLWINDOW} },
3570         { "wind_del",           wkill,          {.id = SWM_ARG_ID_DELETEWINDOW} },
3571         { "screenshot_all",     legacyfunc,     {0} },
3572         { "screenshot_wind",    legacyfunc,     {0} },
3573         { "float_toggle",       floating_toggle,{0} },
3574         { "version",            version,        {0} },
3575         { "spawn_lock",         legacyfunc,     {0} },
3576         { "spawn_initscr",      legacyfunc,     {0} },
3577         { "spawn_custom",       dummykeyfunc,   {0} },
3578         { "iconify",            iconify,        {0} },
3579         { "uniconify",          uniconify,      {0} },
3580         { "raise_toggle",       raise_toggle,   {0} },
3581         { "dumpwins",           dumpwins,       {0} }, /* MUST BE LAST */
3582         { "invalid key func",   NULL,           {0} },
3583 };
3584 struct key {
3585         unsigned int            mod;
3586         KeySym                  keysym;
3587         enum keyfuncid          funcid;
3588         char                    *spawn_name;
3589 };
3590 int                             keys_size = 0, keys_length = 0;
3591 struct key                      *keys = NULL;
3592
3593 /* mouse */
3594 enum { client_click, root_click };
3595 struct button {
3596         unsigned int            action;
3597         unsigned int            mask;
3598         unsigned int            button;
3599         void                    (*func)(struct ws_win *, union arg *);
3600         union arg               args;
3601 } buttons[] = {
3602           /* action     key             mouse button    func    args */
3603         { client_click, MODKEY,         Button3,        resize, {.id = SWM_ARG_ID_DONTCENTER} },
3604         { client_click, MODKEY | ShiftMask, Button3,    resize, {.id = SWM_ARG_ID_CENTER} },
3605         { client_click, MODKEY,         Button1,        move,   {0} },
3606 };
3607
3608 void
3609 update_modkey(unsigned int mod)
3610 {
3611         int                     i;
3612
3613         mod_key = mod;
3614         for (i = 0; i < keys_length; i++)
3615                 if (keys[i].mod & ShiftMask)
3616                         keys[i].mod = mod | ShiftMask;
3617                 else
3618                         keys[i].mod = mod;
3619
3620         for (i = 0; i < LENGTH(buttons); i++)
3621                 if (buttons[i].mask & ShiftMask)
3622                         buttons[i].mask = mod | ShiftMask;
3623                 else
3624                         buttons[i].mask = mod;
3625 }
3626
3627 /* spawn */
3628 struct spawn_prog {
3629         char                    *name;
3630         int                     argc;
3631         char                    **argv;
3632 };
3633
3634 int                             spawns_size = 0, spawns_length = 0;
3635 struct spawn_prog               *spawns = NULL;
3636
3637 int
3638 spawn_expand(struct swm_region *r, union arg *args, char *spawn_name,
3639     char ***ret_args)
3640 {
3641         struct spawn_prog       *prog = NULL;
3642         int                     i;
3643         char                    *ap, **real_args;
3644
3645         DNPRINTF(SWM_D_SPAWN, "spawn_expand %s\n", spawn_name);
3646
3647         /* find program */
3648         for (i = 0; i < spawns_length; i++) {
3649                 if (!strcasecmp(spawn_name, spawns[i].name))
3650                         prog = &spawns[i];
3651         }
3652         if (prog == NULL) {
3653                 fprintf(stderr, "spawn_custom: program %s not found\n",
3654                     spawn_name);
3655                 return (-1);
3656         }
3657
3658         /* make room for expanded args */
3659         if ((real_args = calloc(prog->argc + 1, sizeof(char *))) == NULL)
3660                 err(1, "spawn_custom: calloc real_args");
3661
3662         /* expand spawn_args into real_args */
3663         for (i = 0; i < prog->argc; i++) {
3664                 ap = prog->argv[i];
3665                 DNPRINTF(SWM_D_SPAWN, "spawn_custom: raw arg = %s\n", ap);
3666                 if (!strcasecmp(ap, "$bar_border")) {
3667                         if ((real_args[i] =
3668                             strdup(r->s->c[SWM_S_COLOR_BAR_BORDER].name))
3669                             == NULL)
3670                                 err(1,  "spawn_custom border color");
3671                 } else if (!strcasecmp(ap, "$bar_color")) {
3672                         if ((real_args[i] =
3673                             strdup(r->s->c[SWM_S_COLOR_BAR].name))
3674                             == NULL)
3675                                 err(1, "spawn_custom bar color");
3676                 } else if (!strcasecmp(ap, "$bar_font")) {
3677                         if ((real_args[i] = strdup(bar_fonts[bar_fidx]))
3678                             == NULL)
3679                                 err(1, "spawn_custom bar fonts");
3680                 } else if (!strcasecmp(ap, "$bar_font_color")) {
3681                         if ((real_args[i] =
3682                             strdup(r->s->c[SWM_S_COLOR_BAR_FONT].name))
3683                             == NULL)
3684                                 err(1, "spawn_custom color font");
3685                 } else if (!strcasecmp(ap, "$color_focus")) {
3686                         if ((real_args[i] =
3687                             strdup(r->s->c[SWM_S_COLOR_FOCUS].name))
3688                             == NULL)
3689                                 err(1, "spawn_custom color focus");
3690                 } else if (!strcasecmp(ap, "$color_unfocus")) {
3691                         if ((real_args[i] =
3692                             strdup(r->s->c[SWM_S_COLOR_UNFOCUS].name))
3693                             == NULL)
3694                                 err(1, "spawn_custom color unfocus");
3695                 } else {
3696                         /* no match --> copy as is */
3697                         if ((real_args[i] = strdup(ap)) == NULL)
3698                                 err(1, "spawn_custom strdup(ap)");
3699                 }
3700                 DNPRINTF(SWM_D_SPAWN, "spawn_custom: cooked arg = %s\n",
3701                     real_args[i]);
3702         }
3703
3704 #ifdef SWM_DEBUG
3705         if ((swm_debug & SWM_D_SPAWN) != 0) {
3706                 fprintf(stderr, "spawn_custom: result = ");
3707                 for (i = 0; i < prog->argc; i++)
3708                         fprintf(stderr, "\"%s\" ", real_args[i]);
3709                 fprintf(stderr, "\n");
3710         }
3711 #endif
3712         *ret_args = real_args;
3713         return (prog->argc);
3714 }
3715
3716 void
3717 spawn_custom(struct swm_region *r, union arg *args, char *spawn_name)
3718 {
3719         union arg               a;
3720         char                    **real_args;
3721         int                     spawn_argc, i;
3722
3723         if ((spawn_argc = spawn_expand(r, args, spawn_name, &real_args)) < 0)
3724                 return;
3725         a.argv = real_args;
3726         if (fork() == 0)
3727                 spawn(r->ws->idx, &a, 1);
3728
3729         for (i = 0; i < spawn_argc; i++)
3730                 free(real_args[i]);
3731         free(real_args);
3732 }
3733
3734 void
3735 spawn_select(struct swm_region *r, union arg *args, char *spawn_name, int *pid)
3736 {
3737         union arg               a;
3738         char                    **real_args;
3739         int                     i, spawn_argc;
3740
3741         if ((spawn_argc = spawn_expand(r, args, spawn_name, &real_args)) < 0)
3742                 return;
3743         a.argv = real_args;
3744
3745         if (pipe(select_list_pipe) == -1)
3746                 err(1, "pipe error");
3747         if (pipe(select_resp_pipe) == -1)
3748                 err(1, "pipe error");
3749
3750         if (signal(SIGPIPE, SIG_IGN) == SIG_ERR)
3751                 err(1, "could not disable SIGPIPE");
3752         switch (*pid = fork()) {
3753         case -1:
3754                 err(1, "cannot fork");
3755                 break;
3756         case 0: /* child */
3757                 if (dup2(select_list_pipe[0], 0) == -1)
3758                         errx(1, "dup2");
3759                 if (dup2(select_resp_pipe[1], 1) == -1)
3760                         errx(1, "dup2");
3761                 close(select_list_pipe[1]);
3762                 close(select_resp_pipe[0]);
3763                 spawn(r->ws->idx, &a, 0);
3764                 break;
3765         default: /* parent */
3766                 close(select_list_pipe[0]);
3767                 close(select_resp_pipe[1]);
3768                 break;
3769         }
3770
3771         for (i = 0; i < spawn_argc; i++)
3772                 free(real_args[i]);
3773         free(real_args);
3774 }
3775
3776 void
3777 setspawn(struct spawn_prog *prog)
3778 {
3779         int                     i, j;
3780
3781         if (prog == NULL || prog->name == NULL)
3782                 return;
3783
3784         /* find existing */
3785         for (i = 0; i < spawns_length; i++) {
3786                 if (!strcmp(spawns[i].name, prog->name)) {
3787                         /* found */
3788                         if (prog->argv == NULL) {
3789                                 /* delete */
3790                                 DNPRINTF(SWM_D_SPAWN,
3791                                     "setspawn: delete #%d %s\n",
3792                                     i, spawns[i].name);
3793                                 free(spawns[i].name);
3794                                 for (j = 0; j < spawns[i].argc; j++)
3795                                         free(spawns[i].argv[j]);
3796                                 free(spawns[i].argv);
3797                                 j = spawns_length - 1;
3798                                 if (i < j)
3799                                         spawns[i] = spawns[j];
3800                                 spawns_length--;
3801                                 free(prog->name);
3802                         } else {
3803                                 /* replace */
3804                                 DNPRINTF(SWM_D_SPAWN,
3805                                     "setspawn: replace #%d %s\n",
3806                                     i, spawns[i].name);
3807                                 free(spawns[i].name);
3808                                 for (j = 0; j < spawns[i].argc; j++)
3809                                         free(spawns[i].argv[j]);
3810                                 free(spawns[i].argv);
3811                                 spawns[i] = *prog;
3812                         }
3813                         /* found case handled */
3814                         free(prog);
3815                         return;
3816                 }
3817         }
3818
3819         if (prog->argv == NULL) {
3820                 fprintf(stderr,
3821                     "error: setspawn: cannot find program %s", prog->name);
3822                 free(prog);
3823                 return;
3824         }
3825
3826         /* not found: add */
3827         if (spawns_size == 0 || spawns == NULL) {
3828                 spawns_size = 4;
3829                 DNPRINTF(SWM_D_SPAWN, "setspawn: init list %d\n", spawns_size);
3830                 spawns = malloc((size_t)spawns_size *
3831                     sizeof(struct spawn_prog));
3832                 if (spawns == NULL) {
3833                         fprintf(stderr, "setspawn: malloc failed\n");
3834                         perror(" failed");
3835                         quit(NULL, NULL);
3836                 }
3837         } else if (spawns_length == spawns_size) {
3838                 spawns_size *= 2;
3839                 DNPRINTF(SWM_D_SPAWN, "setspawn: grow list %d\n", spawns_size);
3840                 spawns = realloc(spawns, (size_t)spawns_size *
3841                     sizeof(struct spawn_prog));
3842                 if (spawns == NULL) {
3843                         fprintf(stderr, "setspawn: realloc failed\n");
3844                         perror(" failed");
3845                         quit(NULL, NULL);
3846                 }
3847         }
3848
3849         if (spawns_length < spawns_size) {
3850                 DNPRINTF(SWM_D_SPAWN, "setspawn: add #%d %s\n",
3851                     spawns_length, prog->name);
3852                 i = spawns_length++;
3853                 spawns[i] = *prog;
3854         } else {
3855                 fprintf(stderr, "spawns array problem?\n");
3856                 if (spawns == NULL) {
3857                         fprintf(stderr, "spawns array is NULL!\n");
3858                         quit(NULL, NULL);
3859                 }
3860         }
3861         free(prog);
3862 }
3863
3864 int
3865 setconfspawn(char *selector, char *value, int flags)
3866 {
3867         struct spawn_prog       *prog;
3868         char                    *vp, *cp, *word;
3869
3870         DNPRINTF(SWM_D_SPAWN, "setconfspawn: [%s] [%s]\n", selector, value);
3871         if ((prog = calloc(1, sizeof *prog)) == NULL)
3872                 err(1, "setconfspawn: calloc prog");
3873         prog->name = strdup(selector);
3874         if (prog->name == NULL)
3875                 err(1, "setconfspawn prog->name");
3876         if ((cp = vp = strdup(value)) == NULL)
3877                 err(1, "setconfspawn: strdup(value) ");
3878         while ((word = strsep(&cp, " \t")) != NULL) {
3879                 DNPRINTF(SWM_D_SPAWN, "setconfspawn: arg [%s]\n", word);
3880                 if (cp)
3881                         cp += (long)strspn(cp, " \t");
3882                 if (strlen(word) > 0) {
3883                         prog->argc++;
3884                         if ((prog->argv = realloc(prog->argv,
3885                             prog->argc * sizeof(char *))) == NULL)
3886                                 err(1, "setconfspawn: realloc");
3887                         if ((prog->argv[prog->argc - 1] = strdup(word)) == NULL)
3888                                 err(1, "setconfspawn: strdup");
3889                 }
3890         }
3891         free(vp);
3892
3893         setspawn(prog);
3894
3895         DNPRINTF(SWM_D_SPAWN, "setconfspawn: done\n");
3896         return (0);
3897 }
3898
3899 void
3900 setup_spawn(void)
3901 {
3902         setconfspawn("term",            "xterm",                0);
3903         setconfspawn("screenshot_all",  "screenshot.sh full",   0);
3904         setconfspawn("screenshot_wind", "screenshot.sh window", 0);
3905         setconfspawn("lock",            "xlock",                0);
3906         setconfspawn("initscr",         "initscreen.sh",        0);
3907         setconfspawn("menu",            "dmenu_run"
3908                                         " -fn $bar_font"
3909                                         " -nb $bar_color"
3910                                         " -nf $bar_font_color"
3911                                         " -sb $bar_border"
3912                                         " -sf $bar_color",      0);
3913         setconfspawn("uniconify",       "dmenu"
3914                                         " -i"
3915                                         " -fn $bar_font"
3916                                         " -nb $bar_color"
3917                                         " -nf $bar_font_color"
3918                                         " -sb $bar_border"
3919                                         " -sf $bar_color",      0);
3920 }
3921
3922 /* key bindings */
3923 #define SWM_MODNAME_SIZE        32
3924 #define SWM_KEY_WS              "\n+ \t"
3925 int
3926 parsekeys(char *keystr, unsigned int currmod, unsigned int *mod, KeySym *ks)
3927 {
3928         char                    *cp, *name;
3929         KeySym                  uks;
3930         DNPRINTF(SWM_D_KEY, "parsekeys: enter [%s]\n", keystr);
3931         if (mod == NULL || ks == NULL) {
3932                 DNPRINTF(SWM_D_KEY, "parsekeys: no mod or key vars\n");
3933                 return (1);
3934         }
3935         if (keystr == NULL || strlen(keystr) == 0) {
3936                 DNPRINTF(SWM_D_KEY, "parsekeys: no keystr\n");
3937                 return (1);
3938         }
3939         cp = keystr;
3940         *ks = NoSymbol;
3941         *mod = 0;
3942         while ((name = strsep(&cp, SWM_KEY_WS)) != NULL) {
3943                 DNPRINTF(SWM_D_KEY, "parsekeys: key [%s]\n", name);
3944                 if (cp)
3945                         cp += (long)strspn(cp, SWM_KEY_WS);
3946                 if (strncasecmp(name, "MOD", SWM_MODNAME_SIZE) == 0)
3947                         *mod |= currmod;
3948                 else if (!strncasecmp(name, "Mod1", SWM_MODNAME_SIZE))
3949                         *mod |= Mod1Mask;
3950                 else if (!strncasecmp(name, "Mod2", SWM_MODNAME_SIZE))
3951                         *mod += Mod2Mask;
3952                 else if (!strncmp(name, "Mod3", SWM_MODNAME_SIZE))
3953                         *mod |= Mod3Mask;
3954                 else if (!strncmp(name, "Mod4", SWM_MODNAME_SIZE))
3955                         *mod |= Mod4Mask;
3956                 else if (strncasecmp(name, "SHIFT", SWM_MODNAME_SIZE) == 0)
3957                         *mod |= ShiftMask;
3958                 else if (strncasecmp(name, "CONTROL", SWM_MODNAME_SIZE) == 0)
3959                         *mod |= ControlMask;
3960                 else {
3961                         *ks = XStringToKeysym(name);
3962                         XConvertCase(*ks, ks, &uks);
3963                         if (ks == NoSymbol) {
3964                                 DNPRINTF(SWM_D_KEY,
3965                                     "parsekeys: invalid key %s\n",
3966                                     name);
3967                                 return (1);
3968                         }
3969                 }
3970         }
3971         DNPRINTF(SWM_D_KEY, "parsekeys: leave ok\n");
3972         return (0);
3973 }
3974
3975 char *
3976 strdupsafe(char *str)
3977 {
3978         if (str == NULL)
3979                 return (NULL);
3980         else
3981                 return (strdup(str));
3982 }
3983
3984 void
3985 setkeybinding(unsigned int mod, KeySym ks, enum keyfuncid kfid,
3986     char *spawn_name)
3987 {
3988         int                     i, j;
3989         DNPRINTF(SWM_D_KEY, "setkeybinding: enter %s [%s]\n",
3990             keyfuncs[kfid].name, spawn_name);
3991         /* find existing */
3992         for (i = 0; i < keys_length; i++) {
3993                 if (keys[i].mod == mod && keys[i].keysym == ks) {
3994                         if (kfid == kf_invalid) {
3995                                 /* found: delete */
3996                                 DNPRINTF(SWM_D_KEY,
3997                                     "setkeybinding: delete #%d %s\n",
3998                                     i, keyfuncs[keys[i].funcid].name);
3999                                 free(keys[i].spawn_name);
4000                                 j = keys_length - 1;
4001                                 if (i < j)
4002                                         keys[i] = keys[j];
4003                                 keys_length--;
4004                                 DNPRINTF(SWM_D_KEY, "setkeybinding: leave\n");
4005                                 return;
4006                         } else {
4007                                 /* found: replace */
4008                                 DNPRINTF(SWM_D_KEY,
4009                                     "setkeybinding: replace #%d %s %s\n",
4010                                     i, keyfuncs[keys[i].funcid].name,
4011                                     spawn_name);
4012                                 free(keys[i].spawn_name);
4013                                 keys[i].mod = mod;
4014                                 keys[i].keysym = ks;
4015                                 keys[i].funcid = kfid;
4016                                 keys[i].spawn_name = strdupsafe(spawn_name);
4017                                 DNPRINTF(SWM_D_KEY, "setkeybinding: leave\n");
4018                                 return;
4019                         }
4020                 }
4021         }
4022         if (kfid == kf_invalid) {
4023                 fprintf(stderr,
4024                     "error: setkeybinding: cannot find mod/key combination");
4025                 DNPRINTF(SWM_D_KEY, "setkeybinding: leave\n");
4026                 return;
4027         }
4028         /* not found: add */
4029         if (keys_size == 0 || keys == NULL) {
4030                 keys_size = 4;
4031                 DNPRINTF(SWM_D_KEY, "setkeybinding: init list %d\n", keys_size);
4032                 keys = malloc((size_t)keys_size * sizeof(struct key));
4033                 if (keys == NULL) {
4034                         fprintf(stderr, "malloc failed\n");
4035                         perror(" failed");
4036                         quit(NULL, NULL);
4037                 }
4038         } else if (keys_length == keys_size) {
4039                 keys_size *= 2;
4040                 DNPRINTF(SWM_D_KEY, "setkeybinding: grow list %d\n", keys_size);
4041                 keys = realloc(keys, (size_t)keys_size * sizeof(struct key));
4042                 if (keys == NULL) {
4043                         fprintf(stderr, "realloc failed\n");
4044                         perror(" failed");
4045                         quit(NULL, NULL);
4046                 }
4047         }
4048         if (keys_length < keys_size) {
4049                 j = keys_length++;
4050                 DNPRINTF(SWM_D_KEY, "setkeybinding: add #%d %s %s\n",
4051                     j, keyfuncs[kfid].name, spawn_name);
4052                 keys[j].mod = mod;
4053                 keys[j].keysym = ks;
4054                 keys[j].funcid = kfid;
4055                 keys[j].spawn_name = strdupsafe(spawn_name);
4056         } else {
4057                 fprintf(stderr, "keys array problem?\n");
4058                 if (keys == NULL) {
4059                         fprintf(stderr, "keys array problem\n");
4060                         quit(NULL, NULL);
4061                 }
4062         }
4063         DNPRINTF(SWM_D_KEY, "setkeybinding: leave\n");
4064 }
4065
4066 int
4067 setconfbinding(char *selector, char *value, int flags)
4068 {
4069         enum keyfuncid          kfid;
4070         unsigned int            mod;
4071         KeySym                  ks;
4072         int                     i;
4073         DNPRINTF(SWM_D_KEY, "setconfbinding: enter\n");
4074         if (selector == NULL) {
4075                 DNPRINTF(SWM_D_KEY, "setconfbinding: unbind %s\n", value);
4076                 if (parsekeys(value, mod_key, &mod, &ks) == 0) {
4077                         kfid = kf_invalid;
4078                         setkeybinding(mod, ks, kfid, NULL);
4079                         return (0);
4080                 } else
4081                         return (1);
4082         }
4083         /* search by key function name */
4084         for (kfid = 0; kfid < kf_invalid; (kfid)++) {
4085                 if (strncasecmp(selector, keyfuncs[kfid].name,
4086                     SWM_FUNCNAME_LEN) == 0) {
4087                         DNPRINTF(SWM_D_KEY, "setconfbinding: %s: match\n",
4088                             selector);
4089                         if (parsekeys(value, mod_key, &mod, &ks) == 0) {
4090                                 setkeybinding(mod, ks, kfid, NULL);
4091                                 return (0);
4092                         } else
4093                                 return (1);
4094                 }
4095         }
4096         /* search by custom spawn name */
4097         for (i = 0; i < spawns_length; i++) {
4098                 if (strcasecmp(selector, spawns[i].name) == 0) {
4099                         DNPRINTF(SWM_D_KEY, "setconfbinding: %s: match\n",
4100                             selector);
4101                         if (parsekeys(value, mod_key, &mod, &ks) == 0) {
4102                                 setkeybinding(mod, ks, kf_spawn_custom,
4103                                     spawns[i].name);
4104                                 return (0);
4105                         } else
4106                                 return (1);
4107                 }
4108         }
4109         DNPRINTF(SWM_D_KEY, "setconfbinding: no match\n");
4110         return (1);
4111 }
4112
4113 void
4114 setup_keys(void)
4115 {
4116         setkeybinding(MODKEY,           XK_space,       kf_cycle_layout,NULL);
4117         setkeybinding(MODKEY|ShiftMask, XK_space,       kf_stack_reset, NULL);
4118         setkeybinding(MODKEY,           XK_h,           kf_master_shrink,NULL);
4119         setkeybinding(MODKEY,           XK_l,           kf_master_grow, NULL);
4120         setkeybinding(MODKEY,           XK_comma,       kf_master_add,  NULL);
4121         setkeybinding(MODKEY,           XK_period,      kf_master_del,  NULL);
4122         setkeybinding(MODKEY|ShiftMask, XK_comma,       kf_stack_inc,   NULL);
4123         setkeybinding(MODKEY|ShiftMask, XK_period,      kf_stack_dec,   NULL);
4124         setkeybinding(MODKEY,           XK_Return,      kf_swap_main,   NULL);
4125         setkeybinding(MODKEY,           XK_j,           kf_focus_next,  NULL);
4126         setkeybinding(MODKEY,           XK_k,           kf_focus_prev,  NULL);
4127         setkeybinding(MODKEY|ShiftMask, XK_j,           kf_swap_next,   NULL);
4128         setkeybinding(MODKEY|ShiftMask, XK_k,           kf_swap_prev,   NULL);
4129         setkeybinding(MODKEY|ShiftMask, XK_Return,      kf_spawn_term,  NULL);
4130         setkeybinding(MODKEY,           XK_p,           kf_spawn_custom,        "menu");
4131         setkeybinding(MODKEY|ShiftMask, XK_q,           kf_quit,        NULL);
4132         setkeybinding(MODKEY,           XK_q,           kf_restart,     NULL);
4133         setkeybinding(MODKEY,           XK_m,           kf_focus_main,  NULL);
4134         setkeybinding(MODKEY,           XK_1,           kf_ws_1,        NULL);
4135         setkeybinding(MODKEY,           XK_2,           kf_ws_2,        NULL);
4136         setkeybinding(MODKEY,           XK_3,           kf_ws_3,        NULL);
4137         setkeybinding(MODKEY,           XK_4,           kf_ws_4,        NULL);
4138         setkeybinding(MODKEY,           XK_5,           kf_ws_5,        NULL);
4139         setkeybinding(MODKEY,           XK_6,           kf_ws_6,        NULL);
4140         setkeybinding(MODKEY,           XK_7,           kf_ws_7,        NULL);
4141         setkeybinding(MODKEY,           XK_8,           kf_ws_8,        NULL);
4142         setkeybinding(MODKEY,           XK_9,           kf_ws_9,        NULL);
4143         setkeybinding(MODKEY,           XK_0,           kf_ws_10,       NULL);
4144         setkeybinding(MODKEY,           XK_Right,       kf_ws_next,     NULL);
4145         setkeybinding(MODKEY,           XK_Left,        kf_ws_prev,     NULL);
4146         setkeybinding(MODKEY,           XK_a,           kf_ws_prior,    NULL);
4147         setkeybinding(MODKEY|ShiftMask, XK_Right,       kf_screen_next, NULL);
4148         setkeybinding(MODKEY|ShiftMask, XK_Left,        kf_screen_prev, NULL);
4149         setkeybinding(MODKEY|ShiftMask, XK_1,           kf_mvws_1,      NULL);
4150         setkeybinding(MODKEY|ShiftMask, XK_2,           kf_mvws_2,      NULL);
4151         setkeybinding(MODKEY|ShiftMask, XK_3,           kf_mvws_3,      NULL);
4152         setkeybinding(MODKEY|ShiftMask, XK_4,           kf_mvws_4,      NULL);
4153         setkeybinding(MODKEY|ShiftMask, XK_5,           kf_mvws_5,      NULL);
4154         setkeybinding(MODKEY|ShiftMask, XK_6,           kf_mvws_6,      NULL);
4155         setkeybinding(MODKEY|ShiftMask, XK_7,           kf_mvws_7,      NULL);
4156         setkeybinding(MODKEY|ShiftMask, XK_8,           kf_mvws_8,      NULL);
4157         setkeybinding(MODKEY|ShiftMask, XK_9,           kf_mvws_9,      NULL);
4158         setkeybinding(MODKEY|ShiftMask, XK_0,           kf_mvws_10,     NULL);
4159         setkeybinding(MODKEY,           XK_b,           kf_bar_toggle,  NULL);
4160         setkeybinding(MODKEY,           XK_Tab,         kf_focus_next,  NULL);
4161         setkeybinding(MODKEY|ShiftMask, XK_Tab,         kf_focus_prev,  NULL);
4162         setkeybinding(MODKEY|ShiftMask, XK_x,           kf_wind_kill,   NULL);
4163         setkeybinding(MODKEY,           XK_x,           kf_wind_del,    NULL);
4164         setkeybinding(MODKEY,           XK_s,           kf_spawn_custom,        "screenshot_all");
4165         setkeybinding(MODKEY|ShiftMask, XK_s,           kf_spawn_custom,        "screenshot_wind");
4166         setkeybinding(MODKEY,           XK_t,           kf_float_toggle,NULL);
4167         setkeybinding(MODKEY|ShiftMask, XK_v,           kf_version,     NULL);
4168         setkeybinding(MODKEY|ShiftMask, XK_Delete,      kf_spawn_custom,        "lock");
4169         setkeybinding(MODKEY|ShiftMask, XK_i,           kf_spawn_custom,        "initscr");
4170         setkeybinding(MODKEY,           XK_w,           kf_iconify,     NULL);
4171         setkeybinding(MODKEY|ShiftMask, XK_w,           kf_uniconify,   NULL);
4172         setkeybinding(MODKEY|ShiftMask, XK_r,           kf_raise_toggle,NULL);
4173 #ifdef SWM_DEBUG
4174         setkeybinding(MODKEY|ShiftMask, XK_d,           kf_dumpwins,    NULL);
4175 #endif
4176 }
4177
4178 void
4179 updatenumlockmask(void)
4180 {
4181         unsigned int            i, j;
4182         XModifierKeymap         *modmap;
4183
4184         DNPRINTF(SWM_D_MISC, "updatenumlockmask\n");
4185         numlockmask = 0;
4186         modmap = XGetModifierMapping(display);
4187         for (i = 0; i < 8; i++)
4188                 for (j = 0; j < modmap->max_keypermod; j++)
4189                         if (modmap->modifiermap[i * modmap->max_keypermod + j]
4190                             == XKeysymToKeycode(display, XK_Num_Lock))
4191                                 numlockmask = (1 << i);
4192
4193         XFreeModifiermap(modmap);
4194 }
4195
4196 void
4197 grabkeys(void)
4198 {
4199         unsigned int            i, j, k;
4200         KeyCode                 code;
4201         unsigned int            modifiers[] =
4202             { 0, LockMask, numlockmask, numlockmask | LockMask };
4203
4204         DNPRINTF(SWM_D_MISC, "grabkeys\n");
4205         updatenumlockmask();
4206
4207         for (k = 0; k < ScreenCount(display); k++) {
4208                 if (TAILQ_EMPTY(&screens[k].rl))
4209                         continue;
4210                 XUngrabKey(display, AnyKey, AnyModifier, screens[k].root);
4211                 for (i = 0; i < keys_length; i++) {
4212                         if ((code = XKeysymToKeycode(display, keys[i].keysym)))
4213                                 for (j = 0; j < LENGTH(modifiers); j++)
4214                                         XGrabKey(display, code,
4215                                             keys[i].mod | modifiers[j],
4216                                             screens[k].root, True,
4217                                             GrabModeAsync, GrabModeAsync);
4218                 }
4219         }
4220 }
4221
4222 void
4223 grabbuttons(struct ws_win *win, int focused)
4224 {
4225         unsigned int            i, j;
4226         unsigned int            modifiers[] =
4227             { 0, LockMask, numlockmask, numlockmask|LockMask };
4228
4229         updatenumlockmask();
4230         XUngrabButton(display, AnyButton, AnyModifier, win->id);
4231         if (focused) {
4232                 for (i = 0; i < LENGTH(buttons); i++)
4233                         if (buttons[i].action == client_click)
4234                                 for (j = 0; j < LENGTH(modifiers); j++)
4235                                         XGrabButton(display, buttons[i].button,
4236                                             buttons[i].mask | modifiers[j],
4237                                             win->id, False, BUTTONMASK,
4238                                             GrabModeAsync, GrabModeSync, None,
4239                                             None);
4240         } else
4241                 XGrabButton(display, AnyButton, AnyModifier, win->id, False,
4242                     BUTTONMASK, GrabModeAsync, GrabModeSync, None, None);
4243 }
4244
4245 const char *quirkname[] = {
4246         "NONE",         /* config string for "no value" */
4247         "FLOAT",
4248         "TRANSSZ",
4249         "ANYWHERE",
4250         "XTERM_FONTADJ",
4251         "FULLSCREEN",
4252         "FOCUSPREV",
4253 };
4254
4255 /* SWM_Q_WS: retain '|' for back compat for now (2009-08-11) */
4256 #define SWM_Q_WS                "\n|+ \t"
4257 int
4258 parsequirks(char *qstr, unsigned long *quirk)
4259 {
4260         char                    *cp, *name;
4261         int                     i;
4262
4263         if (quirk == NULL)
4264                 return (1);
4265
4266         cp = qstr;
4267         *quirk = 0;
4268         while ((name = strsep(&cp, SWM_Q_WS)) != NULL) {
4269                 if (cp)
4270                         cp += (long)strspn(cp, SWM_Q_WS);
4271                 for (i = 0; i < LENGTH(quirkname); i++) {
4272                         if (!strncasecmp(name, quirkname[i], SWM_QUIRK_LEN)) {
4273                                 DNPRINTF(SWM_D_QUIRK,
4274                                     "parsequirks: %s\n", name);
4275                                 if (i == 0) {
4276                                         *quirk = 0;
4277                                         return (0);
4278                                 }
4279                                 *quirk |= 1 << (i-1);
4280                                 break;
4281                         }
4282                 }
4283                 if (i >= LENGTH(quirkname)) {
4284                         DNPRINTF(SWM_D_QUIRK,
4285                             "parsequirks: invalid quirk [%s]\n", name);
4286                         return (1);
4287                 }
4288         }
4289         return (0);
4290 }
4291
4292 void
4293 setquirk(const char *class, const char *name, const int quirk)
4294 {
4295         int                     i, j;
4296
4297         /* find existing */
4298         for (i = 0; i < quirks_length; i++) {
4299                 if (!strcmp(quirks[i].class, class) &&
4300                     !strcmp(quirks[i].name, name)) {
4301                         if (!quirk) {
4302                                 /* found: delete */
4303                                 DNPRINTF(SWM_D_QUIRK,
4304                                     "setquirk: delete #%d %s:%s\n",
4305                                     i, quirks[i].class, quirks[i].name);
4306                                 free(quirks[i].class);
4307                                 free(quirks[i].name);
4308                                 j = quirks_length - 1;
4309                                 if (i < j)
4310                                         quirks[i] = quirks[j];
4311                                 quirks_length--;
4312                                 return;
4313                         } else {
4314                                 /* found: replace */
4315                                 DNPRINTF(SWM_D_QUIRK,
4316                                     "setquirk: replace #%d %s:%s\n",
4317                                     i, quirks[i].class, quirks[i].name);
4318                                 free(quirks[i].class);
4319                                 free(quirks[i].name);
4320                                 quirks[i].class = strdup(class);
4321                                 quirks[i].name = strdup(name);
4322                                 quirks[i].quirk = quirk;
4323                                 return;
4324                         }
4325                 }
4326         }
4327         if (!quirk) {
4328                 fprintf(stderr,
4329                     "error: setquirk: cannot find class/name combination");
4330                 return;
4331         }
4332         /* not found: add */
4333         if (quirks_size == 0 || quirks == NULL) {
4334                 quirks_size = 4;
4335                 DNPRINTF(SWM_D_QUIRK, "setquirk: init list %d\n", quirks_size);
4336                 quirks = malloc((size_t)quirks_size * sizeof(struct quirk));
4337                 if (quirks == NULL) {
4338                         fprintf(stderr, "setquirk: malloc failed\n");
4339                         perror(" failed");
4340                         quit(NULL, NULL);
4341                 }
4342         } else if (quirks_length == quirks_size) {
4343                 quirks_size *= 2;
4344                 DNPRINTF(SWM_D_QUIRK, "setquirk: grow list %d\n", quirks_size);
4345                 quirks = realloc(quirks,
4346                     (size_t)quirks_size * sizeof(struct quirk));
4347                 if (quirks == NULL) {
4348                         fprintf(stderr, "setquirk: realloc failed\n");
4349                         perror(" failed");
4350                         quit(NULL, NULL);
4351                 }
4352         }
4353         if (quirks_length < quirks_size) {
4354                 DNPRINTF(SWM_D_QUIRK, "setquirk: add %d\n", quirks_length);
4355                 j = quirks_length++;
4356                 quirks[j].class = strdup(class);
4357                 quirks[j].name = strdup(name);
4358                 quirks[j].quirk = quirk;
4359         } else {
4360                 fprintf(stderr, "quirks array problem?\n");
4361                 if (quirks == NULL) {
4362                         fprintf(stderr, "quirks array problem!\n");
4363                         quit(NULL, NULL);
4364                 }
4365         }
4366 }
4367
4368 int
4369 setconfquirk(char *selector, char *value, int flags)
4370 {
4371         char                    *cp, *class, *name;
4372         int                     retval;
4373         unsigned long           quirks;
4374         if (selector == NULL)
4375                 return (0);
4376         if ((cp = strchr(selector, ':')) == NULL)
4377                 return (0);
4378         *cp = '\0';
4379         class = selector;
4380         name = cp + 1;
4381         if ((retval = parsequirks(value, &quirks)) == 0)
4382                 setquirk(class, name, quirks);
4383         return (retval);
4384 }
4385
4386 void
4387 setup_quirks(void)
4388 {
4389         setquirk("MPlayer",             "xv",           SWM_Q_FLOAT | SWM_Q_FULLSCREEN | SWM_Q_FOCUSPREV);
4390         setquirk("OpenOffice.org 3.2",  "VCLSalFrame",  SWM_Q_FLOAT);
4391         setquirk("Firefox-bin",         "firefox-bin",  SWM_Q_TRANSSZ);
4392         setquirk("Firefox",             "Dialog",       SWM_Q_FLOAT);
4393         setquirk("Gimp",                "gimp",         SWM_Q_FLOAT | SWM_Q_ANYWHERE);
4394         setquirk("XTerm",               "xterm",        SWM_Q_XTERM_FONTADJ);
4395         setquirk("xine",                "Xine Window",  SWM_Q_FLOAT | SWM_Q_ANYWHERE);
4396         setquirk("Xitk",                "Xitk Combo",   SWM_Q_FLOAT | SWM_Q_ANYWHERE);
4397         setquirk("xine",                "xine Panel",   SWM_Q_FLOAT | SWM_Q_ANYWHERE);
4398         setquirk("Xitk",                "Xine Window",  SWM_Q_FLOAT | SWM_Q_ANYWHERE);
4399         setquirk("xine",                "xine Video Fullscreen Window", SWM_Q_FULLSCREEN | SWM_Q_FLOAT);
4400         setquirk("pcb",                 "pcb",          SWM_Q_FLOAT);
4401         setquirk("SDL_App",             "SDL_App",      SWM_Q_FLOAT | SWM_Q_FULLSCREEN);
4402 }
4403
4404 /* conf file stuff */
4405 #define SWM_CONF_FILE   "scrotwm.conf"
4406
4407 enum    { SWM_S_BAR_DELAY, SWM_S_BAR_ENABLED, SWM_S_BAR_BORDER_WIDTH,
4408           SWM_S_STACK_ENABLED, SWM_S_CLOCK_ENABLED, SWM_S_CLOCK_FORMAT,
4409           SWM_S_CYCLE_EMPTY, SWM_S_CYCLE_VISIBLE, SWM_S_SS_ENABLED,
4410           SWM_S_TERM_WIDTH, SWM_S_TITLE_CLASS_ENABLED,
4411           SWM_S_TITLE_NAME_ENABLED, SWM_S_WINDOW_NAME_ENABLED,
4412           SWM_S_FOCUS_MODE, SWM_S_DISABLE_BORDER, SWM_S_BORDER_WIDTH,
4413           SWM_S_BAR_FONT, SWM_S_BAR_ACTION, SWM_S_SPAWN_TERM,
4414           SWM_S_SS_APP, SWM_S_DIALOG_RATIO, SWM_S_BAR_AT_BOTTOM
4415         };
4416
4417 int
4418 setconfvalue(char *selector, char *value, int flags)
4419 {
4420         switch (flags) {
4421         case SWM_S_BAR_DELAY:
4422                 bar_delay = atoi(value);
4423                 break;
4424         case SWM_S_BAR_ENABLED:
4425                 bar_enabled = atoi(value);
4426                 break;
4427         case SWM_S_BAR_BORDER_WIDTH:
4428                 bar_border_width = atoi(value);
4429                 break;
4430         case SWM_S_BAR_AT_BOTTOM:
4431                 bar_at_bottom = atoi(value);
4432                 break;
4433         case SWM_S_STACK_ENABLED:
4434                 stack_enabled = atoi(value);
4435                 break;
4436         case SWM_S_CLOCK_ENABLED:
4437                 clock_enabled = atoi(value);
4438                 break;
4439         case SWM_S_CLOCK_FORMAT:
4440 #ifndef SWM_DENY_CLOCK_FORMAT
4441                 free(clock_format);
4442                 if ((clock_format = strdup(value)) == NULL)
4443                         err(1, "setconfvalue: clock_format");
4444 #endif
4445                 break;
4446         case SWM_S_CYCLE_EMPTY:
4447                 cycle_empty = atoi(value);
4448                 break;
4449         case SWM_S_CYCLE_VISIBLE:
4450                 cycle_visible = atoi(value);
4451                 break;
4452         case SWM_S_SS_ENABLED:
4453                 ss_enabled = atoi(value);
4454                 break;
4455         case SWM_S_TERM_WIDTH:
4456                 term_width = atoi(value);
4457                 break;
4458         case SWM_S_TITLE_CLASS_ENABLED:
4459                 title_class_enabled = atoi(value);
4460                 break;
4461         case SWM_S_WINDOW_NAME_ENABLED:
4462                 window_name_enabled = atoi(value);
4463                 break;
4464         case SWM_S_TITLE_NAME_ENABLED:
4465                 title_name_enabled = atoi(value);
4466                 break;
4467         case SWM_S_FOCUS_MODE:
4468                 if (!strcmp(value, "default"))
4469                         focus_mode = SWM_FOCUS_DEFAULT;
4470                 else if (!strcmp(value, "follow_cursor"))
4471                         focus_mode = SWM_FOCUS_FOLLOW;
4472                 else if (!strcmp(value, "synergy"))
4473                         focus_mode = SWM_FOCUS_SYNERGY;
4474                 else
4475                         err(1, "focus_mode");
4476                 break;
4477         case SWM_S_DISABLE_BORDER:
4478                 disable_border = atoi(value);
4479                 break;
4480         case SWM_S_BORDER_WIDTH:
4481                 border_width = atoi(value);
4482                 break;
4483         case SWM_S_BAR_FONT:
4484                 free(bar_fonts[0]);
4485                 if ((bar_fonts[0] = strdup(value)) == NULL)
4486                         err(1, "setconfvalue: bar_font");
4487                 break;
4488         case SWM_S_BAR_ACTION:
4489                 free(bar_argv[0]);
4490                 if ((bar_argv[0] = strdup(value)) == NULL)
4491                         err(1, "setconfvalue: bar_action");
4492                 break;
4493         case SWM_S_SPAWN_TERM:
4494                 free(spawn_term[0]);
4495                 if ((spawn_term[0] = strdup(value)) == NULL)
4496                         err(1, "setconfvalue: spawn_term");
4497                 break;
4498         case SWM_S_SS_APP:
4499                 break;
4500         case SWM_S_DIALOG_RATIO:
4501                 dialog_ratio = atof(value);
4502                 if (dialog_ratio > 1.0 || dialog_ratio <= .3)
4503                         dialog_ratio = .6;
4504                 break;
4505         default:
4506                 return (1);
4507         }
4508         return (0);
4509 }
4510
4511 int
4512 setconfmodkey(char *selector, char *value, int flags)
4513 {
4514         if (!strncasecmp(value, "Mod1", strlen("Mod1")))
4515                 update_modkey(Mod1Mask);
4516         else if (!strncasecmp(value, "Mod2", strlen("Mod2")))
4517                 update_modkey(Mod2Mask);
4518         else if (!strncasecmp(value, "Mod3", strlen("Mod3")))
4519                 update_modkey(Mod3Mask);
4520         else if (!strncasecmp(value, "Mod4", strlen("Mod4")))
4521                 update_modkey(Mod4Mask);
4522         else
4523                 return (1);
4524         return (0);
4525 }
4526
4527 int
4528 setconfcolor(char *selector, char *value, int flags)
4529 {
4530         setscreencolor(value, ((selector == NULL)?-1:atoi(selector)), flags);
4531         return (0);
4532 }
4533
4534 int
4535 setconfregion(char *selector, char *value, int flags)
4536 {
4537         custom_region(value);
4538         return (0);
4539 }
4540
4541 int
4542 setautorun(char *selector, char *value, int flags)
4543 {
4544         int                     ws_id;
4545         char                    s[1024];
4546         char                    *ap, *sp = s;
4547         union arg               a;
4548         int                     argc = 0;
4549         long                    pid;
4550         struct pid_e            *p;
4551
4552         if (getenv("SWM_STARTED"))
4553                 return (0);
4554
4555         bzero(s, sizeof s);
4556         if (sscanf(value, "ws[%d]:%1023c", &ws_id, s) != 2)
4557                 errx(1, "invalid autorun entry, should be 'ws[<idx>]:command'\n");
4558         ws_id--;
4559         if (ws_id < 0 || ws_id >= SWM_WS_MAX)
4560                 errx(1, "autorun: invalid workspace %d\n", ws_id + 1);
4561
4562         /*
4563          * This is a little intricate
4564          *
4565          * If the pid already exists we simply reuse it because it means it was
4566          * used before AND not claimed by manage_window.  We get away with
4567          * altering it in the parent after INSERT because this can not be a race
4568          */
4569         a.argv = NULL;
4570         while ((ap = strsep(&sp, " \t")) != NULL) {
4571                 if (*ap == '\0')
4572                         continue;
4573                 DNPRINTF(SWM_D_SPAWN, "setautorun: arg [%s]\n", ap);
4574                 argc++;
4575                 if ((a.argv = realloc(a.argv, argc * sizeof(char *))) == NULL)
4576                         err(1, "setautorun: realloc");
4577                 a.argv[argc - 1] = ap;
4578         }
4579
4580         if ((a.argv = realloc(a.argv, (argc + 1) * sizeof(char *))) == NULL)
4581                 err(1, "setautorun: realloc");
4582         a.argv[argc] = NULL;
4583
4584         if ((pid = fork()) == 0) {
4585                 spawn(ws_id, &a, 1);
4586                 /* NOTREACHED */
4587                 _exit(1);
4588         }
4589         free(a.argv);
4590
4591         /* parent */
4592         p = find_pid(pid);
4593         if (p == NULL) {
4594                 p = calloc(1, sizeof *p);
4595                 if (p == NULL)
4596                         return (1);
4597                 TAILQ_INSERT_TAIL(&pidlist, p, entry);
4598         }
4599
4600         p->pid = pid;
4601         p->ws = ws_id;
4602
4603         return (0);
4604 }
4605
4606 int
4607 setlayout(char *selector, char *value, int flags)
4608 {
4609         int                     ws_id, st, i, x, mg, ma, si, raise;
4610         char                    s[1024];
4611         struct workspace        *ws;
4612
4613         if (getenv("SWM_STARTED"))
4614                 return (0);
4615
4616         bzero(s, sizeof s);
4617         if (sscanf(value, "ws[%d]:%d:%d:%d:%d:%1023c",
4618             &ws_id, &mg, &ma, &si, &raise, s) != 6)
4619                 errx(1, "invalid layout entry, should be 'ws[<idx>]:"
4620                     "<master_grow>:<master_add>:<stack_inc>:<always_raise>:"
4621                     "<type>'\n");
4622         ws_id--;
4623         if (ws_id < 0 || ws_id >= SWM_WS_MAX)
4624                 errx(1, "layout: invalid workspace %d\n", ws_id + 1);
4625
4626         if (!strcasecmp(s, "vertical"))
4627                 st = SWM_V_STACK;
4628         else if (!strcasecmp(s, "horizontal"))
4629                 st = SWM_H_STACK;
4630         else if (!strcasecmp(s, "fullscreen"))
4631                 st = SWM_MAX_STACK;
4632         else
4633                 errx(1, "invalid layout entry, should be 'ws[<idx>]:"
4634                     "<master_grow>:<master_add>:<stack_inc>:<always_raise>:"
4635                     "<type>'\n");
4636
4637         for (i = 0; i < ScreenCount(display); i++) {
4638                 ws = (struct workspace *)&screens[i].ws;
4639                 ws[ws_id].cur_layout = &layouts[st];
4640
4641                 ws[ws_id].always_raise = raise;
4642                 if (st == SWM_MAX_STACK)
4643                         continue;
4644
4645                 /* master grow */
4646                 for (x = 0; x < abs(mg); x++) {
4647                         ws[ws_id].cur_layout->l_config(&ws[ws_id],
4648                             mg >= 0 ?  SWM_ARG_ID_MASTERGROW :
4649                             SWM_ARG_ID_MASTERSHRINK);
4650                         stack();
4651                 }
4652                 /* master add */
4653                 for (x = 0; x < abs(ma); x++) {
4654                         ws[ws_id].cur_layout->l_config(&ws[ws_id],
4655                             ma >= 0 ?  SWM_ARG_ID_MASTERADD :
4656                             SWM_ARG_ID_MASTERDEL);
4657                         stack();
4658                 }
4659                 /* stack inc */
4660                 for (x = 0; x < abs(si); x++) {
4661                         ws[ws_id].cur_layout->l_config(&ws[ws_id],
4662                             si >= 0 ?  SWM_ARG_ID_STACKINC :
4663                             SWM_ARG_ID_STACKDEC);
4664                         stack();
4665                 }
4666         }
4667
4668         return (0);
4669 }
4670
4671 /* config options */
4672 struct config_option {
4673         char                    *optname;
4674         int                     (*func)(char*, char*, int);
4675         int                     funcflags;
4676 };
4677 struct config_option configopt[] = {
4678         { "bar_enabled",                setconfvalue,   SWM_S_BAR_ENABLED },
4679         { "bar_at_bottom",              setconfvalue,   SWM_S_BAR_AT_BOTTOM },
4680         { "bar_border",                 setconfcolor,   SWM_S_COLOR_BAR_BORDER },
4681         { "bar_border_width",           setconfvalue,   SWM_S_BAR_BORDER_WIDTH },
4682         { "bar_color",                  setconfcolor,   SWM_S_COLOR_BAR },
4683         { "bar_font_color",             setconfcolor,   SWM_S_COLOR_BAR_FONT },
4684         { "bar_font",                   setconfvalue,   SWM_S_BAR_FONT },
4685         { "bar_action",                 setconfvalue,   SWM_S_BAR_ACTION },
4686         { "bar_delay",                  setconfvalue,   SWM_S_BAR_DELAY },
4687         { "bind",                       setconfbinding, 0 },
4688         { "stack_enabled",              setconfvalue,   SWM_S_STACK_ENABLED },
4689         { "clock_enabled",              setconfvalue,   SWM_S_CLOCK_ENABLED },
4690         { "clock_format",               setconfvalue,   SWM_S_CLOCK_FORMAT },
4691         { "color_focus",                setconfcolor,   SWM_S_COLOR_FOCUS },
4692         { "color_unfocus",              setconfcolor,   SWM_S_COLOR_UNFOCUS },
4693         { "cycle_empty",                setconfvalue,   SWM_S_CYCLE_EMPTY },
4694         { "cycle_visible",              setconfvalue,   SWM_S_CYCLE_VISIBLE },
4695         { "dialog_ratio",               setconfvalue,   SWM_S_DIALOG_RATIO },
4696         { "modkey",                     setconfmodkey,  0 },
4697         { "program",                    setconfspawn,   0 },
4698         { "quirk",                      setconfquirk,   0 },
4699         { "region",                     setconfregion,  0 },
4700         { "spawn_term",                 setconfvalue,   SWM_S_SPAWN_TERM },
4701         { "screenshot_enabled",         setconfvalue,   SWM_S_SS_ENABLED },
4702         { "screenshot_app",             setconfvalue,   SWM_S_SS_APP },
4703         { "window_name_enabled",        setconfvalue,   SWM_S_WINDOW_NAME_ENABLED },
4704         { "term_width",                 setconfvalue,   SWM_S_TERM_WIDTH },
4705         { "title_class_enabled",        setconfvalue,   SWM_S_TITLE_CLASS_ENABLED },
4706         { "title_name_enabled",         setconfvalue,   SWM_S_TITLE_NAME_ENABLED },
4707         { "focus_mode",                 setconfvalue,   SWM_S_FOCUS_MODE },
4708         { "disable_border",             setconfvalue,   SWM_S_DISABLE_BORDER },
4709         { "border_width",               setconfvalue,   SWM_S_BORDER_WIDTH },
4710         { "autorun",                    setautorun,     0 },
4711         { "layout",                     setlayout,      0 },
4712 };
4713
4714
4715 int
4716 conf_load(char *filename)
4717 {
4718         FILE                    *config;
4719         char                    *line, *cp, *optsub, *optval;
4720         size_t                  linelen, lineno = 0;
4721         int                     wordlen, i, optind;
4722         struct config_option    *opt;
4723
4724         DNPRINTF(SWM_D_CONF, "conf_load begin\n");
4725
4726         if (filename == NULL) {
4727                 fprintf(stderr, "conf_load: no filename\n");
4728                 return (1);
4729         }
4730         if ((config = fopen(filename, "r")) == NULL) {
4731                 warn("conf_load: fopen");
4732                 return (1);
4733         }
4734
4735         while (!feof(config)) {
4736                 if ((line = fparseln(config, &linelen, &lineno, NULL, 0))
4737                     == NULL) {
4738                         if (ferror(config))
4739                                 err(1, "%s", filename);
4740                         else
4741                                 continue;
4742                 }
4743                 cp = line;
4744                 cp += strspn(cp, " \t\n"); /* eat whitespace */
4745                 if (cp[0] == '\0') {
4746                         /* empty line */
4747                         free(line);
4748                         continue;
4749                 }
4750                 /* get config option */
4751                 wordlen = strcspn(cp, "=[ \t\n");
4752                 if (wordlen == 0) {
4753                         warnx("%s: line %zd: no option found",
4754                             filename, lineno);
4755                         return (1);
4756                 }
4757                 optind = -1;
4758                 for (i = 0; i < LENGTH(configopt); i++) {
4759                         opt = &configopt[i];
4760                         if (!strncasecmp(cp, opt->optname, wordlen) &&
4761                             strlen(opt->optname) == wordlen) {
4762                                 optind = i;
4763                                 break;
4764                         }
4765                 }
4766                 if (optind == -1) {
4767                         warnx("%s: line %zd: unknown option %.*s",
4768                             filename, lineno, wordlen, cp);
4769                         return (1);
4770                 }
4771                 cp += wordlen;
4772                 cp += strspn(cp, " \t\n"); /* eat whitespace */
4773                 /* get [selector] if any */
4774                 optsub = NULL;
4775                 if (*cp == '[') {
4776                         cp++;
4777                         wordlen = strcspn(cp, "]");
4778                         if (*cp != ']') {
4779                                 if (wordlen == 0) {
4780                                         warnx("%s: line %zd: syntax error",
4781                                             filename, lineno);
4782                                         return (1);
4783                                 }
4784                                 asprintf(&optsub, "%.*s", wordlen, cp);
4785                         }
4786                         cp += wordlen;
4787                         cp += strspn(cp, "] \t\n"); /* eat trailing */
4788                 }
4789                 cp += strspn(cp, "= \t\n"); /* eat trailing */
4790                 /* get RHS value */
4791                 optval = strdup(cp);
4792                 /* call function to deal with it all */
4793                 if (configopt[optind].func(optsub, optval,
4794                     configopt[optind].funcflags) != 0) {
4795                         fprintf(stderr, "%s line %zd: %s\n",
4796                             filename, lineno, line);
4797                         errx(1, "%s: line %zd: invalid data for %s",
4798                             filename, lineno, configopt[optind].optname);
4799                 }
4800                 free(optval);
4801                 free(optsub);
4802                 free(line);
4803         }
4804
4805         fclose(config);
4806         DNPRINTF(SWM_D_CONF, "conf_load end\n");
4807
4808         return (0);
4809 }
4810
4811 void
4812 set_child_transient(struct ws_win *win, Window *trans)
4813 {
4814         struct ws_win           *parent, *w;
4815         XWMHints                *wmh = NULL;
4816         struct swm_region       *r;
4817         struct workspace        *ws;
4818
4819         parent = find_window(win->transient);
4820         if (parent)
4821                 parent->child_trans = win;
4822         else {
4823                 DNPRINTF(SWM_D_MISC, "set_child_transient: parent doesn't exist"
4824                     " for %lu trans %lu\n", win->id, win->transient);
4825
4826                 if (win->hints == NULL) {
4827                         fprintf(stderr, "no hints for %lu\n", win->id);
4828                         return;
4829                 }
4830
4831                 r = root_to_region(win->wa.root);
4832                 ws = r->ws;
4833                 /* parent doen't exist in our window list */
4834                 TAILQ_FOREACH(w, &ws->winlist, entry) {
4835                         if (wmh)
4836                                 XFree(wmh);
4837
4838                         if ((wmh = XGetWMHints(display, w->id)) == NULL) {
4839                                 fprintf(stderr, "can't get hints for %lu\n",
4840                                     w->id);
4841                                 continue;
4842                         }
4843
4844                         if (win->hints->window_group != wmh->window_group)
4845                                 continue;
4846
4847                         w->child_trans = win;
4848                         win->transient = w->id;
4849                         *trans = w->id;
4850                         DNPRINTF(SWM_D_MISC, "set_child_transient: asjusting "
4851                             "transient to %lu\n", win->transient);
4852                         break;
4853                 }
4854         }
4855
4856         if (wmh)
4857                 XFree(wmh);
4858 }
4859
4860 long
4861 window_get_pid(Window win)
4862 {
4863         Atom                    actual_type_return;
4864         int                     actual_format_return = 0;
4865         unsigned long           nitems_return = 0;
4866         unsigned long           bytes_after_return = 0;
4867         long                    *pid = NULL;
4868         long                    ret = 0;
4869         const char              *errstr;
4870         unsigned char           *prop = NULL;
4871
4872         if (XGetWindowProperty(display, win,
4873             XInternAtom(display, "_NET_WM_PID", False), 0, 1, False,
4874             XA_CARDINAL, &actual_type_return, &actual_format_return,
4875             &nitems_return, &bytes_after_return,
4876             (unsigned char**)(void*)&pid) != Success)
4877                 goto tryharder;
4878         if (actual_type_return != XA_CARDINAL)
4879                 goto tryharder;
4880         if (pid == NULL)
4881                 goto tryharder;
4882
4883         ret = *pid;
4884         XFree(pid);
4885
4886         return (ret);
4887
4888 tryharder:
4889         if (XGetWindowProperty(display, win,
4890             XInternAtom(display, "_SWM_PID", False), 0, SWM_PROPLEN, False,
4891             XA_STRING, &actual_type_return, &actual_format_return,
4892             &nitems_return, &bytes_after_return, &prop) != Success)
4893                 return (0);
4894         if (actual_type_return != XA_STRING)
4895                 return (0);
4896         if (prop == NULL)
4897                 return (0);
4898
4899         ret = strtonum(prop, 0, UINT_MAX, &errstr);
4900         /* ignore error because strtonum returns 0 anyway */
4901         XFree(prop);
4902
4903         return (ret);
4904 }
4905
4906 struct ws_win *
4907 manage_window(Window id)
4908 {
4909         Window                  trans = 0;
4910         struct workspace        *ws;
4911         struct ws_win           *win, *ww;
4912         int                     format, i, ws_idx, n, border_me = 0;
4913         unsigned long           nitems, bytes;
4914         Atom                    ws_idx_atom = 0, type;
4915         Atom                    *prot = NULL, *pp;
4916         unsigned char           ws_idx_str[SWM_PROPLEN], *prop = NULL;
4917         struct swm_region       *r;
4918         long                    mask;
4919         const char              *errstr;
4920         XWindowChanges          wc;
4921         struct pid_e            *p;
4922
4923         if ((win = find_window(id)) != NULL)
4924                 return (win);   /* already being managed */
4925
4926         /* see if we are on the unmanaged list */
4927         if ((win = find_unmanaged_window(id)) != NULL) {
4928                 DNPRINTF(SWM_D_MISC, "manage previously unmanaged window "
4929                     "%lu\n", win->id);
4930                 TAILQ_REMOVE(&win->ws->unmanagedlist, win, entry);
4931                 if (win->transient) {
4932                         set_child_transient(win, &trans);
4933                 } if (trans && (ww = find_window(trans)))
4934                         TAILQ_INSERT_AFTER(&win->ws->winlist, ww, win, entry);
4935                 else
4936                         TAILQ_INSERT_TAIL(&win->ws->winlist, win, entry);
4937                 ewmh_update_actions(win);
4938                 return (win);
4939         }
4940
4941         if ((win = calloc(1, sizeof(struct ws_win))) == NULL)
4942                 errx(1, "calloc: failed to allocate memory for new window");
4943
4944         win->id = id;
4945
4946         /* see if we need to override the workspace */
4947         p = find_pid(window_get_pid(id));
4948
4949         /* Get all the window data in one shot */
4950         ws_idx_atom = XInternAtom(display, "_SWM_WS", False);
4951         if (ws_idx_atom) {
4952                 XGetWindowProperty(display, id, ws_idx_atom, 0, SWM_PROPLEN,
4953                     False, XA_STRING, &type, &format, &nitems, &bytes, &prop);
4954         }
4955         XGetWindowAttributes(display, id, &win->wa);
4956         XGetWMNormalHints(display, id, &win->sh, &mask);
4957         win->hints = XGetWMHints(display, id);
4958         XGetTransientForHint(display, id, &trans);
4959         if (trans) {
4960                 win->transient = trans;
4961                 set_child_transient(win, &trans);
4962                 DNPRINTF(SWM_D_MISC, "manage_window: win %lu transient %lu\n",
4963                     win->id, win->transient);
4964         }
4965
4966         /* get supported protocols */
4967         if (XGetWMProtocols(display, id, &prot, &n)) {
4968                 for (i = 0, pp = prot; i < n; i++, pp++) {
4969                         if (*pp == takefocus)
4970                                 win->take_focus = 1;
4971                         if (*pp == adelete)
4972                                 win->can_delete = 1;
4973                 }
4974                 if (prot)
4975                         XFree(prot);
4976         }
4977
4978         win->iconic = get_iconic(win);
4979
4980         /*
4981          * Figure out where to put the window. If it was previously assigned to
4982          * a workspace (either by spawn() or manually moving), and isn't
4983          * transient, * put it in the same workspace
4984          */
4985         r = root_to_region(win->wa.root);
4986         if (p) {
4987                 ws = &r->s->ws[p->ws];
4988                 TAILQ_REMOVE(&pidlist, p, entry);
4989                 free(p);
4990                 p = NULL;
4991         } else if (prop && win->transient == 0) {
4992                 DNPRINTF(SWM_D_PROP, "got property _SWM_WS=%s\n", prop);
4993                 ws_idx = strtonum(prop, 0, 9, &errstr);
4994                 if (errstr) {
4995                         DNPRINTF(SWM_D_EVENT, "window idx is %s: %s",
4996                             errstr, prop);
4997                 }
4998                 ws = &r->s->ws[ws_idx];
4999         } else {
5000                 ws = r->ws;
5001                 /* this should launch transients in the same ws as parent */
5002                 if (id && trans)
5003                         if ((ww = find_window(trans)) != NULL)
5004                                 if (ws->r) {
5005                                         ws = ww->ws;
5006                                         if (ww->ws->r)
5007                                                 r = ww->ws->r;
5008                                         else
5009                                                 fprintf(stderr,
5010                                                     "fix this bug mcbride\n");
5011                                         border_me = 1;
5012                                 }
5013         }
5014
5015         /* set up the window layout */
5016         win->id = id;
5017         win->ws = ws;
5018         win->s = r->s;  /* this never changes */
5019         if (trans && (ww = find_window(trans)))
5020                 TAILQ_INSERT_AFTER(&ws->winlist, ww, win, entry);
5021         else
5022                 TAILQ_INSERT_TAIL(&ws->winlist, win, entry);
5023
5024         win->g.w = win->wa.width;
5025         win->g.h = win->wa.height;
5026         win->g.x = win->wa.x;
5027         win->g.y = win->wa.y;
5028         win->g_floatvalid = 0;
5029         win->floatmaxed = 0;
5030         win->ewmh_flags = 0;
5031
5032         /* Set window properties so we can remember this after reincarnation */
5033         if (ws_idx_atom && prop == NULL &&
5034             snprintf(ws_idx_str, SWM_PROPLEN, "%d", ws->idx) < SWM_PROPLEN) {
5035                 DNPRINTF(SWM_D_PROP, "setting property _SWM_WS to %s\n",
5036                     ws_idx_str);
5037                 XChangeProperty(display, win->id, ws_idx_atom, XA_STRING, 8,
5038                     PropModeReplace, ws_idx_str, SWM_PROPLEN);
5039         }
5040         if (prop)
5041                 XFree(prop);
5042
5043         ewmh_autoquirk(win);
5044
5045         if (XGetClassHint(display, win->id, &win->ch)) {
5046                 DNPRINTF(SWM_D_CLASS, "class: %s name: %s\n",
5047                     win->ch.res_class, win->ch.res_name);
5048
5049                 /* java is retarded so treat it special */
5050                 if (strstr(win->ch.res_name, "sun-awt")) {
5051                         win->java = 1;
5052                         border_me = 1;
5053                 }
5054
5055                 for (i = 0; i < quirks_length; i++){
5056                         if (!strcmp(win->ch.res_class, quirks[i].class) &&
5057                             !strcmp(win->ch.res_name, quirks[i].name)) {
5058                                 DNPRINTF(SWM_D_CLASS, "found: %s name: %s\n",
5059                                     win->ch.res_class, win->ch.res_name);
5060                                 if (quirks[i].quirk & SWM_Q_FLOAT) {
5061                                         win->floating = 1;
5062                                         border_me = 1;
5063                                 }
5064                                 win->quirks = quirks[i].quirk;
5065                         }
5066                 }
5067         }
5068
5069         /* alter window position if quirky */
5070         if (win->quirks & SWM_Q_ANYWHERE) {
5071                 win->manual = 1; /* don't center the quirky windows */
5072                 bzero(&wc, sizeof wc);
5073                 mask = 0;
5074                 if (bar_enabled && win->g.y < bar_height) {
5075                         win->g.y = wc.y = bar_height;
5076                         mask |= CWY;
5077                 }
5078                 if (win->g.w + win->g.x > WIDTH(r)) {
5079                         win->g.x = wc.x = WIDTH(r) - win->g.w - 2;
5080                         mask |= CWX;
5081                 }
5082                 border_me = 1;
5083         }
5084
5085         /* Reset font sizes (the bruteforce way; no default keybinding). */
5086         if (win->quirks & SWM_Q_XTERM_FONTADJ) {
5087                 for (i = 0; i < SWM_MAX_FONT_STEPS; i++)
5088                         fake_keypress(win, XK_KP_Subtract, ShiftMask);
5089                 for (i = 0; i < SWM_MAX_FONT_STEPS; i++)
5090                         fake_keypress(win, XK_KP_Add, ShiftMask);
5091         }
5092
5093         ewmh_get_win_state(win);
5094         ewmh_update_actions(win);
5095         ewmh_update_win_state(win, None, _NET_WM_STATE_REMOVE);
5096
5097         /* border me */
5098         if (border_me) {
5099                 bzero(&wc, sizeof wc);
5100                 wc.border_width = border_width;
5101                 mask = CWBorderWidth;
5102                 XConfigureWindow(display, win->id, mask, &wc);
5103         }
5104
5105         XSelectInput(display, id, EnterWindowMask | FocusChangeMask |
5106             PropertyChangeMask | StructureNotifyMask);
5107
5108         /* floaters need to be mapped if they are in the current workspace */
5109         if ((win->floating || win->transient) && (ws->idx == r->ws->idx))
5110                 XMapRaised(display, win->id);
5111
5112         return (win);
5113 }
5114
5115 void
5116 free_window(struct ws_win *win)
5117 {
5118         DNPRINTF(SWM_D_MISC, "free_window:  %lu\n", win->id);
5119
5120         if (win == NULL)
5121                 return;
5122
5123         /* needed for restart wm */
5124         set_win_state(win, WithdrawnState);
5125
5126         TAILQ_REMOVE(&win->ws->unmanagedlist, win, entry);
5127
5128         if (win->ch.res_class)
5129                 XFree(win->ch.res_class);
5130         if (win->ch.res_name)
5131                 XFree(win->ch.res_name);
5132
5133         kill_refs(win);
5134
5135         /* paint memory */
5136         memset(win, 0xff, sizeof *win); /* XXX kill later */
5137
5138         free(win);
5139 }
5140
5141 void
5142 unmanage_window(struct ws_win *win)
5143 {
5144         struct ws_win           *parent;
5145
5146         if (win == NULL)
5147                 return;
5148
5149         DNPRINTF(SWM_D_MISC, "unmanage_window:  %lu\n", win->id);
5150
5151         if (win->transient) {
5152                 parent = find_window(win->transient);
5153                 if (parent)
5154                         parent->child_trans = NULL;
5155         }
5156
5157         /* focus on root just in case */
5158         XSetInputFocus(display, PointerRoot, PointerRoot, CurrentTime);
5159
5160         focus_prev(win);
5161
5162         TAILQ_REMOVE(&win->ws->winlist, win, entry);
5163         TAILQ_INSERT_TAIL(&win->ws->unmanagedlist, win, entry);
5164
5165         kill_refs(win);
5166 }
5167
5168 void
5169 focus_magic(struct ws_win *win)
5170 {
5171         DNPRINTF(SWM_D_FOCUS, "focus_magic: %lu\n", WINID(win));
5172
5173         if (win == NULL)
5174                 return;
5175
5176         if (win->child_trans) {
5177                 /* win = parent & has a transient so focus on that */
5178                 if (win->java) {
5179                         focus_win(win->child_trans);
5180                         if (win->child_trans->take_focus)
5181                                 client_msg(win, takefocus);
5182                 } else {
5183                         /* make sure transient hasn't dissapeared */
5184                         if (validate_win(win->child_trans) == 0) {
5185                                 focus_win(win->child_trans);
5186                                 if (win->child_trans->take_focus)
5187                                         client_msg(win->child_trans, takefocus);
5188                         } else {
5189                                 win->child_trans = NULL;
5190                                 focus_win(win);
5191                                 if (win->take_focus)
5192                                         client_msg(win, takefocus);
5193                         }
5194                 }
5195         } else {
5196                 /* regular focus */
5197                 focus_win(win);
5198                 if (win->take_focus)
5199                         client_msg(win, takefocus);
5200         }
5201 }
5202
5203 void
5204 expose(XEvent *e)
5205 {
5206         DNPRINTF(SWM_D_EVENT, "expose: window: %lu\n", e->xexpose.window);
5207 }
5208
5209 void
5210 keypress(XEvent *e)
5211 {
5212         unsigned int            i;
5213         KeySym                  keysym;
5214         XKeyEvent               *ev = &e->xkey;
5215
5216         DNPRINTF(SWM_D_EVENT, "keypress: window: %lu\n", ev->window);
5217
5218         keysym = XKeycodeToKeysym(display, (KeyCode)ev->keycode, 0);
5219         for (i = 0; i < keys_length; i++)
5220                 if (keysym == keys[i].keysym
5221                     && CLEANMASK(keys[i].mod) == CLEANMASK(ev->state)
5222                     && keyfuncs[keys[i].funcid].func) {
5223                         if (keys[i].funcid == kf_spawn_custom)
5224                                 spawn_custom(
5225                                     root_to_region(ev->root),
5226                                     &(keyfuncs[keys[i].funcid].args),
5227                                     keys[i].spawn_name
5228                                     );
5229                         else
5230                                 keyfuncs[keys[i].funcid].func(
5231                                     root_to_region(ev->root),
5232                                     &(keyfuncs[keys[i].funcid].args)
5233                                     );
5234                 }
5235 }
5236
5237 void
5238 buttonpress(XEvent *e)
5239 {
5240         struct ws_win           *win;
5241         int                     i, action;
5242         XButtonPressedEvent     *ev = &e->xbutton;
5243
5244         DNPRINTF(SWM_D_EVENT, "buttonpress: window: %lu\n", ev->window);
5245
5246         action = root_click;
5247         if ((win = find_window(ev->window)) == NULL)
5248                 return;
5249
5250         focus_magic(win);
5251         action = client_click;
5252
5253         for (i = 0; i < LENGTH(buttons); i++)
5254                 if (action == buttons[i].action && buttons[i].func &&
5255                     buttons[i].button == ev->button &&
5256                     CLEANMASK(buttons[i].mask) == CLEANMASK(ev->state))
5257                         buttons[i].func(win, &buttons[i].args);
5258 }
5259
5260 void
5261 configurerequest(XEvent *e)
5262 {
5263         XConfigureRequestEvent  *ev = &e->xconfigurerequest;
5264         struct ws_win           *win;
5265         int                     new = 0;
5266         XWindowChanges          wc;
5267
5268         if ((win = find_window(ev->window)) == NULL)
5269                 if ((win = find_unmanaged_window(ev->window)) == NULL)
5270                         new = 1;
5271
5272         if (new) {
5273                 DNPRINTF(SWM_D_EVENT, "configurerequest: new window: %lu\n",
5274                     ev->window);
5275                 bzero(&wc, sizeof wc);
5276                 wc.x = ev->x;
5277                 wc.y = ev->y;
5278                 wc.width = ev->width;
5279                 wc.height = ev->height;
5280                 wc.border_width = ev->border_width;
5281                 wc.sibling = ev->above;
5282                 wc.stack_mode = ev->detail;
5283                 XConfigureWindow(display, ev->window, ev->value_mask, &wc);
5284         } else {
5285                 DNPRINTF(SWM_D_EVENT, "configurerequest: change window: %lu\n",
5286                     ev->window);
5287                 config_win(win, ev);
5288         }
5289 }
5290
5291 void
5292 configurenotify(XEvent *e)
5293 {
5294         struct ws_win           *win;
5295         long                    mask;
5296
5297         DNPRINTF(SWM_D_EVENT, "configurenotify: window: %lu\n",
5298             e->xconfigure.window);
5299
5300         win = find_window(e->xconfigure.window);
5301         if (win) {
5302                 XGetWMNormalHints(display, win->id, &win->sh, &mask);
5303                 adjust_font(win);
5304                 if (font_adjusted)
5305                         stack();
5306                 if (focus_mode == SWM_FOCUS_DEFAULT)
5307                         drain_enter_notify();
5308         }
5309 }
5310
5311 void
5312 destroynotify(XEvent *e)
5313 {
5314         struct ws_win           *win;
5315         XDestroyWindowEvent     *ev = &e->xdestroywindow;
5316
5317         DNPRINTF(SWM_D_EVENT, "destroynotify: window %lu\n", ev->window);
5318
5319         if ((win = find_window(ev->window)) == NULL) {
5320                 if ((win = find_unmanaged_window(ev->window)) == NULL)
5321                         return;
5322                 free_window(win);
5323                 return;
5324         }
5325
5326         /* make sure we focus on something */
5327         win->floating = 0;
5328
5329         unmanage_window(win);
5330         stack();
5331         if (focus_mode == SWM_FOCUS_DEFAULT)
5332                 drain_enter_notify();
5333         free_window(win);
5334 }
5335
5336 void
5337 enternotify(XEvent *e)
5338 {
5339         XCrossingEvent          *ev = &e->xcrossing;
5340         XEvent                  cne;
5341         struct ws_win           *win;
5342 #if 0
5343         struct ws_win           *w;
5344         Window                  focus_return;
5345         int                     revert_to_return;
5346 #endif
5347         DNPRINTF(SWM_D_FOCUS, "enternotify: window: %lu mode %d detail %d root "
5348             "%lu subwindow %lu same_screen %d focus %d state %d\n",
5349             ev->window, ev->mode, ev->detail, ev->root, ev->subwindow,
5350             ev->same_screen, ev->focus, ev->state);
5351
5352         switch (focus_mode) {
5353         case SWM_FOCUS_DEFAULT:
5354                 break;
5355         case SWM_FOCUS_FOLLOW:
5356                 break;
5357         case SWM_FOCUS_SYNERGY:
5358 #if 0
5359         /*
5360          * all these checks need to be in this order because the
5361          * XCheckTypedWindowEvent relies on weeding out the previous events
5362          *
5363          * making this code an option would enable a follow mouse for focus
5364          * feature
5365          */
5366
5367         /*
5368          * state is set when we are switching workspaces and focus is set when
5369          * the window or a subwindow already has focus (occurs during restart).
5370          *
5371          * Only honor the focus flag if last_focus_event is not FocusOut,
5372          * this allows scrotwm to continue to control focus when another
5373          * program is also playing with it.
5374          */
5375         if (ev->state || (ev->focus && last_focus_event != FocusOut)) {
5376                 DNPRINTF(SWM_D_EVENT, "ignoring enternotify: focus\n");
5377                 return;
5378         }
5379
5380         /*
5381          * happens when a window is created or destroyed and the border
5382          * crosses the mouse pointer and when switching ws
5383          *
5384          * we need the subwindow test to see if we came from root in order
5385          * to give focus to floaters
5386          */
5387         if (ev->mode == NotifyNormal && ev->detail == NotifyVirtual &&
5388             ev->subwindow == 0) {
5389                 DNPRINTF(SWM_D_EVENT, "ignoring enternotify: NotifyVirtual\n");
5390                 return;
5391         }
5392
5393         /* this window already has focus */
5394         if (ev->mode == NotifyNormal && ev->detail == NotifyInferior) {
5395                 DNPRINTF(SWM_D_EVENT, "ignoring enternotify: win has focus\n");
5396                 return;
5397         }
5398
5399         /* this window is being deleted or moved to another ws */
5400         if (XCheckTypedWindowEvent(display, ev->window, ConfigureNotify,
5401             &cne) == True) {
5402                 DNPRINTF(SWM_D_EVENT, "ignoring enternotify: configurenotify\n");
5403                 XPutBackEvent(display, &cne);
5404                 return;
5405         }
5406
5407         if ((win = find_window(ev->window)) == NULL) {
5408                 DNPRINTF(SWM_D_EVENT, "ignoring enternotify: win == NULL\n");
5409                 return;
5410         }
5411
5412         /*
5413          * In fullstack kill all enters unless they come from a different ws
5414          * (i.e. another region) or focus has been grabbed externally.
5415          */
5416         if (win->ws->cur_layout->flags & SWM_L_FOCUSPREV &&
5417             last_focus_event != FocusOut) {
5418                 XGetInputFocus(display, &focus_return, &revert_to_return);
5419                 if ((w = find_window(focus_return)) == NULL ||
5420                     w->ws == win->ws) {
5421                         DNPRINTF(SWM_D_EVENT, "ignoring event: fullstack\n");
5422                         return;
5423                 }
5424         }
5425 #endif
5426                 break;
5427         }
5428
5429         if ((win = find_window(ev->window)) == NULL) {
5430                 DNPRINTF(SWM_D_EVENT, "ignoring enternotify: win == NULL\n");
5431                 return;
5432         }
5433
5434         /*
5435          * if we have more enternotifies let them handle it in due time
5436          */
5437         if (XCheckTypedEvent(display, EnterNotify, &cne) == True) {
5438                 DNPRINTF(SWM_D_EVENT,
5439                     "ignoring enternotify: got more enternotify\n");
5440                 XPutBackEvent(display, &cne);
5441                 return;
5442         }
5443
5444         focus_magic(win);
5445 }
5446
5447 /* lets us use one switch statement for arbitrary mode/detail combinations */
5448 #define MERGE_MEMBERS(a,b)      (((a & 0xffff) << 16) | (b & 0xffff))
5449
5450 void
5451 focusevent(XEvent *e)
5452 {
5453 #if 0
5454         struct ws_win           *win;
5455         u_int32_t               mode_detail;
5456         XFocusChangeEvent       *ev = &e->xfocus;
5457
5458         DNPRINTF(SWM_D_EVENT, "focusevent: %s window: %lu mode %d detail %d\n",
5459             ev->type == FocusIn ? "entering" : "leaving",
5460             ev->window, ev->mode, ev->detail);
5461
5462         if (last_focus_event == ev->type) {
5463                 DNPRINTF(SWM_D_FOCUS, "ignoring focusevent: bad ordering\n");
5464                 return;
5465         }
5466
5467         last_focus_event = ev->type;
5468         mode_detail = MERGE_MEMBERS(ev->mode, ev->detail);
5469
5470         switch (mode_detail) {
5471         /* synergy client focus operations */
5472         case MERGE_MEMBERS(NotifyNormal, NotifyNonlinear):
5473         case MERGE_MEMBERS(NotifyNormal, NotifyNonlinearVirtual):
5474
5475         /* synergy server focus operations */
5476         case MERGE_MEMBERS(NotifyWhileGrabbed, NotifyNonlinear):
5477
5478         /* Entering applications like rdesktop that mangle the pointer */
5479         case MERGE_MEMBERS(NotifyNormal, NotifyPointer):
5480
5481                 if ((win = find_window(e->xfocus.window)) != NULL && win->ws->r)
5482                         XSetWindowBorder(display, win->id,
5483                             win->ws->r->s->c[ev->type == FocusIn ?
5484                             SWM_S_COLOR_FOCUS : SWM_S_COLOR_UNFOCUS].color);
5485                 break;
5486         default:
5487                 fprintf(stderr, "ignoring focusevent\n");
5488                 DNPRINTF(SWM_D_FOCUS, "ignoring focusevent\n");
5489                 break;
5490         }
5491 #endif
5492 }
5493
5494 void
5495 mapnotify(XEvent *e)
5496 {
5497         struct ws_win           *win;
5498         XMapEvent               *ev = &e->xmap;
5499
5500         DNPRINTF(SWM_D_EVENT, "mapnotify: window: %lu\n", ev->window);
5501
5502         win = manage_window(ev->window);
5503         if (win)
5504                 set_win_state(win, NormalState);
5505 }
5506
5507 void
5508 mappingnotify(XEvent *e)
5509 {
5510         XMappingEvent           *ev = &e->xmapping;
5511
5512         XRefreshKeyboardMapping(ev);
5513         if (ev->request == MappingKeyboard)
5514                 grabkeys();
5515 }
5516
5517 void
5518 maprequest(XEvent *e)
5519 {
5520         struct ws_win           *win;
5521         struct swm_region       *r;
5522         XWindowAttributes       wa;
5523         XMapRequestEvent        *ev = &e->xmaprequest;
5524
5525         DNPRINTF(SWM_D_EVENT, "maprequest: window: %lu\n",
5526             e->xmaprequest.window);
5527
5528         if (!XGetWindowAttributes(display, ev->window, &wa))
5529                 return;
5530         if (wa.override_redirect)
5531                 return;
5532
5533         win = manage_window(e->xmaprequest.window);
5534         if (win == NULL)
5535                 return; /* can't happen */
5536
5537         stack();
5538
5539         /* make new win focused */
5540         r = root_to_region(win->wa.root);
5541         if (win->ws == r->ws)
5542                 focus_magic(win);
5543 }
5544
5545 void
5546 propertynotify(XEvent *e)
5547 {
5548         struct ws_win           *win;
5549         XPropertyEvent          *ev = &e->xproperty;
5550
5551         DNPRINTF(SWM_D_EVENT, "propertynotify: window: %lu\n",
5552             ev->window);
5553
5554         win = find_window(ev->window);
5555         if (win == NULL)
5556                 return;
5557
5558         if (ev->state == PropertyDelete && ev->atom == a_swm_iconic) {
5559                 update_iconic(win, 0);
5560                 XMapRaised(display, win->id);
5561                 stack();
5562                 focus_win(win);
5563                 return;
5564         }
5565
5566         switch (ev->atom) {
5567         case XA_WM_NORMAL_HINTS:
5568 #if 0
5569                 long            mask;
5570                 XGetWMNormalHints(display, win->id, &win->sh, &mask);
5571                 fprintf(stderr, "normal hints: flag 0x%x\n", win->sh.flags);
5572                 if (win->sh.flags & PMinSize) {
5573                         win->g.w = win->sh.min_width;
5574                         win->g.h = win->sh.min_height;
5575                         fprintf(stderr, "min %d %d\n", win->g.w, win->g.h);
5576                 }
5577                 XMoveResizeWindow(display, win->id,
5578                     win->g.x, win->g.y, win->g.w, win->g.h);
5579 #endif
5580                 if (window_name_enabled)
5581                         bar_update();
5582                 break;
5583         default:
5584                 break;
5585         }
5586 }
5587
5588 void
5589 unmapnotify(XEvent *e)
5590 {
5591         struct ws_win           *win;
5592
5593         DNPRINTF(SWM_D_EVENT, "unmapnotify: window: %lu\n", e->xunmap.window);
5594
5595         /* determine if we need to help unmanage this window */
5596         win = find_window(e->xunmap.window);
5597         if (win == NULL)
5598                 return;
5599
5600         if (getstate(e->xunmap.window) == NormalState) {
5601                 unmanage_window(win);
5602                 stack();
5603
5604                 /* giant hack for apps that don't destroy transient windows */
5605                 /* eat a bunch of events to prevent remanaging the window */
5606                 XEvent                  cne;
5607                 while (XCheckWindowEvent(display, e->xunmap.window,
5608                     EnterWindowMask, &cne))
5609                         ;
5610                 while (XCheckWindowEvent(display, e->xunmap.window,
5611                     StructureNotifyMask, &cne))
5612                         ;
5613                 while (XCheckWindowEvent(display, e->xunmap.window,
5614                     SubstructureNotifyMask, &cne))
5615                         ;
5616                 /* resend unmap because we ated it */
5617                 XUnmapWindow(display, e->xunmap.window);
5618         }
5619
5620         if (focus_mode == SWM_FOCUS_DEFAULT)
5621                 drain_enter_notify();
5622 }
5623
5624 void
5625 visibilitynotify(XEvent *e)
5626 {
5627         int                     i;
5628         struct swm_region       *r;
5629
5630         DNPRINTF(SWM_D_EVENT, "visibilitynotify: window: %lu\n",
5631             e->xvisibility.window);
5632         if (e->xvisibility.state == VisibilityUnobscured)
5633                 for (i = 0; i < ScreenCount(display); i++)
5634                         TAILQ_FOREACH(r, &screens[i].rl, entry)
5635                                 if (e->xvisibility.window == r->bar_window)
5636                                         bar_update();
5637 }
5638
5639 void
5640 clientmessage(XEvent *e)
5641 {
5642         XClientMessageEvent *ev;
5643         struct ws_win *win;
5644
5645         ev = &e->xclient;
5646
5647         win = find_window(ev->window);
5648         if (win == NULL)
5649                 return;
5650
5651         DNPRINTF(SWM_D_EVENT, "clientmessage: window: 0x%lx type: %ld \n",
5652             ev->window, ev->message_type);
5653
5654         if (ev->message_type == ewmh[_NET_ACTIVE_WINDOW].atom) {
5655                 DNPRINTF(SWM_D_EVENT, "clientmessage: _NET_ACTIVE_WINDOW \n");
5656                 focus_win(win);
5657         }
5658         if (ev->message_type == ewmh[_NET_CLOSE_WINDOW].atom) {
5659                 DNPRINTF(SWM_D_EVENT, "clientmessage: _NET_CLOSE_WINDOW \n");
5660                 if (win->can_delete)
5661                         client_msg(win, adelete);
5662                 else
5663                         XKillClient(display, win->id);
5664         }
5665         if (ev->message_type == ewmh[_NET_MOVERESIZE_WINDOW].atom) {
5666                 DNPRINTF(SWM_D_EVENT,
5667                     "clientmessage: _NET_MOVERESIZE_WINDOW \n");
5668                 if (win->floating) {
5669                         if (ev->data.l[0] & (1<<8)) /* x */
5670                                 win->g.x = ev->data.l[1];
5671                         if (ev->data.l[0] & (1<<9)) /* y */
5672                                 win->g.y = ev->data.l[2];
5673                         if (ev->data.l[0] & (1<<10)) /* width */
5674                                 win->g.w = ev->data.l[3];
5675                         if (ev->data.l[0] & (1<<11)) /* height */
5676                                 win->g.h = ev->data.l[4];
5677                 }
5678                 else {
5679                         /* TODO: Change stack sizes */
5680                 }
5681                 config_win(win, NULL);
5682         }
5683         if (ev->message_type == ewmh[_NET_WM_STATE].atom) {
5684                 DNPRINTF(SWM_D_EVENT, "clientmessage: _NET_WM_STATE \n");
5685                 ewmh_update_win_state(win, ev->data.l[1], ev->data.l[0]);
5686                 if (ev->data.l[2])
5687                         ewmh_update_win_state(win, ev->data.l[2],
5688                             ev->data.l[0]);
5689
5690                 stack();
5691         }
5692 }
5693
5694 int
5695 xerror_start(Display *d, XErrorEvent *ee)
5696 {
5697         other_wm = 1;
5698         return (-1);
5699 }
5700
5701 int
5702 xerror(Display *d, XErrorEvent *ee)
5703 {
5704         /* fprintf(stderr, "error: %p %p\n", display, ee); */
5705         return (-1);
5706 }
5707
5708 int
5709 active_wm(void)
5710 {
5711         other_wm = 0;
5712         xerrorxlib = XSetErrorHandler(xerror_start);
5713
5714         /* this causes an error if some other window manager is running */
5715         XSelectInput(display, DefaultRootWindow(display),
5716             SubstructureRedirectMask);
5717         XSync(display, False);
5718         if (other_wm)
5719                 return (1);
5720
5721         XSetErrorHandler(xerror);
5722         XSync(display, False);
5723         return (0);
5724 }
5725
5726 void
5727 new_region(struct swm_screen *s, int x, int y, int w, int h)
5728 {
5729         struct swm_region       *r, *n;
5730         struct workspace        *ws = NULL;
5731         int                     i;
5732
5733         DNPRINTF(SWM_D_MISC, "new region: screen[%d]:%dx%d+%d+%d\n",
5734              s->idx, w, h, x, y);
5735
5736         /* remove any conflicting regions */
5737         n = TAILQ_FIRST(&s->rl);
5738         while (n) {
5739                 r = n;
5740                 n = TAILQ_NEXT(r, entry);
5741                 if (X(r) < (x + w) &&
5742                     (X(r) + WIDTH(r)) > x &&
5743                     Y(r) < (y + h) &&
5744                     (Y(r) + HEIGHT(r)) > y) {
5745                         if (r->ws->r != NULL)
5746                                 r->ws->old_r = r->ws->r;
5747                         r->ws->r = NULL;
5748                         XDestroyWindow(display, r->bar_window);
5749                         TAILQ_REMOVE(&s->rl, r, entry);
5750                         TAILQ_INSERT_TAIL(&s->orl, r, entry);
5751                 }
5752         }
5753
5754         /* search old regions for one to reuse */
5755
5756         /* size + location match */
5757         TAILQ_FOREACH(r, &s->orl, entry)
5758                 if (X(r) == x && Y(r) == y &&
5759                     HEIGHT(r) == h && WIDTH(r) == w)
5760                         break;
5761
5762         /* size match */
5763         TAILQ_FOREACH(r, &s->orl, entry)
5764                 if (HEIGHT(r) == h && WIDTH(r) == w)
5765                         break;
5766
5767         if (r != NULL) {
5768                 TAILQ_REMOVE(&s->orl, r, entry);
5769                 /* try to use old region's workspace */
5770                 if (r->ws->r == NULL)
5771                         ws = r->ws;
5772         } else
5773                 if ((r = calloc(1, sizeof(struct swm_region))) == NULL)
5774                         errx(1, "calloc: failed to allocate memory for screen");
5775
5776         /* if we don't have a workspace already, find one */
5777         if (ws == NULL) {
5778                 for (i = 0; i < SWM_WS_MAX; i++)
5779                         if (s->ws[i].r == NULL) {
5780                                 ws = &s->ws[i];
5781                                 break;
5782                         }
5783         }
5784
5785         if (ws == NULL)
5786                 errx(1, "no free workspaces\n");
5787
5788         X(r) = x;
5789         Y(r) = y;
5790         WIDTH(r) = w;
5791         HEIGHT(r) = h;
5792         r->s = s;
5793         r->ws = ws;
5794         r->ws_prior = NULL;
5795         ws->r = r;
5796         outputs++;
5797         TAILQ_INSERT_TAIL(&s->rl, r, entry);
5798 }
5799
5800 void
5801 scan_xrandr(int i)
5802 {
5803 #ifdef SWM_XRR_HAS_CRTC
5804         XRRCrtcInfo             *ci;
5805         XRRScreenResources      *sr;
5806         int                     c;
5807         int                     ncrtc = 0;
5808 #endif /* SWM_XRR_HAS_CRTC */
5809         struct swm_region       *r;
5810
5811
5812         if (i >= ScreenCount(display))
5813                 errx(1, "invalid screen");
5814
5815         /* remove any old regions */
5816         while ((r = TAILQ_FIRST(&screens[i].rl)) != NULL) {
5817                 r->ws->old_r = r->ws->r = NULL;
5818                 XDestroyWindow(display, r->bar_window);
5819                 TAILQ_REMOVE(&screens[i].rl, r, entry);
5820                 TAILQ_INSERT_TAIL(&screens[i].orl, r, entry);
5821         }
5822         outputs = 0;
5823
5824         /* map virtual screens onto physical screens */
5825 #ifdef SWM_XRR_HAS_CRTC
5826         if (xrandr_support) {
5827                 sr = XRRGetScreenResources(display, screens[i].root);
5828                 if (sr == NULL)
5829                         new_region(&screens[i], 0, 0,
5830                             DisplayWidth(display, i),
5831                             DisplayHeight(display, i));
5832                 else
5833                         ncrtc = sr->ncrtc;
5834
5835                 for (c = 0, ci = NULL; c < ncrtc; c++) {
5836                         ci = XRRGetCrtcInfo(display, sr, sr->crtcs[c]);
5837                         if (ci->noutput == 0)
5838                                 continue;
5839
5840                         if (ci != NULL && ci->mode == None)
5841                                 new_region(&screens[i], 0, 0,
5842                                     DisplayWidth(display, i),
5843                                     DisplayHeight(display, i));
5844                         else
5845                                 new_region(&screens[i],
5846                                     ci->x, ci->y, ci->width, ci->height);
5847                 }
5848                 if (ci)
5849                         XRRFreeCrtcInfo(ci);
5850                 XRRFreeScreenResources(sr);
5851         } else
5852 #endif /* SWM_XRR_HAS_CRTC */
5853         {
5854                 new_region(&screens[i], 0, 0, DisplayWidth(display, i),
5855                     DisplayHeight(display, i));
5856         }
5857 }
5858
5859 void
5860 screenchange(XEvent *e) {
5861         XRRScreenChangeNotifyEvent      *xe = (XRRScreenChangeNotifyEvent *)e;
5862         struct swm_region               *r;
5863         int                             i;
5864
5865         DNPRINTF(SWM_D_EVENT, "screenchange: %lu\n", xe->root);
5866
5867         if (!XRRUpdateConfiguration(e))
5868                 return;
5869
5870         /* silly event doesn't include the screen index */
5871         for (i = 0; i < ScreenCount(display); i++)
5872                 if (screens[i].root == xe->root)
5873                         break;
5874         if (i >= ScreenCount(display))
5875                 errx(1, "screenchange: screen not found\n");
5876
5877         /* brute force for now, just re-enumerate the regions */
5878         scan_xrandr(i);
5879
5880         /* add bars to all regions */
5881         for (i = 0; i < ScreenCount(display); i++)
5882                 TAILQ_FOREACH(r, &screens[i].rl, entry)
5883                         bar_setup(r);
5884         stack();
5885         if (focus_mode == SWM_FOCUS_DEFAULT)
5886                 drain_enter_notify();
5887 }
5888
5889 void
5890 grab_windows(void)
5891 {
5892         Window                  d1, d2, *wins = NULL;
5893         XWindowAttributes       wa;
5894         unsigned int            no;
5895         int                     i, j;
5896         long                    state, manage;
5897
5898         for (i = 0; i < ScreenCount(display); i++) {
5899                 if (!XQueryTree(display, screens[i].root, &d1, &d2, &wins, &no))
5900                         continue;
5901
5902                 /* attach windows to a region */
5903                 /* normal windows */
5904                 for (j = 0; j < no; j++) {
5905                         if (!XGetWindowAttributes(display, wins[j], &wa) ||
5906                             wa.override_redirect ||
5907                             XGetTransientForHint(display, wins[j], &d1))
5908                                 continue;
5909
5910                         state = getstate(wins[j]);
5911                         manage = state == IconicState;
5912                         if (wa.map_state == IsViewable || manage)
5913                                 manage_window(wins[j]);
5914                 }
5915                 /* transient windows */
5916                 for (j = 0; j < no; j++) {
5917                         if (!XGetWindowAttributes(display, wins[j], &wa) ||
5918                             wa.override_redirect)
5919                                 continue;
5920
5921                         state = getstate(wins[j]);
5922                         manage = state == IconicState;
5923                         if (XGetTransientForHint(display, wins[j], &d1) &&
5924                             manage)
5925                                 manage_window(wins[j]);
5926                 }
5927                 if (wins) {
5928                         XFree(wins);
5929                         wins = NULL;
5930                 }
5931         }
5932 }
5933
5934 void
5935 setup_screens(void)
5936 {
5937         int                     i, j, k;
5938         int                     errorbase, major, minor;
5939         struct workspace        *ws;
5940
5941         if ((screens = calloc(ScreenCount(display),
5942              sizeof(struct swm_screen))) == NULL)
5943                 errx(1, "calloc: screens");
5944
5945         /* initial Xrandr setup */
5946         xrandr_support = XRRQueryExtension(display,
5947             &xrandr_eventbase, &errorbase);
5948         if (xrandr_support)
5949                 if (XRRQueryVersion(display, &major, &minor) && major < 1)
5950                         xrandr_support = 0;
5951
5952         /* map physical screens */
5953         for (i = 0; i < ScreenCount(display); i++) {
5954                 DNPRINTF(SWM_D_WS, "setup_screens: init screen %d\n", i);
5955                 screens[i].idx = i;
5956                 TAILQ_INIT(&screens[i].rl);
5957                 TAILQ_INIT(&screens[i].orl);
5958                 screens[i].root = RootWindow(display, i);
5959
5960                 /* set default colors */
5961                 setscreencolor("red", i + 1, SWM_S_COLOR_FOCUS);
5962                 setscreencolor("rgb:88/88/88", i + 1, SWM_S_COLOR_UNFOCUS);
5963                 setscreencolor("rgb:00/80/80", i + 1, SWM_S_COLOR_BAR_BORDER);
5964                 setscreencolor("black", i + 1, SWM_S_COLOR_BAR);
5965                 setscreencolor("rgb:a0/a0/a0", i + 1, SWM_S_COLOR_BAR_FONT);
5966
5967                 /* set default cursor */
5968                 XDefineCursor(display, screens[i].root,
5969                     XCreateFontCursor(display, XC_left_ptr));
5970
5971                 /* init all workspaces */
5972                 /* XXX these should be dynamically allocated too */
5973                 for (j = 0; j < SWM_WS_MAX; j++) {
5974                         ws = &screens[i].ws[j];
5975                         ws->idx = j;
5976                         ws->focus = NULL;
5977                         ws->r = NULL;
5978                         ws->old_r = NULL;
5979                         TAILQ_INIT(&ws->winlist);
5980                         TAILQ_INIT(&ws->unmanagedlist);
5981
5982                         for (k = 0; layouts[k].l_stack != NULL; k++)
5983                                 if (layouts[k].l_config != NULL)
5984                                         layouts[k].l_config(ws,
5985                                             SWM_ARG_ID_STACKINIT);
5986                         ws->cur_layout = &layouts[0];
5987                 }
5988
5989                 scan_xrandr(i);
5990
5991                 if (xrandr_support)
5992                         XRRSelectInput(display, screens[i].root,
5993                             RRScreenChangeNotifyMask);
5994         }
5995 }
5996
5997 void
5998 setup_globals(void)
5999 {
6000         if ((bar_fonts[0] = strdup("-*-terminus-medium-*-*-*-*-*-*-*-*-*-*-*"))
6001             == NULL)
6002                 err(1, "setup_globals: strdup");
6003         if ((bar_fonts[1] = strdup("-*-times-medium-r-*-*-*-*-*-*-*-*-*-*"))
6004             == NULL)
6005                 err(1, "setup_globals: strdup");
6006         if ((bar_fonts[2] = strdup("-misc-fixed-medium-r-*-*-*-*-*-*-*-*-*-*"))
6007             == NULL)
6008                 err(1, "setup_globals: strdup");
6009         if ((spawn_term[0] = strdup("xterm")) == NULL)
6010                 err(1, "setup_globals: strdup");
6011         if ((clock_format = strdup("%a %b %d %R %Z %Y")) == NULL)
6012                 errx(1, "strdup");
6013 }
6014
6015 void
6016 workaround(void)
6017 {
6018         int                     i;
6019         Atom                    netwmcheck, netwmname, utf8_string;
6020         Window                  root, win;
6021
6022         /* work around sun jdk bugs, code from wmname */
6023         netwmcheck = XInternAtom(display, "_NET_SUPPORTING_WM_CHECK", False);
6024         netwmname = XInternAtom(display, "_NET_WM_NAME", False);
6025         utf8_string = XInternAtom(display, "UTF8_STRING", False);
6026         for (i = 0; i < ScreenCount(display); i++) {
6027                 root = screens[i].root;
6028                 win = XCreateSimpleWindow(display,root, 0, 0, 1, 1, 0,
6029                     screens[i].c[SWM_S_COLOR_UNFOCUS].color,
6030                     screens[i].c[SWM_S_COLOR_UNFOCUS].color);
6031
6032                 XChangeProperty(display, root, netwmcheck, XA_WINDOW, 32,
6033                     PropModeReplace, (unsigned char *)&win,1);
6034                 XChangeProperty(display, win, netwmcheck, XA_WINDOW, 32,
6035                     PropModeReplace, (unsigned char *)&win,1);
6036                 XChangeProperty(display, win, netwmname, utf8_string, 8,
6037                     PropModeReplace, (unsigned char*)"LG3D", strlen("LG3D"));
6038         }
6039 }
6040
6041 int
6042 main(int argc, char *argv[])
6043 {
6044         struct passwd           *pwd;
6045         struct swm_region       *r, *rr;
6046         struct ws_win           *winfocus = NULL;
6047         struct timeval          tv;
6048         union arg               a;
6049         char                    conf[PATH_MAX], *cfile = NULL;
6050         struct stat             sb;
6051         XEvent                  e;
6052         int                     xfd, i;
6053         fd_set                  rd;
6054         struct sigaction        sact;
6055
6056         start_argv = argv;
6057         fprintf(stderr, "Welcome to scrotwm V%s cvs tag: %s\n",
6058             SWM_VERSION, cvstag);
6059         if (!setlocale(LC_CTYPE, "") || !XSupportsLocale())
6060                 warnx("no locale support");
6061
6062         if (!(display = XOpenDisplay(0)))
6063                 errx(1, "can not open display");
6064
6065         if (active_wm())
6066                 errx(1, "other wm running");
6067
6068         /* handle some signals */
6069         bzero(&sact, sizeof(sact));
6070         sigemptyset(&sact.sa_mask);
6071         sact.sa_flags = 0;
6072         sact.sa_handler = sighdlr;
6073         sigaction(SIGINT, &sact, NULL);
6074         sigaction(SIGQUIT, &sact, NULL);
6075         sigaction(SIGTERM, &sact, NULL);
6076         sigaction(SIGHUP, &sact, NULL);
6077
6078         sact.sa_handler = sighdlr;
6079         sact.sa_flags = SA_NOCLDSTOP;
6080         sigaction(SIGCHLD, &sact, NULL);
6081
6082         astate = XInternAtom(display, "WM_STATE", False);
6083         aprot = XInternAtom(display, "WM_PROTOCOLS", False);
6084         adelete = XInternAtom(display, "WM_DELETE_WINDOW", False);
6085         takefocus = XInternAtom(display, "WM_TAKE_FOCUS", False);
6086         a_wmname = XInternAtom(display, "WM_NAME", False);
6087         a_utf8_string = XInternAtom(display, "UTF8_STRING", False);
6088         a_string = XInternAtom(display, "STRING", False);
6089         a_swm_iconic = XInternAtom(display, "_SWM_ICONIC", False);
6090
6091         /* look for local and global conf file */
6092         pwd = getpwuid(getuid());
6093         if (pwd == NULL)
6094                 errx(1, "invalid user %d", getuid());
6095
6096         setup_screens();
6097         setup_globals();
6098         setup_keys();
6099         setup_quirks();
6100         setup_spawn();
6101
6102         /* load config */
6103         snprintf(conf, sizeof conf, "%s/.%s", pwd->pw_dir, SWM_CONF_FILE);
6104         if (stat(conf, &sb) != -1) {
6105                 if (S_ISREG(sb.st_mode))
6106                         cfile = conf;
6107         } else {
6108                 /* try global conf file */
6109                 snprintf(conf, sizeof conf, "/etc/%s", SWM_CONF_FILE);
6110                 if (!stat(conf, &sb))
6111                         if (S_ISREG(sb.st_mode))
6112                                 cfile = conf;
6113         }
6114         if (cfile)
6115                 conf_load(cfile);
6116
6117         setup_ewmh();
6118         /* set some values to work around bad programs */
6119         workaround();
6120
6121         /* grab existing windows (before we build the bars) */
6122         grab_windows();
6123
6124         if (getenv("SWM_STARTED") == NULL)
6125                 setenv("SWM_STARTED", "YES", 1);
6126
6127         /* setup all bars */
6128         for (i = 0; i < ScreenCount(display); i++)
6129                 TAILQ_FOREACH(r, &screens[i].rl, entry) {
6130                         if (winfocus == NULL)
6131                                 winfocus = TAILQ_FIRST(&r->ws->winlist);
6132                         bar_setup(r);
6133                 }
6134
6135         unfocus_all();
6136
6137         grabkeys();
6138         stack();
6139         if (focus_mode == SWM_FOCUS_DEFAULT)
6140                 drain_enter_notify();
6141
6142         xfd = ConnectionNumber(display);
6143         while (running) {
6144                 while (XPending(display)) {
6145                         XNextEvent(display, &e);
6146                         if (running == 0)
6147                                 goto done;
6148                         if (e.type < LASTEvent) {
6149                                 dumpevent(&e);
6150                                 if (handler[e.type])
6151                                         handler[e.type](&e);
6152                                 else
6153                                         DNPRINTF(SWM_D_EVENT,
6154                                             "win: %lu unknown event: %d\n",
6155                                             e.xany.window, e.type);
6156                         } else {
6157                                 switch (e.type - xrandr_eventbase) {
6158                                 case RRScreenChangeNotify:
6159                                         screenchange(&e);
6160                                         break;
6161                                 default:
6162                                         DNPRINTF(SWM_D_EVENT,
6163                                             "win: %lu unknown xrandr event: "
6164                                             "%d\n", e.xany.window, e.type);
6165                                         break;
6166                                 }
6167                         }
6168                 }
6169
6170                 /* if we are being restarted go focus on first window */
6171                 if (winfocus) {
6172                         rr = winfocus->ws->r;
6173                         if (rr == NULL) {
6174                                 /* not a visible window */
6175                                 winfocus = NULL;
6176                                 continue;
6177                         }
6178                         /* move pointer to first screen if multi screen */
6179                         if (ScreenCount(display) > 1 || outputs > 1)
6180                                 XWarpPointer(display, None, rr->s[0].root,
6181                                     0, 0, 0, 0, rr->g.x,
6182                                     rr->g.y + (bar_enabled ? bar_height : 0));
6183
6184                         a.id = SWM_ARG_ID_FOCUSCUR;
6185                         focus(rr, &a);
6186                         winfocus = NULL;
6187                         continue;
6188                 }
6189
6190                 FD_ZERO(&rd);
6191                 FD_SET(xfd, &rd);
6192                 tv.tv_sec = 1;
6193                 tv.tv_usec = 0;
6194                 if (select(xfd + 1, &rd, NULL, NULL, &tv) == -1)
6195                         if (errno != EINTR)
6196                                 DNPRINTF(SWM_D_MISC, "select failed");
6197                 if (restart_wm == 1)
6198                         restart(NULL, NULL);
6199                 if (search_resp == 1)
6200                         search_do_resp();
6201                 if (running == 0)
6202                         goto done;
6203                 if (bar_alarm) {
6204                         bar_alarm = 0;
6205                         bar_update();
6206                 }
6207         }
6208 done:
6209         teardown_ewmh();
6210         bar_extra_stop();
6211         XFreeGC(display, bar_gc);
6212         XCloseDisplay(display);
6213
6214         return (0);
6215 }