]> code.delx.au - spectrwm/blob - spectrwm.c
Implement the "raise_focused" function
[spectrwm] / spectrwm.c
1 /*
2 * Copyright (c) 2009-2015 Marco Peereboom <marco@peereboom.us>
3 * Copyright (c) 2009-2011 Ryan McBride <mcbride@countersiege.com>
4 * Copyright (c) 2009 Darrin Chandler <dwchandler@stilyagin.com>
5 * Copyright (c) 2009 Pierre-Yves Ritschard <pyr@spootnik.org>
6 * Copyright (c) 2010 Tuukka Kataja <stuge@xor.fi>
7 * Copyright (c) 2011 Jason L. Wright <jason@thought.net>
8 * Copyright (c) 2011-2016 Reginald Kennedy <rk@rejii.com>
9 * Copyright (c) 2011-2012 Lawrence Teo <lteo@lteo.net>
10 * Copyright (c) 2011-2012 Tiago Cunha <tcunha@gmx.com>
11 * Copyright (c) 2012-2015 David Hill <dhill@mindcry.org>
12 * Copyright (c) 2014-2015 Yuri D'Elia <yuri.delia@eurac.edu>
13 *
14 * Permission to use, copy, modify, and distribute this software for any
15 * purpose with or without fee is hereby granted, provided that the above
16 * copyright notice and this permission notice appear in all copies.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
19 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
20 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
21 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
22 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
23 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
24 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
25 */
26
27 /* kernel includes */
28 #include <sys/types.h>
29 #include <sys/time.h>
30 #include <sys/stat.h>
31 #include <sys/wait.h>
32 #ifdef __OSX__
33 #include "queue.h"
34 #else
35 #include <sys/queue.h>
36 #endif
37 #include <sys/param.h>
38 #include <sys/select.h>
39 #if defined(__linux__)
40 #include "tree.h"
41 #elif defined(__OpenBSD__)
42 #include <sys/tree.h>
43 #elif defined(__FreeBSD__)
44 #include <sys/tree.h>
45 #else
46 #include "tree.h"
47 #endif
48
49 /* /usr/includes */
50 #include <ctype.h>
51 #include <err.h>
52 #include <errno.h>
53 #include <poll.h>
54 #include <fcntl.h>
55 #include <locale.h>
56 #include <paths.h>
57 #include <pwd.h>
58 #include <regex.h>
59 #include <signal.h>
60 #include <stdbool.h>
61 #include <stdio.h>
62 #include <stdlib.h>
63 #include <string.h>
64 #include <time.h>
65 #include <unistd.h>
66 #include <util.h>
67 #include <X11/cursorfont.h>
68 #include <X11/extensions/Xrandr.h>
69 #include <X11/Xcursor/Xcursor.h>
70 #include <X11/Xft/Xft.h>
71 #include <X11/Xlib-xcb.h>
72 #include <xcb/xcb_atom.h>
73 #include <xcb/xcb_aux.h>
74 #include <xcb/xcb_event.h>
75 #include <xcb/xcb_icccm.h>
76 #include <xcb/xcb_keysyms.h>
77 #include <xcb/xtest.h>
78 #include <xcb/randr.h>
79
80 /* local includes */
81 #include "version.h"
82 #ifdef __OSX__
83 #include <osx.h>
84 #endif
85
86 #ifdef SPECTRWM_BUILDSTR
87 static const char *buildstr = SPECTRWM_BUILDSTR;
88 #else
89 static const char *buildstr = SPECTRWM_VERSION;
90 #endif
91
92 #if !defined(__CYGWIN__) /* cygwin chokes on randr stuff */
93 # if RANDR_MAJOR < 1
94 # error RandR versions less than 1.0 are not supported
95 #endif
96
97 # if RANDR_MAJOR >= 1
98 # if RANDR_MINOR >= 2
99 # define SWM_XRR_HAS_CRTC
100 # endif
101 # endif
102 #endif /* __CYGWIN__ */
103
104 #ifndef XCB_ICCCM_NUM_WM_HINTS_ELEMENTS
105 #define XCB_ICCCM_SIZE_HINT_P_MIN_SIZE XCB_SIZE_HINT_P_MIN_SIZE
106 #define XCB_ICCCM_SIZE_HINT_P_MAX_SIZE XCB_SIZE_HINT_P_MAX_SIZE
107 #define XCB_ICCCM_SIZE_HINT_P_RESIZE_INC XCB_SIZE_HINT_P_RESIZE_INC
108 #define XCB_ICCCM_WM_HINT_INPUT XCB_WM_HINT_INPUT
109 #define XCB_ICCCM_WM_HINT_X_URGENCY XCB_WM_HINT_X_URGENCY
110 #define XCB_ICCCM_WM_STATE_ICONIC XCB_WM_STATE_ICONIC
111 #define XCB_ICCCM_WM_STATE_WITHDRAWN XCB_WM_STATE_WITHDRAWN
112 #define XCB_ICCCM_WM_STATE_NORMAL XCB_WM_STATE_NORMAL
113 #define xcb_icccm_get_text_property_reply_t xcb_get_text_property_reply_t
114 #define xcb_icccm_get_text_property_reply_wipe xcb_get_text_property_reply_wipe
115 #define xcb_icccm_get_wm_class xcb_get_wm_class
116 #define xcb_icccm_get_wm_class_reply xcb_get_wm_class_reply
117 #define xcb_icccm_get_wm_class_reply_t xcb_get_wm_class_reply_t
118 #define xcb_icccm_get_wm_class_reply_wipe xcb_get_wm_class_reply_wipe
119 #define xcb_icccm_get_wm_hints xcb_get_wm_hints
120 #define xcb_icccm_wm_hints_get_urgency xcb_wm_hints_get_urgency
121 #define xcb_icccm_get_wm_hints_reply xcb_get_wm_hints_reply
122 #define xcb_icccm_get_wm_name xcb_get_wm_name
123 #define xcb_icccm_get_wm_name_reply xcb_get_wm_name_reply
124 #define xcb_icccm_get_wm_normal_hints xcb_get_wm_normal_hints
125 #define xcb_icccm_get_wm_normal_hints_reply xcb_get_wm_normal_hints_reply
126 #define xcb_icccm_get_wm_protocols xcb_get_wm_protocols
127 #define xcb_icccm_get_wm_protocols_reply xcb_get_wm_protocols_reply
128 #define xcb_icccm_get_wm_protocols_reply_t xcb_get_wm_protocols_reply_t
129 #define xcb_icccm_get_wm_protocols_reply_wipe xcb_get_wm_protocols_reply_wipe
130 #define xcb_icccm_get_wm_transient_for xcb_get_wm_transient_for
131 #define xcb_icccm_get_wm_transient_for_reply xcb_get_wm_transient_for_reply
132 #define xcb_icccm_wm_hints_t xcb_wm_hints_t
133 #endif
134
135 /*#define SWM_DEBUG*/
136 #ifdef SWM_DEBUG
137 #define DPRINTF(x...) do { \
138 if (swm_debug) \
139 fprintf(stderr, x); \
140 } while (0)
141 #define DNPRINTF(n,x...) do { \
142 if (swm_debug & n) { \
143 fprintf(stderr, "%ld ", (long)(time(NULL) - time_started)); \
144 fprintf(stderr, x); \
145 } \
146 } while (0)
147 #define SWM_D_MISC 0x0001
148 #define SWM_D_EVENT 0x0002
149 #define SWM_D_WS 0x0004
150 #define SWM_D_FOCUS 0x0008
151 #define SWM_D_MOVE 0x0010
152 #define SWM_D_STACK 0x0020
153 #define SWM_D_MOUSE 0x0040
154 #define SWM_D_PROP 0x0080
155 #define SWM_D_CLASS 0x0100
156 #define SWM_D_KEY 0x0200
157 #define SWM_D_QUIRK 0x0400
158 #define SWM_D_SPAWN 0x0800
159 #define SWM_D_EVENTQ 0x1000
160 #define SWM_D_CONF 0x2000
161 #define SWM_D_BAR 0x4000
162 #define SWM_D_INIT 0x8000
163
164 uint32_t swm_debug = 0
165 | SWM_D_MISC
166 | SWM_D_EVENT
167 | SWM_D_WS
168 | SWM_D_FOCUS
169 | SWM_D_MOVE
170 | SWM_D_STACK
171 | SWM_D_MOUSE
172 | SWM_D_PROP
173 | SWM_D_CLASS
174 | SWM_D_KEY
175 | SWM_D_QUIRK
176 | SWM_D_SPAWN
177 | SWM_D_EVENTQ
178 | SWM_D_CONF
179 | SWM_D_BAR
180 | SWM_D_INIT
181 ;
182 #else
183 #define DPRINTF(x...)
184 #define DNPRINTF(n,x...)
185 #endif
186
187 #define SWM_EWMH_ACTION_COUNT_MAX (8)
188 #define EWMH_F_FULLSCREEN (0x001)
189 #define EWMH_F_ABOVE (0x002)
190 #define EWMH_F_HIDDEN (0x004)
191 #define EWMH_F_MAXIMIZED_VERT (0x008)
192 #define EWMH_F_MAXIMIZED_HORZ (0x010)
193 #define EWMH_F_SKIP_PAGER (0x020)
194 #define EWMH_F_SKIP_TASKBAR (0x040)
195 #define SWM_F_MANUAL (0x080)
196
197 #define EWMH_F_MAXIMIZED (EWMH_F_MAXIMIZED_VERT | EWMH_F_MAXIMIZED_HORZ)
198
199 /* convert 8-bit to 16-bit */
200 #define RGB_8_TO_16(col) (((col) << 8) + (col))
201
202 #define PIXEL_TO_XRENDERCOLOR(px, xrc) \
203 xrc.red = RGB_8_TO_16((px) >> 16 & 0xff); \
204 xrc.green = RGB_8_TO_16((px) >> 8 & 0xff); \
205 xrc.blue = RGB_8_TO_16((px) & 0xff); \
206 xrc.alpha = 0xffff;
207
208 #define LENGTH(x) (int)(sizeof (x) / sizeof (x)[0])
209 #define MODKEY XCB_MOD_MASK_1
210 #define ANYMOD XCB_MOD_MASK_ANY
211 #define CLEANMASK(mask) ((mask) & (XCB_KEY_BUT_MASK_SHIFT | \
212 XCB_KEY_BUT_MASK_CONTROL | XCB_KEY_BUT_MASK_MOD_1 | \
213 XCB_KEY_BUT_MASK_MOD_2 | XCB_KEY_BUT_MASK_MOD_3 | \
214 XCB_KEY_BUT_MASK_MOD_4 | XCB_KEY_BUT_MASK_MOD_5) & ~(numlockmask))
215 #define BUTTONMASK (XCB_EVENT_MASK_BUTTON_PRESS | \
216 XCB_EVENT_MASK_BUTTON_RELEASE)
217 #define MOUSEMASK (BUTTONMASK|XCB_EVENT_MASK_POINTER_MOTION)
218 #define SWM_PROPLEN (16)
219 #define SWM_FUNCNAME_LEN (32)
220 #define SWM_QUIRK_LEN (64)
221 #define X(r) ((r)->g.x)
222 #define Y(r) ((r)->g.y)
223 #define WIDTH(r) ((r)->g.w)
224 #define HEIGHT(r) ((r)->g.h)
225 #define MAX_X(r) ((r)->g.x + (r)->g.w)
226 #define MAX_Y(r) ((r)->g.y + (r)->g.h)
227 #define SH_MIN(w) ((w)->sh.flags & XCB_ICCCM_SIZE_HINT_P_MIN_SIZE)
228 #define SH_MIN_W(w) ((w)->sh.min_width)
229 #define SH_MIN_H(w) ((w)->sh.min_height)
230 #define SH_MAX(w) ((w)->sh.flags & XCB_ICCCM_SIZE_HINT_P_MAX_SIZE)
231 #define SH_MAX_W(w) ((w)->sh.max_width)
232 #define SH_MAX_H(w) ((w)->sh.max_height)
233 #define SH_INC(w) ((w)->sh.flags & XCB_ICCCM_SIZE_HINT_P_RESIZE_INC)
234 #define SH_INC_W(w) ((w)->sh.width_inc)
235 #define SH_INC_H(w) ((w)->sh.height_inc)
236 #define SWM_MAX_FONT_STEPS (3)
237 #define WINID(w) ((w) ? (w)->id : XCB_WINDOW_NONE)
238 #define ACCEPTS_FOCUS(w) (!((w)->hints.flags & XCB_ICCCM_WM_HINT_INPUT) \
239 || ((w)->hints.input))
240 #define WS_FOCUSED(ws) ((ws)->r && (ws)->r->s->r_focus == (ws)->r)
241 #define YESNO(x) ((x) ? "yes" : "no")
242 #define ICONIC(w) ((w)->ewmh_flags & EWMH_F_HIDDEN)
243 #define ABOVE(w) ((w)->ewmh_flags & EWMH_F_ABOVE)
244 #define FULLSCREEN(w) ((w)->ewmh_flags & EWMH_F_FULLSCREEN)
245 #define MAXIMIZED_VERT(w) ((w)->ewmh_flags & EWMH_F_MAXIMIZED_VERT)
246 #define MAXIMIZED_HORZ(w) ((w)->ewmh_flags & EWMH_F_MAXIMIZED_HORZ)
247 #define MAXIMIZED(w) (MAXIMIZED_VERT(w) || MAXIMIZED_HORZ(w))
248 #define MANUAL(w) ((w)->ewmh_flags & SWM_F_MANUAL)
249 #define TRANS(w) ((w)->transient != XCB_WINDOW_NONE)
250 #define FLOATING(w) (ABOVE(w) || TRANS(w) || FULLSCREEN(w) || \
251 MAXIMIZED(w))
252
253 /* Constrain Window flags */
254 #define SWM_CW_RESIZABLE (0x01)
255 #define SWM_CW_SOFTBOUNDARY (0x02)
256 #define SWM_CW_HARDBOUNDARY (0x04)
257 #define SWM_CW_RIGHT (0x10)
258 #define SWM_CW_LEFT (0x20)
259 #define SWM_CW_BOTTOM (0x40)
260 #define SWM_CW_TOP (0x80)
261 #define SWM_CW_ALLSIDES (0xf0)
262
263 #define SWM_FOCUS_DEFAULT (0)
264 #define SWM_FOCUS_FOLLOW (1)
265 #define SWM_FOCUS_MANUAL (2)
266
267 #define SWM_CK_ALL (0xf)
268 #define SWM_CK_FOCUS (0x1)
269 #define SWM_CK_POINTER (0x2)
270 #define SWM_CK_FALLBACK (0x4)
271 #define SWM_CK_REGION (0x8)
272
273 #define SWM_CONF_DEFAULT (0)
274 #define SWM_CONF_KEYMAPPING (1)
275
276 #ifndef SWM_LIB
277 #define SWM_LIB "/usr/local/lib/libswmhack.so"
278 #endif
279
280 char **start_argv;
281 xcb_atom_t a_state;
282 xcb_atom_t a_prot;
283 xcb_atom_t a_delete;
284 xcb_atom_t a_net_frame_extents;
285 xcb_atom_t a_net_wm_check;
286 xcb_atom_t a_net_supported;
287 xcb_atom_t a_takefocus;
288 xcb_atom_t a_utf8_string;
289 xcb_atom_t a_swm_ws;
290 volatile sig_atomic_t running = 1;
291 volatile sig_atomic_t restart_wm = 0;
292 xcb_timestamp_t last_event_time = 0;
293 int outputs = 0;
294 bool randr_support;
295 int randr_eventbase;
296 unsigned int numlockmask = 0;
297
298 Display *display;
299 xcb_connection_t *conn;
300 xcb_key_symbols_t *syms;
301
302 int boundary_width = 50;
303 bool cycle_empty = false;
304 bool cycle_visible = false;
305 int term_width = 0;
306 int font_adjusted = 0;
307 uint16_t mod_key = MODKEY;
308 bool warp_focus = false;
309 bool warp_pointer = false;
310 bool workspace_clamp = false;
311
312 /* dmenu search */
313 struct swm_region *search_r;
314 int select_list_pipe[2];
315 int select_resp_pipe[2];
316 pid_t searchpid;
317 volatile sig_atomic_t search_resp;
318 int search_resp_action;
319
320 struct search_window {
321 TAILQ_ENTRY(search_window) entry;
322 int idx;
323 struct ws_win *win;
324 xcb_gcontext_t gc;
325 xcb_window_t indicator;
326 };
327 TAILQ_HEAD(search_winlist, search_window);
328 struct search_winlist search_wl;
329
330 /* search actions */
331 enum {
332 SWM_SEARCH_NONE,
333 SWM_SEARCH_UNICONIFY,
334 SWM_SEARCH_NAME_WORKSPACE,
335 SWM_SEARCH_SEARCH_WORKSPACE,
336 SWM_SEARCH_SEARCH_WINDOW
337 };
338
339 #define SWM_STACK_TOP (0)
340 #define SWM_STACK_BOTTOM (1)
341 #define SWM_STACK_ABOVE (2)
342 #define SWM_STACK_BELOW (3)
343
344 /* dialog windows */
345 double dialog_ratio = 0.6;
346 /* status bar */
347 #define SWM_BAR_MAX (256)
348 #define SWM_BAR_JUSTIFY_LEFT (0)
349 #define SWM_BAR_JUSTIFY_CENTER (1)
350 #define SWM_BAR_JUSTIFY_RIGHT (2)
351 #define SWM_BAR_OFFSET (4)
352 #define SWM_BAR_FONTS "-*-terminus-medium-*-*-*-12-*-*-*-*-*-*-*," \
353 "-*-profont-*-*-*-*-12-*-*-*-*-*-*-*," \
354 "-*-times-medium-r-*-*-12-*-*-*-*-*-*-*," \
355 "-misc-fixed-medium-r-*-*-12-*-*-*-*-*-*-*," \
356 "-*-*-*-r-*-*-*-*-*-*-*-*-*-*"
357
358 #ifdef X_HAVE_UTF8_STRING
359 #define DRAWSTRING(x...) Xutf8DrawString(x)
360 #else
361 #define DRAWSTRING(x...) XmbDrawString(x)
362 #endif
363
364 char *bar_argv[] = { NULL, NULL };
365 int bar_pipe[2];
366 char bar_ext[SWM_BAR_MAX];
367 char bar_ext_buf[SWM_BAR_MAX];
368 char bar_vertext[SWM_BAR_MAX];
369 bool bar_version = false;
370 bool bar_enabled = true;
371 int bar_border_width = 1;
372 bool bar_at_bottom = false;
373 bool bar_extra = false;
374 int bar_height = 0;
375 int bar_justify = SWM_BAR_JUSTIFY_LEFT;
376 char *bar_format = NULL;
377 bool stack_enabled = true;
378 bool clock_enabled = true;
379 bool iconic_enabled = false;
380 bool maximize_hide_bar = false;
381 bool urgent_enabled = false;
382 bool urgent_collapse = false;
383 char *clock_format = NULL;
384 bool window_class_enabled = false;
385 bool window_instance_enabled = false;
386 bool window_name_enabled = false;
387 int focus_mode = SWM_FOCUS_DEFAULT;
388 int focus_close = SWM_STACK_BELOW;
389 bool focus_close_wrap = true;
390 int focus_default = SWM_STACK_TOP;
391 int spawn_position = SWM_STACK_TOP;
392 bool disable_border = false;
393 int border_width = 1;
394 int region_padding = 0;
395 int tile_gap = 0;
396 bool java_workaround = true;
397 bool verbose_layout = false;
398 #ifdef SWM_DEBUG
399 bool debug_enabled;
400 time_t time_started;
401 #endif
402 pid_t bar_pid;
403 XFontSet bar_fs = NULL;
404 XFontSetExtents *bar_fs_extents;
405 XftFont *bar_font = NULL;
406 bool bar_font_legacy = true;
407 char *bar_fonts = NULL;
408 XftColor bar_font_color;
409 XftColor search_font_color;
410 char *startup_exception = NULL;
411 unsigned int nr_exceptions = 0;
412
413 /* layout manager data */
414 struct swm_geometry {
415 int x;
416 int y;
417 int w;
418 int h;
419 };
420
421 struct swm_screen;
422 struct workspace;
423
424 struct swm_bar {
425 xcb_window_t id;
426 xcb_pixmap_t buffer;
427 struct swm_geometry g;
428 struct swm_region *r; /* Associated region. */
429 };
430
431 /* virtual "screens" */
432 struct swm_region {
433 TAILQ_ENTRY(swm_region) entry;
434 xcb_window_t id;
435 struct swm_geometry g;
436 struct workspace *ws; /* current workspace on this region */
437 struct workspace *ws_prior; /* prior workspace on this region */
438 struct swm_screen *s; /* screen idx */
439 struct swm_bar *bar;
440 };
441 TAILQ_HEAD(swm_region_list, swm_region);
442
443 enum {
444 SWM_WIN_STATE_REPARENTING,
445 SWM_WIN_STATE_REPARENTED,
446 SWM_WIN_STATE_UNPARENTING,
447 SWM_WIN_STATE_UNPARENTED,
448 };
449
450 struct ws_win {
451 TAILQ_ENTRY(ws_win) entry;
452 TAILQ_ENTRY(ws_win) stack_entry;
453 xcb_window_t id;
454 xcb_window_t frame;
455 xcb_window_t transient;
456 struct ws_win *focus_child; /* focus on child transient */
457 struct swm_geometry g; /* current geometry */
458 struct swm_geometry g_prev; /* prev configured geometry */
459 struct swm_geometry g_float; /* region coordinates */
460 bool g_floatvalid; /* g_float geometry validity */
461 bool mapped;
462 uint8_t state;
463 bool bordered;
464 uint32_t ewmh_flags;
465 int font_size_boundary[SWM_MAX_FONT_STEPS];
466 int font_steps;
467 int last_inc;
468 bool can_delete;
469 bool take_focus;
470 bool java;
471 uint32_t quirks;
472 struct workspace *ws; /* always valid */
473 struct swm_screen *s; /* always valid, never changes */
474 xcb_size_hints_t sh;
475 xcb_icccm_get_wm_class_reply_t ch;
476 xcb_icccm_wm_hints_t hints;
477 #ifdef SWM_DEBUG
478 xcb_window_t debug;
479 #endif
480 };
481 TAILQ_HEAD(ws_win_list, ws_win);
482 TAILQ_HEAD(ws_win_stack, ws_win);
483
484 /* pid goo */
485 struct pid_e {
486 TAILQ_ENTRY(pid_e) entry;
487 pid_t pid;
488 int ws;
489 };
490 TAILQ_HEAD(pid_list, pid_e);
491 struct pid_list pidlist = TAILQ_HEAD_INITIALIZER(pidlist);
492
493 /* layout handlers */
494 void stack(struct swm_region *);
495 void vertical_config(struct workspace *, int);
496 void vertical_stack(struct workspace *, struct swm_geometry *);
497 void horizontal_config(struct workspace *, int);
498 void horizontal_stack(struct workspace *, struct swm_geometry *);
499 void max_stack(struct workspace *, struct swm_geometry *);
500 void plain_stacker(struct workspace *);
501 void fancy_stacker(struct workspace *);
502
503 struct layout {
504 void (*l_stack)(struct workspace *, struct swm_geometry *);
505 void (*l_config)(struct workspace *, int);
506 uint32_t flags;
507 #define SWM_L_FOCUSPREV (1<<0)
508 #define SWM_L_MAPONFOCUS (1<<1)
509 void (*l_string)(struct workspace *);
510 } layouts[] = {
511 /* stack, configure */
512 { vertical_stack, vertical_config, 0, plain_stacker },
513 { horizontal_stack, horizontal_config, 0, plain_stacker },
514 { max_stack, NULL,
515 SWM_L_MAPONFOCUS | SWM_L_FOCUSPREV, plain_stacker },
516 { NULL, NULL, 0, NULL },
517 };
518
519 /* position of max_stack mode in the layouts array, index into layouts! */
520 #define SWM_V_STACK (0)
521 #define SWM_H_STACK (1)
522 #define SWM_MAX_STACK (2)
523
524 #define SWM_H_SLICE (32)
525 #define SWM_V_SLICE (32)
526
527 /* define work spaces */
528 struct workspace {
529 int idx; /* workspace index */
530 char *name; /* workspace name */
531 bool always_raise; /* raise windows on focus */
532 bool bar_enabled; /* bar visibility */
533 struct layout *cur_layout; /* current layout handlers */
534 struct ws_win *focus; /* may be NULL */
535 struct ws_win *focus_prev; /* may be NULL */
536 struct ws_win *focus_pending; /* may be NULL */
537 struct ws_win *raised; /* may be NULL */
538 struct swm_region *r; /* may be NULL */
539 struct swm_region *old_r; /* may be NULL */
540 struct ws_win_list winlist; /* list of windows in ws */
541 struct ws_win_list unmanagedlist; /* list of dead windows in ws */
542 struct ws_win_stack stack; /* stacking order */
543 int state; /* mapping state */
544 char stacker[10]; /* display stacker and layout */
545
546 /* stacker state */
547 struct {
548 int horizontal_msize;
549 int horizontal_mwin;
550 int horizontal_stacks;
551 bool horizontal_flip;
552 int vertical_msize;
553 int vertical_mwin;
554 int vertical_stacks;
555 bool vertical_flip;
556 } l_state;
557 };
558
559 enum {
560 SWM_WS_STATE_HIDDEN,
561 SWM_WS_STATE_MAPPING,
562 SWM_WS_STATE_MAPPED,
563 };
564
565 enum {
566 SWM_S_COLOR_BAR,
567 SWM_S_COLOR_BAR_BORDER,
568 SWM_S_COLOR_BAR_BORDER_UNFOCUS,
569 SWM_S_COLOR_BAR_FONT,
570 SWM_S_COLOR_FOCUS,
571 SWM_S_COLOR_FOCUS_MAXIMIZED,
572 SWM_S_COLOR_UNFOCUS,
573 SWM_S_COLOR_UNFOCUS_MAXIMIZED,
574 SWM_S_COLOR_MAX
575 };
576
577 /* physical screen mapping */
578 #define SWM_WS_MAX (22) /* hard limit */
579 int workspace_limit = 10; /* soft limit */
580
581 struct swm_screen {
582 int idx; /* screen index */
583 struct swm_region_list rl; /* list of regions on this screen */
584 struct swm_region_list orl; /* list of old regions */
585 xcb_window_t root;
586 struct workspace ws[SWM_WS_MAX];
587 struct swm_region *r_focus;
588
589 /* colors */
590 struct {
591 uint32_t pixel;
592 char *name;
593 int manual;
594 } c[SWM_S_COLOR_MAX];
595
596 xcb_gcontext_t bar_gc;
597 GC bar_gc_legacy;
598 };
599 struct swm_screen *screens;
600
601 /* args to functions */
602 union arg {
603 int id;
604 #define SWM_ARG_ID_FOCUSNEXT (0)
605 #define SWM_ARG_ID_FOCUSPREV (1)
606 #define SWM_ARG_ID_FOCUSMAIN (2)
607 #define SWM_ARG_ID_FOCUSURGENT (3)
608 #define SWM_ARG_ID_SWAPNEXT (10)
609 #define SWM_ARG_ID_SWAPPREV (11)
610 #define SWM_ARG_ID_SWAPMAIN (12)
611 #define SWM_ARG_ID_MOVELAST (13)
612 #define SWM_ARG_ID_MASTERSHRINK (20)
613 #define SWM_ARG_ID_MASTERGROW (21)
614 #define SWM_ARG_ID_MASTERADD (22)
615 #define SWM_ARG_ID_MASTERDEL (23)
616 #define SWM_ARG_ID_FLIPLAYOUT (24)
617 #define SWM_ARG_ID_STACKRESET (30)
618 #define SWM_ARG_ID_STACKINIT (31)
619 #define SWM_ARG_ID_STACKBALANCE (32)
620 #define SWM_ARG_ID_CYCLEWS_UP (40)
621 #define SWM_ARG_ID_CYCLEWS_DOWN (41)
622 #define SWM_ARG_ID_CYCLERG_UP (42)
623 #define SWM_ARG_ID_CYCLERG_DOWN (43)
624 #define SWM_ARG_ID_CYCLEWS_UP_ALL (44)
625 #define SWM_ARG_ID_CYCLEWS_DOWN_ALL (45)
626 #define SWM_ARG_ID_CYCLEWS_MOVE_UP (46)
627 #define SWM_ARG_ID_CYCLEWS_MOVE_DOWN (47)
628 #define SWM_ARG_ID_STACKINC (50)
629 #define SWM_ARG_ID_STACKDEC (51)
630 #define SWM_ARG_ID_DONTCENTER (70)
631 #define SWM_ARG_ID_CENTER (71)
632 #define SWM_ARG_ID_KILLWINDOW (80)
633 #define SWM_ARG_ID_DELETEWINDOW (81)
634 #define SWM_ARG_ID_WIDTHGROW (90)
635 #define SWM_ARG_ID_WIDTHSHRINK (91)
636 #define SWM_ARG_ID_HEIGHTGROW (92)
637 #define SWM_ARG_ID_HEIGHTSHRINK (93)
638 #define SWM_ARG_ID_MOVEUP (100)
639 #define SWM_ARG_ID_MOVEDOWN (101)
640 #define SWM_ARG_ID_MOVELEFT (102)
641 #define SWM_ARG_ID_MOVERIGHT (103)
642 #define SWM_ARG_ID_BAR_TOGGLE (110)
643 #define SWM_ARG_ID_BAR_TOGGLE_WS (111)
644 #define SWM_ARG_ID_CYCLERG_MOVE_UP (112)
645 #define SWM_ARG_ID_CYCLERG_MOVE_DOWN (113)
646 char **argv;
647 };
648
649 /* quirks */
650 struct quirk {
651 TAILQ_ENTRY(quirk) entry;
652 char *class; /* WM_CLASS:class */
653 char *instance; /* WM_CLASS:instance */
654 char *name; /* WM_NAME */
655 regex_t regex_class;
656 regex_t regex_instance;
657 regex_t regex_name;
658 uint32_t quirk;
659 int ws; /* Initial workspace. */
660 #define SWM_Q_FLOAT (1<<0) /* Float this window. */
661 #define SWM_Q_TRANSSZ (1<<1) /* Transient window size too small. */
662 #define SWM_Q_ANYWHERE (1<<2) /* Don't position this window */
663 #define SWM_Q_XTERM_FONTADJ (1<<3) /* Adjust xterm fonts when resizing. */
664 #define SWM_Q_FULLSCREEN (1<<4) /* Remove border when fullscreen. */
665 #define SWM_Q_FOCUSPREV (1<<5) /* Focus on caller. */
666 #define SWM_Q_NOFOCUSONMAP (1<<6) /* Don't focus on window when mapped. */
667 #define SWM_Q_FOCUSONMAP_SINGLE (1<<7) /* Only focus if single win of type. */
668 #define SWM_Q_OBEYAPPFOCUSREQ (1<<8) /* Focus when applications ask. */
669 #define SWM_Q_IGNOREPID (1<<9) /* Ignore PID when determining ws. */
670 #define SWM_Q_IGNORESPAWNWS (1<<10) /* Ignore _SWM_WS when managing win. */
671 #define SWM_Q_NOFOCUSCYCLE (1<<11) /* Remove from normal focus cycle. */
672 #define SWM_Q_MINIMALBORDER (1<<12) /* No border when floating/unfocused. */
673 };
674 TAILQ_HEAD(quirk_list, quirk);
675 struct quirk_list quirks = TAILQ_HEAD_INITIALIZER(quirks);
676
677 /*
678 * Supported EWMH hints should be added to
679 * both the enum and the ewmh array
680 */
681 enum {
682 _NET_ACTIVE_WINDOW,
683 _NET_CLIENT_LIST,
684 _NET_CLOSE_WINDOW,
685 _NET_CURRENT_DESKTOP,
686 _NET_DESKTOP_GEOMETRY,
687 _NET_DESKTOP_NAMES,
688 _NET_DESKTOP_VIEWPORT,
689 _NET_MOVERESIZE_WINDOW,
690 _NET_NUMBER_OF_DESKTOPS,
691 _NET_REQUEST_FRAME_EXTENTS,
692 _NET_RESTACK_WINDOW,
693 _NET_WM_ACTION_ABOVE,
694 _NET_WM_ACTION_CLOSE,
695 _NET_WM_ACTION_FULLSCREEN,
696 _NET_WM_ACTION_MOVE,
697 _NET_WM_ACTION_RESIZE,
698 _NET_WM_ALLOWED_ACTIONS,
699 _NET_WM_DESKTOP,
700 _NET_WM_FULL_PLACEMENT,
701 _NET_WM_NAME,
702 _NET_WM_STATE,
703 _NET_WM_STATE_ABOVE,
704 _NET_WM_STATE_FULLSCREEN,
705 _NET_WM_STATE_HIDDEN,
706 _NET_WM_STATE_MAXIMIZED_VERT,
707 _NET_WM_STATE_MAXIMIZED_HORZ,
708 _NET_WM_STATE_SKIP_PAGER,
709 _NET_WM_STATE_SKIP_TASKBAR,
710 _NET_WM_WINDOW_TYPE,
711 _NET_WM_WINDOW_TYPE_DIALOG,
712 _NET_WM_WINDOW_TYPE_DOCK,
713 _NET_WM_WINDOW_TYPE_NORMAL,
714 _NET_WM_WINDOW_TYPE_SPLASH,
715 _NET_WM_WINDOW_TYPE_TOOLBAR,
716 _NET_WM_WINDOW_TYPE_UTILITY,
717 _SWM_WM_STATE_MANUAL,
718 SWM_EWMH_HINT_MAX
719 };
720
721 struct ewmh_hint {
722 char *name;
723 xcb_atom_t atom;
724 } ewmh[SWM_EWMH_HINT_MAX] = {
725 /* must be in same order as in the enum */
726 {"_NET_ACTIVE_WINDOW", XCB_ATOM_NONE},
727 {"_NET_CLIENT_LIST", XCB_ATOM_NONE},
728 {"_NET_CLOSE_WINDOW", XCB_ATOM_NONE},
729 {"_NET_CURRENT_DESKTOP", XCB_ATOM_NONE},
730 {"_NET_DESKTOP_GEOMETRY", XCB_ATOM_NONE},
731 {"_NET_DESKTOP_NAMES", XCB_ATOM_NONE},
732 {"_NET_DESKTOP_VIEWPORT", XCB_ATOM_NONE},
733 {"_NET_MOVERESIZE_WINDOW", XCB_ATOM_NONE},
734 {"_NET_NUMBER_OF_DESKTOPS", XCB_ATOM_NONE},
735 {"_NET_REQUEST_FRAME_EXTENTS", XCB_ATOM_NONE},
736 {"_NET_RESTACK_WINDOW", XCB_ATOM_NONE},
737 {"_NET_WM_ACTION_ABOVE", XCB_ATOM_NONE},
738 {"_NET_WM_ACTION_CLOSE", XCB_ATOM_NONE},
739 {"_NET_WM_ACTION_FULLSCREEN", XCB_ATOM_NONE},
740 {"_NET_WM_ACTION_MOVE", XCB_ATOM_NONE},
741 {"_NET_WM_ACTION_RESIZE", XCB_ATOM_NONE},
742 {"_NET_WM_ALLOWED_ACTIONS", XCB_ATOM_NONE},
743 {"_NET_WM_DESKTOP", XCB_ATOM_NONE},
744 {"_NET_WM_FULL_PLACEMENT", XCB_ATOM_NONE},
745 {"_NET_WM_NAME", XCB_ATOM_NONE},
746 {"_NET_WM_STATE", XCB_ATOM_NONE},
747 {"_NET_WM_STATE_ABOVE", XCB_ATOM_NONE},
748 {"_NET_WM_STATE_FULLSCREEN", XCB_ATOM_NONE},
749 {"_NET_WM_STATE_HIDDEN", XCB_ATOM_NONE},
750 {"_NET_WM_STATE_MAXIMIZED_VERT", XCB_ATOM_NONE},
751 {"_NET_WM_STATE_MAXIMIZED_HORZ", XCB_ATOM_NONE},
752 {"_NET_WM_STATE_SKIP_PAGER", XCB_ATOM_NONE},
753 {"_NET_WM_STATE_SKIP_TASKBAR", XCB_ATOM_NONE},
754 {"_NET_WM_WINDOW_TYPE", XCB_ATOM_NONE},
755 {"_NET_WM_WINDOW_TYPE_DIALOG", XCB_ATOM_NONE},
756 {"_NET_WM_WINDOW_TYPE_DOCK", XCB_ATOM_NONE},
757 {"_NET_WM_WINDOW_TYPE_NORMAL", XCB_ATOM_NONE},
758 {"_NET_WM_WINDOW_TYPE_SPLASH", XCB_ATOM_NONE},
759 {"_NET_WM_WINDOW_TYPE_TOOLBAR", XCB_ATOM_NONE},
760 {"_NET_WM_WINDOW_TYPE_UTILITY", XCB_ATOM_NONE},
761 {"_SWM_WM_STATE_MANUAL", XCB_ATOM_NONE},
762 };
763
764 /* EWMH source type */
765 enum {
766 EWMH_SOURCE_TYPE_NONE = 0,
767 EWMH_SOURCE_TYPE_NORMAL = 1,
768 EWMH_SOURCE_TYPE_OTHER = 2,
769 };
770
771 /* Cursors */
772 enum {
773 XC_FLEUR,
774 XC_LEFT_PTR,
775 XC_BOTTOM_LEFT_CORNER,
776 XC_BOTTOM_RIGHT_CORNER,
777 XC_SIZING,
778 XC_TOP_LEFT_CORNER,
779 XC_TOP_RIGHT_CORNER,
780 XC_MAX
781 };
782
783 struct cursors {
784 char *name; /* Name used by Xcursor .*/
785 uint8_t cf_char; /* cursorfont index. */
786 xcb_cursor_t cid;
787 } cursors[XC_MAX] = {
788 {"fleur", XC_fleur, XCB_CURSOR_NONE},
789 {"left_ptr", XC_left_ptr, XCB_CURSOR_NONE},
790 {"bottom_left_corner", XC_bottom_left_corner, XCB_CURSOR_NONE},
791 {"bottom_right_corner", XC_bottom_right_corner, XCB_CURSOR_NONE},
792 {"sizing", XC_sizing, XCB_CURSOR_NONE},
793 {"top_left_corner", XC_top_left_corner, XCB_CURSOR_NONE},
794 {"top_right_corner", XC_top_right_corner, XCB_CURSOR_NONE},
795 };
796
797 #define SWM_SPAWN_OPTIONAL 0x1
798
799 /* spawn */
800 struct spawn_prog {
801 TAILQ_ENTRY(spawn_prog) entry;
802 char *name;
803 int argc;
804 char **argv;
805 int flags;
806 };
807 TAILQ_HEAD(spawn_list, spawn_prog);
808 struct spawn_list spawns = TAILQ_HEAD_INITIALIZER(spawns);
809
810 enum {
811 FN_F_NOREPLAY = 0x1,
812 };
813
814 /* User callable function IDs. */
815 enum actionid {
816 FN_BAR_TOGGLE,
817 FN_BAR_TOGGLE_WS,
818 FN_BUTTON2,
819 FN_CYCLE_LAYOUT,
820 FN_FLIP_LAYOUT,
821 FN_FLOAT_TOGGLE,
822 FN_FOCUS,
823 FN_FOCUS_MAIN,
824 FN_FOCUS_NEXT,
825 FN_FOCUS_PREV,
826 FN_FOCUS_URGENT,
827 FN_MAXIMIZE_TOGGLE,
828 FN_HEIGHT_GROW,
829 FN_HEIGHT_SHRINK,
830 FN_ICONIFY,
831 FN_MASTER_SHRINK,
832 FN_MASTER_GROW,
833 FN_MASTER_ADD,
834 FN_MASTER_DEL,
835 FN_MOVE,
836 FN_MOVE_DOWN,
837 FN_MOVE_LEFT,
838 FN_MOVE_RIGHT,
839 FN_MOVE_UP,
840 FN_MVRG_1,
841 FN_MVRG_2,
842 FN_MVRG_3,
843 FN_MVRG_4,
844 FN_MVRG_5,
845 FN_MVRG_6,
846 FN_MVRG_7,
847 FN_MVRG_8,
848 FN_MVRG_9,
849 FN_MVWS_1,
850 FN_MVWS_2,
851 FN_MVWS_3,
852 FN_MVWS_4,
853 FN_MVWS_5,
854 FN_MVWS_6,
855 FN_MVWS_7,
856 FN_MVWS_8,
857 FN_MVWS_9,
858 FN_MVWS_10,
859 FN_MVWS_11,
860 FN_MVWS_12,
861 FN_MVWS_13,
862 FN_MVWS_14,
863 FN_MVWS_15,
864 FN_MVWS_16,
865 FN_MVWS_17,
866 FN_MVWS_18,
867 FN_MVWS_19,
868 FN_MVWS_20,
869 FN_MVWS_21,
870 FN_MVWS_22,
871 FN_NAME_WORKSPACE,
872 FN_QUIT,
873 FN_RAISE_FOCUSED,
874 FN_RAISE_TOGGLE,
875 FN_RESIZE,
876 FN_RESIZE_CENTERED,
877 FN_RESTART,
878 FN_RG_1,
879 FN_RG_2,
880 FN_RG_3,
881 FN_RG_4,
882 FN_RG_5,
883 FN_RG_6,
884 FN_RG_7,
885 FN_RG_8,
886 FN_RG_9,
887 FN_RG_MOVE_NEXT,
888 FN_RG_MOVE_PREV,
889 FN_RG_NEXT,
890 FN_RG_PREV,
891 FN_SCREEN_NEXT,
892 FN_SCREEN_PREV,
893 FN_SEARCH_WIN,
894 FN_SEARCH_WORKSPACE,
895 FN_SPAWN_CUSTOM,
896 FN_STACK_BALANCE,
897 FN_STACK_INC,
898 FN_STACK_DEC,
899 FN_STACK_RESET,
900 FN_SWAP_MAIN,
901 FN_SWAP_NEXT,
902 FN_SWAP_PREV,
903 FN_UNICONIFY,
904 FN_VERSION,
905 FN_WIDTH_GROW,
906 FN_WIDTH_SHRINK,
907 FN_WIND_DEL,
908 FN_WIND_KILL,
909 FN_WS_1,
910 FN_WS_2,
911 FN_WS_3,
912 FN_WS_4,
913 FN_WS_5,
914 FN_WS_6,
915 FN_WS_7,
916 FN_WS_8,
917 FN_WS_9,
918 FN_WS_10,
919 FN_WS_11,
920 FN_WS_12,
921 FN_WS_13,
922 FN_WS_14,
923 FN_WS_15,
924 FN_WS_16,
925 FN_WS_17,
926 FN_WS_18,
927 FN_WS_19,
928 FN_WS_20,
929 FN_WS_21,
930 FN_WS_22,
931 FN_WS_NEXT,
932 FN_WS_NEXT_ALL,
933 FN_WS_NEXT_MOVE,
934 FN_WS_PREV,
935 FN_WS_PREV_ALL,
936 FN_WS_PREV_MOVE,
937 FN_WS_PRIOR,
938 /* SWM_DEBUG actions MUST be here: */
939 FN_DEBUG_TOGGLE,
940 FN_DUMPWINS,
941 /* ALWAYS last: */
942 FN_INVALID
943 };
944
945 enum binding_type {
946 KEYBIND,
947 BTNBIND
948 };
949
950 enum {
951 BINDING_F_REPLAY = 0x1,
952 };
953
954 struct binding {
955 RB_ENTRY(binding) entry;
956 uint16_t mod; /* Modifier Mask. */
957 enum binding_type type; /* Key or Button. */
958 uint32_t value; /* KeySym or Button Index. */
959 enum actionid action; /* Action Identifier. */
960 uint32_t flags;
961 char *spawn_name;
962 };
963 RB_HEAD(binding_tree, binding);
964
965 /* function prototypes */
966 void adjust_font(struct ws_win *);
967 char *argsep(char **);
968 void bar_cleanup(struct swm_region *);
969 void bar_extra_setup(void);
970 void bar_extra_stop(void);
971 int bar_extra_update(void);
972 void bar_fmt(const char *, char *, struct swm_region *, size_t);
973 void bar_fmt_expand(char *, size_t);
974 void bar_draw(struct swm_bar *);
975 void bar_print(struct swm_region *, const char *);
976 void bar_print_legacy(struct swm_region *, const char *);
977 void bar_replace(char *, char *, struct swm_region *, size_t);
978 void bar_replace_pad(char *, int *, size_t);
979 char *bar_replace_seq(char *, char *, struct swm_region *, size_t *, size_t);
980 void bar_setup(struct swm_region *);
981 void bar_toggle(struct binding *, struct swm_region *, union arg *);
982 void bar_urgent(char *, size_t);
983 void bar_window_class(char *, size_t, struct swm_region *);
984 void bar_window_class_instance(char *, size_t, struct swm_region *);
985 void bar_window_float(char *, size_t, struct swm_region *);
986 void bar_window_instance(char *, size_t, struct swm_region *);
987 void bar_window_name(char *, size_t, struct swm_region *);
988 void bar_window_state(char *, size_t, struct swm_region *);
989 void bar_workspace_name(char *, size_t, struct swm_region *);
990 int binding_cmp(struct binding *, struct binding *);
991 void binding_insert(uint16_t, enum binding_type, uint32_t, enum actionid,
992 uint32_t, const char *);
993 struct binding *binding_lookup(uint16_t, enum binding_type, uint32_t);
994 void binding_remove(struct binding *);
995 void buttonpress(xcb_button_press_event_t *);
996 void buttonrelease(xcb_button_release_event_t *);
997 void center_pointer(struct swm_region *);
998 void check_conn(void);
999 void clear_bindings(void);
1000 void clear_keybindings(void);
1001 int clear_maximized(struct workspace *);
1002 void clear_quirks(void);
1003 void clear_spawns(void);
1004 void clientmessage(xcb_client_message_event_t *);
1005 void client_msg(struct ws_win *, xcb_atom_t, xcb_timestamp_t);
1006 int conf_load(const char *, int);
1007 void configurenotify(xcb_configure_notify_event_t *);
1008 void configurerequest(xcb_configure_request_event_t *);
1009 void config_win(struct ws_win *, xcb_configure_request_event_t *);
1010 void constrain_window(struct ws_win *, struct swm_geometry *, int *);
1011 int count_win(struct workspace *, bool);
1012 void cursors_cleanup(void);
1013 void cursors_load(void);
1014 void custom_region(const char *);
1015 void cycle_layout(struct binding *, struct swm_region *, union arg *);
1016 void cyclerg(struct binding *, struct swm_region *, union arg *);
1017 void cyclews(struct binding *, struct swm_region *, union arg *);
1018 #ifdef SWM_DEBUG
1019 void debug_refresh(struct ws_win *);
1020 #endif
1021 void debug_toggle(struct binding *, struct swm_region *, union arg *);
1022 void destroynotify(xcb_destroy_notify_event_t *);
1023 void dumpwins(struct binding *, struct swm_region *, union arg *);
1024 int enable_wm(void);
1025 void enternotify(xcb_enter_notify_event_t *);
1026 void event_drain(uint8_t);
1027 void event_error(xcb_generic_error_t *);
1028 void event_handle(xcb_generic_event_t *);
1029 void ewmh_apply_flags(struct ws_win *, uint32_t);
1030 void ewmh_autoquirk(struct ws_win *);
1031 void ewmh_get_desktop_names(void);
1032 void ewmh_get_wm_state(struct ws_win *);
1033 void ewmh_update_actions(struct ws_win *);
1034 void ewmh_update_client_list(void);
1035 void ewmh_update_current_desktop(void);
1036 void ewmh_update_desktop_names(void);
1037 void ewmh_update_desktops(void);
1038 void ewmh_change_wm_state(struct ws_win *, xcb_atom_t, long);
1039 void ewmh_update_wm_state(struct ws_win *);
1040 char *expand_tilde(const char *);
1041 void expose(xcb_expose_event_t *);
1042 void fake_keypress(struct ws_win *, xcb_keysym_t, uint16_t);
1043 struct swm_bar *find_bar(xcb_window_t);
1044 struct ws_win *find_frame_window(xcb_window_t);
1045 struct pid_e *find_pid(pid_t);
1046 struct swm_region *find_region(xcb_window_t);
1047 struct ws_win *find_unmanaged_window(xcb_window_t);
1048 struct ws_win *find_window(xcb_window_t);
1049 void floating_toggle(struct binding *, struct swm_region *, union arg *);
1050 void focus(struct binding *, struct swm_region *, union arg *);
1051 void focus_flush(void);
1052 void focus_pointer(struct binding *, struct swm_region *, union arg *);
1053 void focus_region(struct swm_region *);
1054 void focus_win(struct ws_win *);
1055 void focusin(xcb_focus_in_event_t *);
1056 #ifdef SWM_DEBUG
1057 void focusout(xcb_focus_out_event_t *);
1058 #endif
1059 void focusrg(struct binding *, struct swm_region *, union arg *);
1060 void fontset_init(void);
1061 void free_window(struct ws_win *);
1062 xcb_atom_t get_atom_from_string(const char *);
1063 #ifdef SWM_DEBUG
1064 char *get_atom_name(xcb_atom_t);
1065 #endif
1066 struct ws_win *get_focus_magic(struct ws_win *);
1067 struct ws_win *get_focus_prev(struct ws_win *);
1068 xcb_generic_event_t *get_next_event(bool);
1069 #ifdef SWM_DEBUG
1070 char *get_notify_detail_label(uint8_t);
1071 char *get_notify_mode_label(uint8_t);
1072 #endif
1073 struct ws_win *get_pointer_win(xcb_window_t);
1074 struct ws_win *get_region_focus(struct swm_region *);
1075 int get_region_index(struct swm_region *);
1076 xcb_screen_t *get_screen(int);
1077 int get_screen_count(void);
1078 #ifdef SWM_DEBUG
1079 char *get_source_type_label(uint32_t);
1080 char *get_stack_mode_name(uint8_t);
1081 char *get_state_mask_label(uint16_t);
1082 #endif
1083 int32_t get_swm_ws(xcb_window_t);
1084 bool get_urgent(struct ws_win *);
1085 #ifdef SWM_DEBUG
1086 char *get_win_input_model(struct ws_win *);
1087 #endif
1088 char *get_win_name(xcb_window_t);
1089 uint8_t get_win_state(xcb_window_t);
1090 void get_wm_protocols(struct ws_win *);
1091 int get_ws_idx(struct ws_win *);
1092 void grab_windows(void);
1093 void grabbuttons(void);
1094 void grabkeys(void);
1095 void iconify(struct binding *, struct swm_region *, union arg *);
1096 bool isxlfd(char *);
1097 bool keybindreleased(struct binding *, xcb_key_release_event_t *);
1098 void keypress(xcb_key_press_event_t *);
1099 void keyrelease(xcb_key_release_event_t *);
1100 bool keyrepeating(xcb_key_release_event_t *);
1101 void kill_bar_extra_atexit(void);
1102 void kill_refs(struct ws_win *);
1103 #ifdef SWM_DEBUG
1104 void leavenotify(xcb_leave_notify_event_t *);
1105 #endif
1106 void load_float_geom(struct ws_win *);
1107 void lower_window(struct ws_win *);
1108 struct ws_win *manage_window(xcb_window_t, int, bool);
1109 void map_window(struct ws_win *);
1110 void mapnotify(xcb_map_notify_event_t *);
1111 void mappingnotify(xcb_mapping_notify_event_t *);
1112 void maprequest(xcb_map_request_event_t *);
1113 void maximize_toggle(struct binding *, struct swm_region *, union arg *);
1114 void motionnotify(xcb_motion_notify_event_t *);
1115 void move(struct binding *, struct swm_region *, union arg *);
1116 void move_win(struct ws_win *, struct binding *, int);
1117 uint32_t name_to_pixel(int, const char *);
1118 void name_workspace(struct binding *, struct swm_region *, union arg *);
1119 void new_region(struct swm_screen *, int, int, int, int);
1120 int parse_rgb(const char *, uint16_t *, uint16_t *, uint16_t *);
1121 int parsebinding(const char *, uint16_t *, enum binding_type *, uint32_t *,
1122 uint32_t *);
1123 int parsequirks(const char *, uint32_t *, int *);
1124 void pressbutton(struct binding *, struct swm_region *, union arg *);
1125 void priorws(struct binding *, struct swm_region *, union arg *);
1126 #ifdef SWM_DEBUG
1127 void print_win_geom(xcb_window_t);
1128 #endif
1129 void propertynotify(xcb_property_notify_event_t *);
1130 void put_back_event(xcb_generic_event_t *);
1131 void quirk_free(struct quirk *);
1132 void quirk_insert(const char *, const char *, const char *, uint32_t, int);
1133 void quirk_remove(struct quirk *);
1134 void quirk_replace(struct quirk *, const char *, const char *, const char *,
1135 uint32_t, int);
1136 void quit(struct binding *, struct swm_region *, union arg *);
1137 void raise_focused(struct binding *, struct swm_region *, union arg *);
1138 void raise_toggle(struct binding *, struct swm_region *, union arg *);
1139 void raise_window(struct ws_win *);
1140 void region_containment(struct ws_win *, struct swm_region *, int);
1141 struct swm_region *region_under(struct swm_screen *, int, int);
1142 void regionize(struct ws_win *, int, int);
1143 void reparent_window(struct ws_win *);
1144 void reparentnotify(xcb_reparent_notify_event_t *);
1145 void resize(struct binding *, struct swm_region *, union arg *);
1146 void resize_win(struct ws_win *, struct binding *, int);
1147 void restart(struct binding *, struct swm_region *, union arg *);
1148 struct swm_region *root_to_region(xcb_window_t, int);
1149 void screenchange(xcb_randr_screen_change_notify_event_t *);
1150 void scan_randr(int);
1151 void search_do_resp(void);
1152 void search_resp_name_workspace(const char *, size_t);
1153 void search_resp_search_window(const char *);
1154 void search_resp_search_workspace(const char *);
1155 void search_resp_uniconify(const char *, size_t);
1156 void search_win(struct binding *, struct swm_region *, union arg *);
1157 void search_win_cleanup(void);
1158 void search_workspace(struct binding *, struct swm_region *, union arg *);
1159 void send_to_rg(struct binding *, struct swm_region *, union arg *);
1160 void send_to_ws(struct binding *, struct swm_region *, union arg *);
1161 void set_region(struct swm_region *);
1162 int setautorun(const char *, const char *, int);
1163 void setbinding(uint16_t, enum binding_type, uint32_t, enum actionid,
1164 uint32_t, const char *);
1165 int setconfbinding(const char *, const char *, int);
1166 int setconfcolor(const char *, const char *, int);
1167 int setconfmodkey(const char *, const char *, int);
1168 int setconfquirk(const char *, const char *, int);
1169 int setconfregion(const char *, const char *, int);
1170 int setconfspawn(const char *, const char *, int);
1171 int setconfvalue(const char *, const char *, int);
1172 int setkeymapping(const char *, const char *, int);
1173 int setlayout(const char *, const char *, int);
1174 void setquirk(const char *, const char *, const char *, uint32_t, int);
1175 void setscreencolor(const char *, int, int);
1176 void setspawn(const char *, const char *, int);
1177 void setup_btnbindings(void);
1178 void setup_ewmh(void);
1179 void setup_globals(void);
1180 void setup_keybindings(void);
1181 void setup_quirks(void);
1182 void setup_screens(void);
1183 void setup_spawn(void);
1184 void set_child_transient(struct ws_win *, xcb_window_t *);
1185 void set_win_state(struct ws_win *, uint8_t);
1186 void shutdown_cleanup(void);
1187 void sighdlr(int);
1188 void socket_setnonblock(int);
1189 void sort_windows(struct ws_win_list *);
1190 void spawn(int, union arg *, bool);
1191 void spawn_custom(struct swm_region *, union arg *, const char *);
1192 int spawn_expand(struct swm_region *, union arg *, const char *, char ***);
1193 void spawn_insert(const char *, const char *, int);
1194 struct spawn_prog *spawn_find(const char *);
1195 void spawn_remove(struct spawn_prog *);
1196 void spawn_replace(struct spawn_prog *, const char *, const char *, int);
1197 void spawn_select(struct swm_region *, union arg *, const char *, int *);
1198 void stack_config(struct binding *, struct swm_region *, union arg *);
1199 void stack_master(struct workspace *, struct swm_geometry *, int, bool);
1200 void store_float_geom(struct ws_win *);
1201 char *strdupsafe(const char *);
1202 void swapwin(struct binding *, struct swm_region *, union arg *);
1203 void switchws(struct binding *, struct swm_region *, union arg *);
1204 void teardown_ewmh(void);
1205 void unescape_selector(char *);
1206 void unfocus_win(struct ws_win *);
1207 void uniconify(struct binding *, struct swm_region *, union arg *);
1208 void unmanage_window(struct ws_win *);
1209 void unmap_all(void);
1210 void unmap_window(struct ws_win *);
1211 void unmapnotify(xcb_unmap_notify_event_t *);
1212 void unparent_window(struct ws_win *);
1213 void update_floater(struct ws_win *);
1214 void update_modkey(uint16_t);
1215 void update_win_stacking(struct ws_win *);
1216 void update_window(struct ws_win *);
1217 void draw_frame(struct ws_win *);
1218 void update_wm_state(struct ws_win *win);
1219 void updatenumlockmask(void);
1220 void validate_spawns(void);
1221 int validate_win(struct ws_win *);
1222 int validate_ws(struct workspace *);
1223 void version(struct binding *, struct swm_region *, union arg *);
1224 void win_to_ws(struct ws_win *, int, bool);
1225 pid_t window_get_pid(xcb_window_t);
1226 void wkill(struct binding *, struct swm_region *, union arg *);
1227 void update_ws_stack(struct workspace *);
1228 void xft_init(struct swm_region *);
1229 void _add_startup_exception(const char *, va_list);
1230 void add_startup_exception(const char *, ...);
1231
1232 RB_PROTOTYPE(binding_tree, binding, entry, binding_cmp);
1233 RB_GENERATE(binding_tree, binding, entry, binding_cmp);
1234 struct binding_tree bindings;
1235
1236 void
1237 cursors_load(void)
1238 {
1239 xcb_font_t cf = XCB_NONE;
1240 int i;
1241
1242 for (i = 0; i < LENGTH(cursors); ++i) {
1243 /* try to load Xcursor first. */
1244 cursors[i].cid = XcursorLibraryLoadCursor(display,
1245 cursors[i].name);
1246
1247 /* fallback to cursorfont. */
1248 if (cursors[i].cid == XCB_CURSOR_NONE) {
1249 if (cf == XCB_NONE) {
1250 cf = xcb_generate_id(conn);
1251 xcb_open_font(conn, cf, strlen("cursor"),
1252 "cursor");
1253 }
1254
1255 cursors[i].cid = xcb_generate_id(conn);
1256 xcb_create_glyph_cursor(conn, cursors[i].cid, cf, cf,
1257 cursors[i].cf_char, cursors[i].cf_char + 1, 0, 0, 0,
1258 0xffff, 0xffff, 0xffff);
1259
1260 }
1261 }
1262
1263 if (cf != XCB_NONE)
1264 xcb_close_font(conn, cf);
1265 }
1266
1267 void
1268 cursors_cleanup(void)
1269 {
1270 int i;
1271 for (i = 0; i < LENGTH(cursors); ++i)
1272 xcb_free_cursor(conn, cursors[i].cid);
1273 }
1274
1275 char *
1276 expand_tilde(const char *s)
1277 {
1278 struct passwd *ppwd;
1279 int i;
1280 long max;
1281 char *user;
1282 const char *sc = s;
1283 char *result;
1284
1285 if (s == NULL)
1286 errx(1, "expand_tilde: NULL string.");
1287
1288 if (s[0] != '~') {
1289 result = strdup(sc);
1290 goto out;
1291 }
1292
1293 ++s;
1294
1295 if ((max = sysconf(_SC_LOGIN_NAME_MAX)) == -1)
1296 errx(1, "expand_tilde: sysconf");
1297
1298 if ((user = calloc(1, max + 1)) == NULL)
1299 errx(1, "expand_tilde: calloc");
1300
1301 for (i = 0; s[i] != '/' && s[i] != '\0'; ++i)
1302 user[i] = s[i];
1303 user[i] = '\0';
1304 s = &s[i];
1305
1306 ppwd = strlen(user) == 0 ? getpwuid(getuid()) : getpwnam(user);
1307 free(user);
1308
1309 if (ppwd == NULL)
1310 result = strdup(sc);
1311 else
1312 if (asprintf(&result, "%s%s", ppwd->pw_dir, s) == -1)
1313 result = NULL;
1314 out:
1315 if (result == NULL)
1316 errx(1, "expand_tilde: failed to allocate memory.");
1317
1318 return result;
1319 }
1320
1321 int
1322 parse_rgb(const char *rgb, uint16_t *rr, uint16_t *gg, uint16_t *bb)
1323 {
1324 unsigned int tmpr, tmpg, tmpb;
1325
1326 if (sscanf(rgb, "rgb:%x/%x/%x", &tmpr, &tmpg, &tmpb) != 3)
1327 return (-1);
1328
1329 *rr = RGB_8_TO_16(tmpr);
1330 *gg = RGB_8_TO_16(tmpg);
1331 *bb = RGB_8_TO_16(tmpb);
1332
1333 return (0);
1334 }
1335
1336 xcb_screen_t *
1337 get_screen(int screen)
1338 {
1339 const xcb_setup_t *r;
1340 xcb_screen_iterator_t iter;
1341
1342 if ((r = xcb_get_setup(conn)) == NULL) {
1343 DNPRINTF(SWM_D_MISC, "get_screen: xcb_get_setup\n");
1344 check_conn();
1345 }
1346
1347 iter = xcb_setup_roots_iterator(r);
1348 for (; iter.rem; --screen, xcb_screen_next(&iter))
1349 if (screen == 0)
1350 return (iter.data);
1351
1352 return (NULL);
1353 }
1354
1355 int
1356 get_screen_count(void)
1357 {
1358 const xcb_setup_t *r;
1359
1360 if ((r = xcb_get_setup(conn)) == NULL) {
1361 DNPRINTF(SWM_D_MISC, "get_screen_count: xcb_get_setup\n");
1362 check_conn();
1363 }
1364
1365 return xcb_setup_roots_length(r);
1366 }
1367
1368 int
1369 get_region_index(struct swm_region *r)
1370 {
1371 struct swm_region *rr;
1372 int ridx = 0;
1373
1374 if (r == NULL)
1375 return -1;
1376
1377 TAILQ_FOREACH(rr, &r->s->rl, entry) {
1378 if (rr == r)
1379 break;
1380 ++ridx;
1381 }
1382
1383 if (rr == NULL)
1384 return -1;
1385
1386 return ridx;
1387 }
1388
1389 void
1390 focus_flush(void)
1391 {
1392 if (focus_mode == SWM_FOCUS_DEFAULT)
1393 event_drain(XCB_ENTER_NOTIFY);
1394 else
1395 xcb_flush(conn);
1396 }
1397
1398 xcb_atom_t
1399 get_atom_from_string(const char *str)
1400 {
1401 xcb_intern_atom_cookie_t c;
1402 xcb_intern_atom_reply_t *r;
1403 xcb_atom_t atom;
1404
1405 c = xcb_intern_atom(conn, 0, strlen(str), str);
1406 r = xcb_intern_atom_reply(conn, c, NULL);
1407 if (r) {
1408 atom = r->atom;
1409 free(r);
1410
1411 return (atom);
1412 }
1413
1414 return (XCB_ATOM_NONE);
1415 }
1416
1417 void
1418 get_wm_protocols(struct ws_win *win) {
1419 int i;
1420 xcb_icccm_get_wm_protocols_reply_t wpr;
1421
1422 if (xcb_icccm_get_wm_protocols_reply(conn,
1423 xcb_icccm_get_wm_protocols(conn, win->id, a_prot),
1424 &wpr, NULL)) {
1425 for (i = 0; i < (int)wpr.atoms_len; i++) {
1426 if (wpr.atoms[i] == a_takefocus)
1427 win->take_focus = true;
1428 if (wpr.atoms[i] == a_delete)
1429 win->can_delete = true;
1430 }
1431 xcb_icccm_get_wm_protocols_reply_wipe(&wpr);
1432 }
1433 }
1434
1435 void
1436 setup_ewmh(void)
1437 {
1438 xcb_window_t root, win;
1439 int i, j, num_screens;
1440
1441 for (i = 0; i < LENGTH(ewmh); i++)
1442 ewmh[i].atom = get_atom_from_string(ewmh[i].name);
1443
1444 num_screens = get_screen_count();
1445 for (i = 0; i < num_screens; i++) {
1446 root = screens[i].root;
1447
1448 /* Set up _NET_SUPPORTING_WM_CHECK. */
1449 win = xcb_generate_id(conn);
1450 xcb_create_window(conn, XCB_COPY_FROM_PARENT, win, root,
1451 0, 0, 1, 1, 0, XCB_WINDOW_CLASS_INPUT_OUTPUT,
1452 XCB_COPY_FROM_PARENT, 0, NULL);
1453
1454 xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root,
1455 a_net_wm_check, XCB_ATOM_WINDOW, 32, 1, &win);
1456 xcb_change_property(conn, XCB_PROP_MODE_REPLACE, win,
1457 a_net_wm_check, XCB_ATOM_WINDOW, 32, 1, &win);
1458
1459 xcb_change_property(conn, XCB_PROP_MODE_REPLACE, win,
1460 ewmh[_NET_WM_NAME].atom, a_utf8_string,
1461 8, strlen("spectrwm"), "spectrwm");
1462
1463 /* Report supported atoms */
1464 xcb_delete_property(conn, root, a_net_supported);
1465 for (j = 0; j < LENGTH(ewmh); j++)
1466 xcb_change_property(conn, XCB_PROP_MODE_APPEND, root,
1467 a_net_supported, XCB_ATOM_ATOM, 32, 1,
1468 &ewmh[j].atom);
1469
1470 }
1471
1472 ewmh_update_desktops();
1473 ewmh_get_desktop_names();
1474 }
1475
1476 void
1477 teardown_ewmh(void)
1478 {
1479 int i, num_screens;
1480 xcb_window_t id;
1481 xcb_get_property_cookie_t pc;
1482 xcb_get_property_reply_t *pr;
1483
1484 num_screens = get_screen_count();
1485
1486 for (i = 0; i < num_screens; i++) {
1487 /* Get the support check window and destroy it */
1488 pc = xcb_get_property(conn, 0, screens[i].root, a_net_wm_check,
1489 XCB_ATOM_WINDOW, 0, 1);
1490 pr = xcb_get_property_reply(conn, pc, NULL);
1491 if (pr == NULL)
1492 continue;
1493 if (pr->format == a_net_wm_check) {
1494 id = *((xcb_window_t *)xcb_get_property_value(pr));
1495
1496 xcb_destroy_window(conn, id);
1497 xcb_delete_property(conn, screens[i].root,
1498 a_net_wm_check);
1499 xcb_delete_property(conn, screens[i].root,
1500 a_net_supported);
1501 }
1502 free(pr);
1503 }
1504 }
1505
1506 void
1507 ewmh_autoquirk(struct ws_win *win)
1508 {
1509 xcb_get_property_reply_t *r;
1510 xcb_get_property_cookie_t c;
1511 xcb_atom_t *type;
1512 int i, n;
1513
1514 c = xcb_get_property(conn, 0, win->id,
1515 ewmh[_NET_WM_WINDOW_TYPE].atom, XCB_ATOM_ATOM, 0, UINT32_MAX);
1516 r = xcb_get_property_reply(conn, c, NULL);
1517 if (r == NULL)
1518 return;
1519
1520 type = xcb_get_property_value(r);
1521 n = xcb_get_property_value_length(r) / sizeof(xcb_atom_t);
1522
1523 for (i = 0; i < n; i++) {
1524 if (type[i] == ewmh[_NET_WM_WINDOW_TYPE_NORMAL].atom)
1525 break;
1526 if (type[i] == ewmh[_NET_WM_WINDOW_TYPE_DOCK].atom ||
1527 type[i] == ewmh[_NET_WM_WINDOW_TYPE_TOOLBAR].atom ||
1528 type[i] == ewmh[_NET_WM_WINDOW_TYPE_UTILITY].atom) {
1529 win->quirks = SWM_Q_FLOAT | SWM_Q_ANYWHERE;
1530 break;
1531 }
1532 if (type[i] == ewmh[_NET_WM_WINDOW_TYPE_SPLASH].atom ||
1533 type[i] == ewmh[_NET_WM_WINDOW_TYPE_DIALOG].atom) {
1534 win->quirks = SWM_Q_FLOAT;
1535 break;
1536 }
1537 }
1538 free(r);
1539 }
1540
1541 void
1542 ewmh_update_actions(struct ws_win *win)
1543 {
1544 xcb_atom_t actions[SWM_EWMH_ACTION_COUNT_MAX];
1545 int n = 0;
1546
1547 if (win == NULL)
1548 return;
1549
1550 actions[n++] = ewmh[_NET_WM_ACTION_CLOSE].atom;
1551
1552 if (ABOVE(win)) {
1553 actions[n++] = ewmh[_NET_WM_ACTION_MOVE].atom;
1554 actions[n++] = ewmh[_NET_WM_ACTION_RESIZE].atom;
1555 actions[n++] = ewmh[_NET_WM_ACTION_ABOVE].atom;
1556 }
1557
1558 xcb_change_property(conn, XCB_PROP_MODE_REPLACE, win->id,
1559 ewmh[_NET_WM_ALLOWED_ACTIONS].atom, XCB_ATOM_ATOM, 32, 1, actions);
1560 }
1561
1562 #define _NET_WM_STATE_REMOVE 0 /* remove/unset property */
1563 #define _NET_WM_STATE_ADD 1 /* add/set property */
1564 #define _NET_WM_STATE_TOGGLE 2 /* toggle property */
1565
1566 void
1567 ewmh_change_wm_state(struct ws_win *win, xcb_atom_t state, long action)
1568 {
1569 uint32_t flag = 0;
1570 uint32_t new_flags;
1571 #ifdef SWM_DEBUG
1572 char *name;
1573
1574 name = get_atom_name(state);
1575 DNPRINTF(SWM_D_PROP, "ewmh_change_wm_state: win %#x, state: %s, "
1576 "action: %ld\n", WINID(win), name, action);
1577 free(name);
1578 #endif
1579 if (win == NULL)
1580 goto out;
1581
1582 if (state == ewmh[_NET_WM_STATE_FULLSCREEN].atom)
1583 flag = EWMH_F_FULLSCREEN;
1584 else if (state == ewmh[_NET_WM_STATE_ABOVE].atom)
1585 flag = EWMH_F_ABOVE;
1586 else if (state == ewmh[_NET_WM_STATE_HIDDEN].atom)
1587 flag = EWMH_F_HIDDEN;
1588 else if (state == ewmh[_NET_WM_STATE_MAXIMIZED_VERT].atom ||
1589 state == ewmh[_NET_WM_STATE_MAXIMIZED_HORZ].atom)
1590 flag = EWMH_F_MAXIMIZED;
1591 else if (state == ewmh[_SWM_WM_STATE_MANUAL].atom)
1592 flag = SWM_F_MANUAL;
1593 else if (state == ewmh[_NET_WM_STATE_SKIP_PAGER].atom)
1594 flag = EWMH_F_SKIP_PAGER;
1595 else if (state == ewmh[_NET_WM_STATE_SKIP_TASKBAR].atom)
1596 flag = EWMH_F_SKIP_TASKBAR;
1597
1598 /* Disallow unfloating transients. */
1599 if (TRANS(win) && flag == EWMH_F_ABOVE)
1600 goto out;
1601
1602 new_flags = win->ewmh_flags;
1603
1604 switch (action) {
1605 case _NET_WM_STATE_REMOVE:
1606 new_flags &= ~flag;
1607 break;
1608 case _NET_WM_STATE_ADD:
1609 new_flags |= flag;
1610 break;
1611 case _NET_WM_STATE_TOGGLE:
1612 new_flags ^= flag;
1613 break;
1614 }
1615
1616 ewmh_apply_flags(win, new_flags);
1617
1618 out:
1619 DNPRINTF(SWM_D_PROP, "ewmh_change_wm_state: done.\n");
1620 }
1621
1622 void
1623 ewmh_apply_flags(struct ws_win *win, uint32_t pending)
1624 {
1625 struct workspace *ws;
1626 uint32_t changed;
1627
1628 changed = win->ewmh_flags ^ pending;
1629 if (changed == 0)
1630 return;
1631
1632 DNPRINTF(SWM_D_PROP, "ewmh_apply_flags: pending: %d\n", pending);
1633
1634 win->ewmh_flags = pending;
1635 ws = win->ws;
1636
1637 if (changed & EWMH_F_HIDDEN) {
1638 if (ICONIC(win)) {
1639 if (focus_mode != SWM_FOCUS_FOLLOW)
1640 ws->focus_pending = get_focus_prev(win);
1641
1642 unfocus_win(win);
1643 unmap_window(win);
1644 } else {
1645 /* Reload floating geometry in case region changed. */
1646 if (FLOATING(win))
1647 load_float_geom(win);
1648
1649 /* The window is no longer iconic, prepare focus. */
1650 if (focus_mode != SWM_FOCUS_FOLLOW)
1651 ws->focus_pending = get_focus_magic(win);
1652 raise_window(win);
1653 }
1654 }
1655
1656 if (changed & EWMH_F_ABOVE) {
1657 if (ws->cur_layout != &layouts[SWM_MAX_STACK]) {
1658 if (ABOVE(win))
1659 load_float_geom(win);
1660 else if (!MAXIMIZED(win))
1661 store_float_geom(win);
1662
1663 win->ewmh_flags &= ~EWMH_F_MAXIMIZED;
1664 changed &= ~EWMH_F_MAXIMIZED;
1665 raise_window(win);
1666 } else {
1667 /* Revert. */
1668 win->ewmh_flags ^= EWMH_F_ABOVE & pending;
1669 }
1670 }
1671
1672 if (changed & EWMH_F_MAXIMIZED) {
1673 /* VERT and/or HORZ changed. */
1674 if (ABOVE(win)) {
1675 if (!MAXIMIZED(win))
1676 load_float_geom(win);
1677 else
1678 store_float_geom(win);
1679 }
1680
1681 if (MAXIMIZED(win)) {
1682 if (focus_mode != SWM_FOCUS_FOLLOW &&
1683 ws->cur_layout != &layouts[SWM_MAX_STACK]) {
1684 if (WS_FOCUSED(ws))
1685 focus_win(win);
1686 else
1687 ws->focus_pending = win;
1688 }
1689 }
1690
1691 draw_frame(win);
1692 raise_window(win);
1693 }
1694
1695 if (changed & EWMH_F_FULLSCREEN) {
1696 if (FULLSCREEN(win)) {
1697 if (focus_mode != SWM_FOCUS_FOLLOW) {
1698 if (WS_FOCUSED(ws))
1699 focus_win(win);
1700 else
1701 ws->focus_pending = win;
1702 }
1703 } else {
1704 load_float_geom(win);
1705 }
1706
1707 win->ewmh_flags &= ~EWMH_F_MAXIMIZED;
1708 raise_window(win);
1709 }
1710
1711 DNPRINTF(SWM_D_PROP, "ewmh_apply_flags: done.\n");
1712 }
1713
1714 void
1715 ewmh_update_wm_state(struct ws_win *win) {
1716 xcb_atom_t vals[SWM_EWMH_ACTION_COUNT_MAX];
1717 int n = 0;
1718
1719 if (ICONIC(win))
1720 vals[n++] = ewmh[_NET_WM_STATE_HIDDEN].atom;
1721 if (FULLSCREEN(win))
1722 vals[n++] = ewmh[_NET_WM_STATE_FULLSCREEN].atom;
1723 if (MAXIMIZED_VERT(win))
1724 vals[n++] = ewmh[_NET_WM_STATE_MAXIMIZED_VERT].atom;
1725 if (MAXIMIZED_HORZ(win))
1726 vals[n++] = ewmh[_NET_WM_STATE_MAXIMIZED_HORZ].atom;
1727 if (win->ewmh_flags & EWMH_F_SKIP_PAGER)
1728 vals[n++] = ewmh[_NET_WM_STATE_SKIP_PAGER].atom;
1729 if (win->ewmh_flags & EWMH_F_SKIP_TASKBAR)
1730 vals[n++] = ewmh[_NET_WM_STATE_SKIP_TASKBAR].atom;
1731 if (win->ewmh_flags & EWMH_F_ABOVE)
1732 vals[n++] = ewmh[_NET_WM_STATE_ABOVE].atom;
1733 if (win->ewmh_flags & SWM_F_MANUAL)
1734 vals[n++] = ewmh[_SWM_WM_STATE_MANUAL].atom;
1735
1736 if (n > 0)
1737 xcb_change_property(conn, XCB_PROP_MODE_REPLACE, win->id,
1738 ewmh[_NET_WM_STATE].atom, XCB_ATOM_ATOM, 32, n, vals);
1739 else
1740 xcb_delete_property(conn, win->id, ewmh[_NET_WM_STATE].atom);
1741 }
1742
1743 void
1744 ewmh_get_wm_state(struct ws_win *win)
1745 {
1746 xcb_atom_t *states;
1747 xcb_get_property_cookie_t c;
1748 xcb_get_property_reply_t *r;
1749 int i, n;
1750
1751 if (win == NULL)
1752 return;
1753
1754 win->ewmh_flags = 0;
1755
1756 c = xcb_get_property(conn, 0, win->id, ewmh[_NET_WM_STATE].atom,
1757 XCB_ATOM_ATOM, 0, UINT32_MAX);
1758 r = xcb_get_property_reply(conn, c, NULL);
1759 if (r == NULL)
1760 return;
1761
1762 states = xcb_get_property_value(r);
1763 n = xcb_get_property_value_length(r) / sizeof(xcb_atom_t);
1764
1765 for (i = 0; i < n; i++)
1766 ewmh_change_wm_state(win, states[i], _NET_WM_STATE_ADD);
1767
1768 free(r);
1769 }
1770
1771 /* events */
1772 #ifdef SWM_DEBUG
1773 void
1774 dumpwins(struct binding *bp, struct swm_region *r, union arg *args)
1775 {
1776 struct ws_win *w;
1777 uint32_t state;
1778 xcb_get_window_attributes_cookie_t c;
1779 xcb_get_window_attributes_reply_t *wa;
1780
1781 /* suppress unused warning since var is needed */
1782 (void)bp;
1783 (void)args;
1784
1785 if (r->ws == NULL) {
1786 DPRINTF("dumpwins: invalid workspace\n");
1787 return;
1788 }
1789
1790 DPRINTF("=== managed window list ws %02d ===\n", r->ws->idx);
1791 TAILQ_FOREACH(w, &r->ws->winlist, entry) {
1792 state = get_win_state(w->id);
1793 c = xcb_get_window_attributes(conn, w->id);
1794 wa = xcb_get_window_attributes_reply(conn, c, NULL);
1795 if (wa) {
1796 DPRINTF("win %#x (%#x), map_state: %d, state: %u, "
1797 "transient: %#x\n", w->frame, w->id, wa->map_state,
1798 state, w->transient);
1799 free(wa);
1800 } else
1801 DPRINTF("win %#x, failed xcb_get_window_attributes\n",
1802 w->id);
1803 }
1804
1805 DPRINTF("=== stacking order (top down) === \n");
1806 TAILQ_FOREACH(w, &r->ws->stack, stack_entry) {
1807 DPRINTF("win %#x (%#x), fs: %s, maximized: %s, above: %s, "
1808 "iconic: %s\n", w->frame, w->id, YESNO(FULLSCREEN(w)),
1809 YESNO(MAXIMIZED(w)), YESNO(ABOVE(w)), YESNO(ICONIC(w)));
1810 }
1811
1812 DPRINTF("===== unmanaged window list =====\n");
1813 TAILQ_FOREACH(w, &r->ws->unmanagedlist, entry) {
1814 state = get_win_state(w->id);
1815 c = xcb_get_window_attributes(conn, w->id);
1816 wa = xcb_get_window_attributes_reply(conn, c, NULL);
1817 if (wa) {
1818 DPRINTF("win %#x, map_state: %d, state: %u, "
1819 "transient: %#x\n", w->id, wa->map_state,
1820 state, w->transient);
1821 free(wa);
1822 } else
1823 DPRINTF("win %#x, failed xcb_get_window_attributes\n",
1824 w->id);
1825 }
1826
1827 DPRINTF("=================================\n");
1828 }
1829
1830 void
1831 debug_toggle(struct binding *b, struct swm_region *r, union arg *s)
1832 {
1833 struct ws_win *win;
1834 int num_screens, i, j;
1835
1836 /* Suppress warnings. */
1837 (void)b;
1838 (void)r;
1839 (void)s;
1840
1841 DNPRINTF(SWM_D_MISC, "debug_toggle\n");
1842
1843 debug_enabled = !debug_enabled;
1844
1845 num_screens = get_screen_count();
1846 for (i = 0; i < num_screens; i++)
1847 for (j = 0; j < workspace_limit; j++)
1848 TAILQ_FOREACH(win, &screens[i].ws[j].winlist, entry)
1849 debug_refresh(win);
1850
1851 xcb_flush(conn);
1852 }
1853
1854 void
1855 debug_refresh(struct ws_win *win)
1856 {
1857 struct ws_win *w;
1858 XftDraw *draw;
1859 XGlyphInfo info;
1860 GC l_draw;
1861 XGCValues l_gcv;
1862 XRectangle l_ibox, l_lbox;
1863 xcb_rectangle_t rect;
1864 size_t len;
1865 uint32_t wc[4], mask, width, height, gcv[1];
1866 int widx, sidx;
1867 char *s;
1868 xcb_screen_t *screen;
1869
1870 if (debug_enabled) {
1871 /* Create debug window if it doesn't exist. */
1872 if (win->debug == XCB_WINDOW_NONE) {
1873 if ((screen = get_screen(win->s->idx)) == NULL)
1874 errx(1, "ERROR: can't get screen %d.",
1875 win->s->idx);
1876
1877 win->debug = xcb_generate_id(conn);
1878 wc[0] = win->s->c[SWM_S_COLOR_BAR].pixel;
1879 wc[1] = win->s->c[SWM_S_COLOR_BAR_BORDER].pixel;
1880 wc[2] = screen->default_colormap;
1881
1882 xcb_create_window(conn, screen->root_depth, win->debug,
1883 win->frame, 0, 0, 10, 10, 1,
1884 XCB_WINDOW_CLASS_INPUT_OUTPUT, screen->root_visual,
1885 XCB_CW_BACK_PIXEL | XCB_CW_BORDER_PIXEL |
1886 XCB_CW_COLORMAP, wc);
1887
1888 xcb_map_window(conn, win->debug);
1889 }
1890
1891 /* Determine workspace window list index. */
1892 widx = 0;
1893 TAILQ_FOREACH(w, &win->ws->winlist, entry) {
1894 ++widx;
1895 if (w == win)
1896 break;
1897 }
1898
1899 /* Determine stacking index (top down). */
1900 sidx = 0;
1901 TAILQ_FOREACH(w, &win->ws->stack, stack_entry) {
1902 ++sidx;
1903 if (w == win)
1904 break;
1905 }
1906
1907 if (asprintf(&s, "%#x f:%#x wl:%d s:%d im:%s", win->id,
1908 win->frame, widx, sidx, get_win_input_model(win)) == -1)
1909 return;
1910
1911 len = strlen(s);
1912
1913 /* Update window to an appropriate dimension. */
1914 if (bar_font_legacy) {
1915 XmbTextExtents(bar_fs, s, len, &l_ibox, &l_lbox);
1916 width = l_lbox.width + 4;
1917 height = bar_fs_extents->max_logical_extent.height + 4;
1918 } else {
1919 XftTextExtentsUtf8(display, bar_font, (FcChar8 *)s, len,
1920 &info);
1921 width = info.width + 4;
1922 height = bar_font->height + 4;
1923 }
1924
1925 mask = XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y |
1926 XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT;
1927 if (win->bordered)
1928 wc[0] = wc[1] = border_width;
1929 else
1930 wc[0] = wc[1] = 0;
1931
1932 wc[2] = width;
1933 wc[3] = height;
1934
1935 xcb_configure_window(conn, win->debug, mask, wc);
1936
1937 /* Draw a filled rectangle to 'clear' window. */
1938 rect.x = 0;
1939 rect.y = 0;
1940 rect.width = width;
1941 rect.height = height;
1942
1943 gcv[0] = win->s->c[SWM_S_COLOR_BAR].pixel;
1944 xcb_change_gc(conn, win->s->bar_gc, XCB_GC_FOREGROUND, gcv);
1945 xcb_poly_fill_rectangle(conn, win->debug, win->s->bar_gc, 1,
1946 &rect);
1947
1948 /* Draw text. */
1949 if (bar_font_legacy) {
1950 l_gcv.graphics_exposures = 0;
1951 l_draw = XCreateGC(display, win->debug, 0, &l_gcv);
1952
1953 XSetForeground(display, l_draw,
1954 win->s->c[SWM_S_COLOR_BAR_FONT].pixel);
1955
1956 DRAWSTRING(display, win->debug, bar_fs, l_draw, 2,
1957 (bar_fs_extents->max_logical_extent.height -
1958 l_lbox.height) / 2 - l_lbox.y, s, len);
1959
1960 XFreeGC(display, l_draw);
1961 } else {
1962 draw = XftDrawCreate(display, win->debug,
1963 DefaultVisual(display, win->s->idx),
1964 DefaultColormap(display, win->s->idx));
1965
1966 XftDrawStringUtf8(draw, &bar_font_color, bar_font, 2,
1967 (bar_height + bar_font->height) / 2 -
1968 bar_font->descent, (FcChar8 *)s, len);
1969
1970 XftDrawDestroy(draw);
1971 }
1972
1973 free(s);
1974 } else if (win->debug != XCB_WINDOW_NONE) {
1975 xcb_destroy_window(conn, win->debug);
1976 win->debug = XCB_WINDOW_NONE;
1977 }
1978 }
1979 #else
1980 void
1981 dumpwins(struct binding *b, struct swm_region *r, union arg *s)
1982 {
1983 (void)b;
1984 (void)r;
1985 (void)s;
1986 }
1987
1988 void
1989 debug_toggle(struct binding *b, struct swm_region *r, union arg *s)
1990 {
1991 (void)b;
1992 (void)r;
1993 (void)s;
1994 }
1995 #endif /* SWM_DEBUG */
1996
1997 void
1998 sighdlr(int sig)
1999 {
2000 int saved_errno, status;
2001 pid_t pid;
2002
2003 saved_errno = errno;
2004
2005 switch (sig) {
2006 case SIGCHLD:
2007 while ((pid = waitpid(WAIT_ANY, &status, WNOHANG)) != 0) {
2008 if (pid == -1) {
2009 if (errno == EINTR)
2010 continue;
2011 #ifdef SWM_DEBUG
2012 if (errno != ECHILD)
2013 warn("sighdlr: waitpid");
2014 #endif /* SWM_DEBUG */
2015 break;
2016 }
2017 if (pid == searchpid)
2018 search_resp = 1;
2019
2020 #ifdef SWM_DEBUG
2021 if (WIFEXITED(status)) {
2022 if (WEXITSTATUS(status) != 0)
2023 warnx("sighdlr: child exit status: %d",
2024 WEXITSTATUS(status));
2025 } else
2026 warnx("sighdlr: child is terminated "
2027 "abnormally");
2028 #endif /* SWM_DEBUG */
2029 }
2030 break;
2031
2032 case SIGHUP:
2033 restart_wm = 1;
2034 break;
2035 case SIGINT:
2036 case SIGTERM:
2037 case SIGQUIT:
2038 running = 0;
2039 break;
2040 }
2041
2042 errno = saved_errno;
2043 }
2044
2045 struct pid_e *
2046 find_pid(pid_t pid)
2047 {
2048 struct pid_e *p = NULL;
2049
2050 DNPRINTF(SWM_D_MISC, "find_pid: %d\n", pid);
2051
2052 if (pid == 0)
2053 return (NULL);
2054
2055 TAILQ_FOREACH(p, &pidlist, entry) {
2056 if (p->pid == pid)
2057 return (p);
2058 }
2059
2060 return (NULL);
2061 }
2062
2063 uint32_t
2064 name_to_pixel(int sidx, const char *colorname)
2065 {
2066 uint32_t result = 0;
2067 char cname[32] = "#";
2068 xcb_screen_t *screen;
2069 xcb_colormap_t cmap;
2070 xcb_alloc_color_reply_t *cr;
2071 xcb_alloc_named_color_reply_t *nr;
2072 uint16_t rr, gg, bb;
2073
2074 screen = get_screen(sidx);
2075 cmap = screen->default_colormap;
2076
2077 /* color is in format rgb://rr/gg/bb */
2078 if (strncmp(colorname, "rgb:", 4) == 0) {
2079 if (parse_rgb(colorname, &rr, &gg, &bb) == -1)
2080 warnx("could not parse rgb %s", colorname);
2081 else {
2082 cr = xcb_alloc_color_reply(conn,
2083 xcb_alloc_color(conn, cmap, rr, gg, bb),
2084 NULL);
2085 if (cr) {
2086 result = cr->pixel;
2087 free(cr);
2088 } else
2089 warnx("color '%s' not found", colorname);
2090 }
2091 } else {
2092 nr = xcb_alloc_named_color_reply(conn,
2093 xcb_alloc_named_color(conn, cmap, strlen(colorname),
2094 colorname), NULL);
2095 if (nr == NULL) {
2096 strlcat(cname, colorname + 2, sizeof cname - 1);
2097 nr = xcb_alloc_named_color_reply(conn,
2098 xcb_alloc_named_color(conn, cmap, strlen(cname),
2099 cname), NULL);
2100 }
2101 if (nr) {
2102 result = nr->pixel;
2103 free(nr);
2104 } else
2105 warnx("color '%s' not found", colorname);
2106 }
2107
2108 return (result);
2109 }
2110
2111 void
2112 setscreencolor(const char *val, int i, int c)
2113 {
2114 if (i < 0 || i >= get_screen_count())
2115 return;
2116
2117 screens[i].c[c].pixel = name_to_pixel(i, val);
2118 free(screens[i].c[c].name);
2119 if ((screens[i].c[c].name = strdup(val)) == NULL)
2120 err(1, "strdup");
2121 }
2122
2123 void
2124 fancy_stacker(struct workspace *ws)
2125 {
2126 strlcpy(ws->stacker, "[ ]", sizeof ws->stacker);
2127 if (ws->cur_layout->l_stack == vertical_stack)
2128 snprintf(ws->stacker, sizeof ws->stacker,
2129 ws->l_state.vertical_flip ? "[%d>%d]" : "[%d|%d]",
2130 ws->l_state.vertical_mwin, ws->l_state.vertical_stacks);
2131 else if (ws->cur_layout->l_stack == horizontal_stack)
2132 snprintf(ws->stacker, sizeof ws->stacker,
2133 ws->l_state.horizontal_flip ? "[%dv%d]" : "[%d-%d]",
2134 ws->l_state.horizontal_mwin, ws->l_state.horizontal_stacks);
2135 }
2136
2137 void
2138 plain_stacker(struct workspace *ws)
2139 {
2140 strlcpy(ws->stacker, "[ ]", sizeof ws->stacker);
2141 if (ws->cur_layout->l_stack == vertical_stack)
2142 strlcpy(ws->stacker, ws->l_state.vertical_flip ? "[>]" : "[|]",
2143 sizeof ws->stacker);
2144 else if (ws->cur_layout->l_stack == horizontal_stack)
2145 strlcpy(ws->stacker, ws->l_state.horizontal_flip ? "[v]" : "[-]",
2146 sizeof ws->stacker);
2147 }
2148
2149 void
2150 custom_region(const char *val)
2151 {
2152 unsigned int x, y, w, h;
2153 int sidx, num_screens;
2154 xcb_screen_t *screen;
2155
2156 DNPRINTF(SWM_D_CONF, "custom_region: %s\n", val);
2157
2158 num_screens = get_screen_count();
2159 if (sscanf(val, "screen[%d]:%ux%u+%u+%u", &sidx, &w, &h, &x, &y) != 5)
2160 errx(1, "invalid custom region, "
2161 "should be 'screen[<n>]:<n>x<n>+<n>+<n>");
2162 if (sidx < 1 || sidx > num_screens)
2163 errx(1, "invalid screen index: %d out of bounds (maximum %d)",
2164 sidx, num_screens);
2165 sidx--;
2166
2167 if ((screen = get_screen(sidx)) == NULL)
2168 errx(1, "ERROR: can't get screen %d.", sidx);
2169
2170 if (w < 1 || h < 1)
2171 errx(1, "region %ux%u+%u+%u too small", w, h, x, y);
2172
2173 if (x > screen->width_in_pixels ||
2174 y > screen->height_in_pixels ||
2175 w + x > screen->width_in_pixels ||
2176 h + y > screen->height_in_pixels) {
2177 warnx("ignoring region %ux%u+%u+%u - not within screen "
2178 "boundaries (%ux%u)", w, h, x, y,
2179 screen->width_in_pixels, screen->height_in_pixels);
2180 return;
2181 }
2182
2183 new_region(&screens[sidx], x, y, w, h);
2184 }
2185
2186 void
2187 socket_setnonblock(int fd)
2188 {
2189 int flags;
2190
2191 if ((flags = fcntl(fd, F_GETFL, 0)) == -1)
2192 err(1, "fcntl F_GETFL");
2193 flags |= O_NONBLOCK;
2194 if ((flags = fcntl(fd, F_SETFL, flags)) == -1)
2195 err(1, "fcntl F_SETFL");
2196 }
2197
2198 void
2199 bar_print_legacy(struct swm_region *r, const char *s)
2200 {
2201 xcb_rectangle_t rect;
2202 uint32_t gcv[1];
2203 XGCValues gcvd;
2204 int x = 0;
2205 size_t len;
2206 XRectangle ibox, lbox;
2207 GC draw;
2208
2209 len = strlen(s);
2210 XmbTextExtents(bar_fs, s, len, &ibox, &lbox);
2211
2212 switch (bar_justify) {
2213 case SWM_BAR_JUSTIFY_LEFT:
2214 x = SWM_BAR_OFFSET;
2215 break;
2216 case SWM_BAR_JUSTIFY_CENTER:
2217 x = (WIDTH(r) - lbox.width) / 2;
2218 break;
2219 case SWM_BAR_JUSTIFY_RIGHT:
2220 x = WIDTH(r) - lbox.width - SWM_BAR_OFFSET;
2221 break;
2222 }
2223
2224 if (x < SWM_BAR_OFFSET)
2225 x = SWM_BAR_OFFSET;
2226
2227 rect.x = 0;
2228 rect.y = 0;
2229 rect.width = WIDTH(r->bar);
2230 rect.height = HEIGHT(r->bar);
2231
2232 /* clear back buffer */
2233 gcv[0] = r->s->c[SWM_S_COLOR_BAR].pixel;
2234 xcb_change_gc(conn, r->s->bar_gc, XCB_GC_FOREGROUND, gcv);
2235 xcb_poly_fill_rectangle(conn, r->bar->buffer, r->s->bar_gc, 1, &rect);
2236
2237 /* draw back buffer */
2238 gcvd.graphics_exposures = 0;
2239 draw = XCreateGC(display, r->bar->buffer, GCGraphicsExposures, &gcvd);
2240 XSetForeground(display, draw, r->s->c[SWM_S_COLOR_BAR_FONT].pixel);
2241 DRAWSTRING(display, r->bar->buffer, bar_fs, draw,
2242 x, (bar_fs_extents->max_logical_extent.height - lbox.height) / 2 -
2243 lbox.y, s, len);
2244 XFreeGC(display, draw);
2245
2246 /* blt */
2247 xcb_copy_area(conn, r->bar->buffer, r->bar->id, r->s->bar_gc, 0, 0,
2248 0, 0, WIDTH(r->bar), HEIGHT(r->bar));
2249 }
2250
2251 void
2252 bar_print(struct swm_region *r, const char *s)
2253 {
2254 size_t len;
2255 xcb_rectangle_t rect;
2256 uint32_t gcv[1];
2257 int32_t x = 0;
2258 XGlyphInfo info;
2259 XftDraw *draw;
2260
2261 len = strlen(s);
2262
2263 XftTextExtentsUtf8(display, bar_font, (FcChar8 *)s, len, &info);
2264
2265 switch (bar_justify) {
2266 case SWM_BAR_JUSTIFY_LEFT:
2267 x = SWM_BAR_OFFSET;
2268 break;
2269 case SWM_BAR_JUSTIFY_CENTER:
2270 x = (WIDTH(r) - info.width) / 2;
2271 break;
2272 case SWM_BAR_JUSTIFY_RIGHT:
2273 x = WIDTH(r) - info.width - SWM_BAR_OFFSET;
2274 break;
2275 }
2276
2277 if (x < SWM_BAR_OFFSET)
2278 x = SWM_BAR_OFFSET;
2279
2280 rect.x = 0;
2281 rect.y = 0;
2282 rect.width = WIDTH(r->bar);
2283 rect.height = HEIGHT(r->bar);
2284
2285 /* clear back buffer */
2286 gcv[0] = r->s->c[SWM_S_COLOR_BAR].pixel;
2287 xcb_change_gc(conn, r->s->bar_gc, XCB_GC_FOREGROUND, gcv);
2288 xcb_poly_fill_rectangle(conn, r->bar->buffer, r->s->bar_gc, 1, &rect);
2289
2290 /* draw back buffer */
2291 draw = XftDrawCreate(display, r->bar->buffer,
2292 DefaultVisual(display, r->s->idx),
2293 DefaultColormap(display, r->s->idx));
2294
2295 XftDrawStringUtf8(draw, &bar_font_color, bar_font, x,
2296 (HEIGHT(r->bar) + bar_font->height) / 2 - bar_font->descent,
2297 (FcChar8 *)s, len);
2298
2299 XftDrawDestroy(draw);
2300
2301 /* blt */
2302 xcb_copy_area(conn, r->bar->buffer, r->bar->id, r->s->bar_gc, 0, 0,
2303 0, 0, WIDTH(r->bar), HEIGHT(r->bar));
2304 }
2305
2306 void
2307 bar_extra_stop(void)
2308 {
2309 if (bar_pipe[0]) {
2310 close(bar_pipe[0]);
2311 bzero(bar_pipe, sizeof bar_pipe);
2312 }
2313 if (bar_pid) {
2314 kill(bar_pid, SIGTERM);
2315 bar_pid = 0;
2316 }
2317 strlcpy(bar_ext, "", sizeof bar_ext);
2318 bar_extra = false;
2319 }
2320
2321 void
2322 bar_window_class(char *s, size_t sz, struct swm_region *r)
2323 {
2324 if (r == NULL || r->ws == NULL || r->ws->focus == NULL)
2325 return;
2326 if (r->ws->focus->ch.class_name != NULL)
2327 strlcat(s, r->ws->focus->ch.class_name, sz);
2328 }
2329
2330 void
2331 bar_window_instance(char *s, size_t sz, struct swm_region *r)
2332 {
2333 if (r == NULL || r->ws == NULL || r->ws->focus == NULL)
2334 return;
2335 if (r->ws->focus->ch.instance_name != NULL)
2336 strlcat(s, r->ws->focus->ch.instance_name, sz);
2337 }
2338
2339 void
2340 bar_window_class_instance(char *s, size_t sz, struct swm_region *r)
2341 {
2342 if (r == NULL || r->ws == NULL || r->ws->focus == NULL)
2343 return;
2344
2345 bar_window_class(s, sz, r);
2346 strlcat(s, ":", sz);
2347 bar_window_instance(s, sz, r);
2348 }
2349
2350 void
2351 bar_window_state(char *s, size_t sz, struct swm_region *r)
2352 {
2353 if (r == NULL || r ->ws == NULL || r->ws->focus == NULL)
2354 return;
2355 if (MAXIMIZED(r->ws->focus))
2356 strlcat(s, "(m)", sz);
2357 else if (ABOVE(r->ws->focus))
2358 strlcat(s, "(f)", sz);
2359 }
2360
2361 void
2362 bar_window_name(char *s, size_t sz, struct swm_region *r)
2363 {
2364 char *title;
2365
2366 if (r == NULL || r->ws == NULL || r->ws->focus == NULL)
2367 return;
2368
2369 title = get_win_name(r->ws->focus->id);
2370 strlcat(s, title, sz);
2371 free(title);
2372 }
2373
2374 bool
2375 get_urgent(struct ws_win *win)
2376 {
2377 xcb_icccm_wm_hints_t hints;
2378 xcb_get_property_cookie_t c;
2379 bool urgent = false;
2380
2381 if (win) {
2382 c = xcb_icccm_get_wm_hints(conn, win->id);
2383 if (xcb_icccm_get_wm_hints_reply(conn, c, &hints, NULL))
2384 urgent = xcb_icccm_wm_hints_get_urgency(&hints);
2385 }
2386
2387 return urgent;
2388 }
2389
2390 void
2391 bar_urgent(char *s, size_t sz)
2392 {
2393 struct ws_win *win;
2394 int i, j, num_screens;
2395 bool urgent[SWM_WS_MAX];
2396 char b[8];
2397
2398 for (i = 0; i < workspace_limit; i++)
2399 urgent[i] = false;
2400
2401 num_screens = get_screen_count();
2402 for (i = 0; i < num_screens; i++)
2403 for (j = 0; j < workspace_limit; j++)
2404 TAILQ_FOREACH(win, &screens[i].ws[j].winlist, entry)
2405 if (get_urgent(win))
2406 urgent[j] = true;
2407
2408 for (i = 0; i < workspace_limit; i++) {
2409 if (urgent[i]) {
2410 snprintf(b, sizeof b, "%d ", i + 1);
2411 strlcat(s, b, sz);
2412 } else if (!urgent_collapse) {
2413 strlcat(s, "- ", sz);
2414 }
2415 }
2416 if(urgent_collapse && s[0])
2417 s[strlen(s) - 1] = 0;
2418 }
2419
2420 void
2421 bar_workspace_name(char *s, size_t sz, struct swm_region *r)
2422 {
2423 if (r == NULL || r->ws == NULL)
2424 return;
2425 if (r->ws->name != NULL)
2426 strlcat(s, r->ws->name, sz);
2427 }
2428
2429 /* build the default bar format according to the defined enabled options */
2430 void
2431 bar_fmt(const char *fmtexp, char *fmtnew, struct swm_region *r, size_t sz)
2432 {
2433 struct ws_win *w;
2434
2435 /* if format provided, just copy the buffers */
2436 if (bar_format != NULL) {
2437 strlcpy(fmtnew, fmtexp, sz);
2438 return;
2439 }
2440
2441 /* reset the output buffer */
2442 *fmtnew = '\0';
2443
2444 strlcat(fmtnew, "+N:+I ", sz);
2445 if (stack_enabled)
2446 strlcat(fmtnew, "+S", sz);
2447 strlcat(fmtnew, " ", sz);
2448
2449 /* only show the workspace name if there's actually one */
2450 if (r != NULL && r->ws != NULL && r->ws->name != NULL)
2451 strlcat(fmtnew, "<+D>", sz);
2452
2453 /* If enabled, only show the iconic count if there are iconic wins. */
2454 if (iconic_enabled && r != NULL && r->ws != NULL)
2455 TAILQ_FOREACH(w, &r->ws->winlist, entry)
2456 if (ICONIC(w)) {
2457 strlcat(fmtnew, "{+M}", sz);
2458 break;
2459 }
2460
2461 strlcat(fmtnew, "+3<", sz);
2462
2463 if (clock_enabled) {
2464 strlcat(fmtnew, fmtexp, sz);
2465 strlcat(fmtnew, "+4<", sz);
2466 }
2467
2468 /* bar_urgent already adds the space before the last asterisk */
2469 if (urgent_enabled)
2470 strlcat(fmtnew, (urgent_collapse ? "*+U*+4<" : "* +U*+4<"), sz);
2471
2472 if (window_class_enabled) {
2473 strlcat(fmtnew, "+C", sz);
2474 if (!window_instance_enabled)
2475 strlcat(fmtnew, "+4<", sz);
2476 }
2477
2478 /* checks needed by the colon and floating strlcat(3) calls below */
2479 if (r != NULL && r->ws != NULL && r->ws->focus != NULL) {
2480 if (window_instance_enabled) {
2481 if (window_class_enabled)
2482 strlcat(fmtnew, ":", sz);
2483 strlcat(fmtnew, "+T+4<", sz);
2484 }
2485 if (window_name_enabled) {
2486 if (ABOVE(r->ws->focus) || MAXIMIZED(r->ws->focus))
2487 strlcat(fmtnew, "+F ", sz);
2488 strlcat(fmtnew, "+64W ", sz);
2489 }
2490 }
2491
2492 /* finally add the action script output and the version */
2493 strlcat(fmtnew, "+4<+A+4<+V", sz);
2494 }
2495
2496 void
2497 bar_replace_pad(char *tmp, int *limit, size_t sz)
2498 {
2499 /* special case; no limit given, pad one space, instead */
2500 if (*limit == (int)sz - 1)
2501 *limit = 1;
2502 snprintf(tmp, sz, "%*s", *limit, " ");
2503 }
2504
2505 /* replaces the bar format character sequences (like in tmux(1)) */
2506 char *
2507 bar_replace_seq(char *fmt, char *fmtrep, struct swm_region *r, size_t *offrep,
2508 size_t sz)
2509 {
2510 struct ws_win *w;
2511 char *ptr;
2512 char tmp[SWM_BAR_MAX];
2513 int limit, size, count;
2514 size_t len;
2515
2516 /* reset strlcat(3) buffer */
2517 *tmp = '\0';
2518
2519 /* get number, if any */
2520 fmt++;
2521 size = 0;
2522 if (sscanf(fmt, "%d%n", &limit, &size) != 1)
2523 limit = sizeof tmp - 1;
2524 if (limit <= 0 || limit >= (int)sizeof tmp)
2525 limit = sizeof tmp - 1;
2526
2527 /* there is nothing to replace (ie EOL) */
2528 fmt += size;
2529 if (*fmt == '\0')
2530 return (fmt);
2531
2532 switch (*fmt) {
2533 case '<':
2534 bar_replace_pad(tmp, &limit, sizeof tmp);
2535 break;
2536 case 'A':
2537 snprintf(tmp, sizeof tmp, "%s", bar_ext);
2538 break;
2539 case 'C':
2540 bar_window_class(tmp, sizeof tmp, r);
2541 break;
2542 case 'D':
2543 bar_workspace_name(tmp, sizeof tmp, r);
2544 break;
2545 case 'F':
2546 bar_window_state(tmp, sizeof tmp, r);
2547 break;
2548 case 'I':
2549 snprintf(tmp, sizeof tmp, "%d", r->ws->idx + 1);
2550 break;
2551 case 'M':
2552 count = 0;
2553 TAILQ_FOREACH(w, &r->ws->winlist, entry)
2554 if (ICONIC(w))
2555 ++count;
2556
2557 snprintf(tmp, sizeof tmp, "%d", count);
2558 break;
2559 case 'N':
2560 snprintf(tmp, sizeof tmp, "%d", r->s->idx + 1);
2561 break;
2562 case 'P':
2563 bar_window_class_instance(tmp, sizeof tmp, r);
2564 break;
2565 case 'S':
2566 snprintf(tmp, sizeof tmp, "%s", r->ws->stacker);
2567 break;
2568 case 'T':
2569 bar_window_instance(tmp, sizeof tmp, r);
2570 break;
2571 case 'U':
2572 bar_urgent(tmp, sizeof tmp);
2573 break;
2574 case 'V':
2575 snprintf(tmp, sizeof tmp, "%s", bar_vertext);
2576 break;
2577 case 'W':
2578 bar_window_name(tmp, sizeof tmp, r);
2579 break;
2580 default:
2581 /* unknown character sequence; copy as-is */
2582 snprintf(tmp, sizeof tmp, "+%c", *fmt);
2583 break;
2584 }
2585
2586 len = strlen(tmp);
2587 ptr = tmp;
2588 if ((int)len < limit)
2589 limit = len;
2590 while (limit-- > 0) {
2591 if (*offrep >= sz - 1)
2592 break;
2593 fmtrep[(*offrep)++] = *ptr++;
2594 }
2595
2596 fmt++;
2597 return (fmt);
2598 }
2599
2600 void
2601 bar_replace(char *fmt, char *fmtrep, struct swm_region *r, size_t sz)
2602 {
2603 size_t off;
2604
2605 off = 0;
2606 while (*fmt != '\0') {
2607 if (*fmt != '+') {
2608 /* skip ordinary characters */
2609 if (off >= sz - 1)
2610 break;
2611 fmtrep[off++] = *fmt++;
2612 continue;
2613 }
2614
2615 /* character sequence found; replace it */
2616 fmt = bar_replace_seq(fmt, fmtrep, r, &off, sz);
2617 if (off >= sz - 1)
2618 break;
2619 }
2620
2621 fmtrep[off] = '\0';
2622 }
2623
2624 void
2625 bar_fmt_expand(char *fmtexp, size_t sz)
2626 {
2627 char *fmt = NULL;
2628 size_t len;
2629 struct tm tm;
2630 time_t tmt;
2631
2632 /* start by grabbing the current time and date */
2633 time(&tmt);
2634 localtime_r(&tmt, &tm);
2635
2636 /* figure out what to expand */
2637 if (bar_format != NULL)
2638 fmt = bar_format;
2639 else if (bar_format == NULL && clock_enabled)
2640 fmt = clock_format;
2641 /* if nothing to expand bail out */
2642 if (fmt == NULL) {
2643 *fmtexp = '\0';
2644 return;
2645 }
2646
2647 /* copy as-is, just in case the format shouldn't be expanded below */
2648 strlcpy(fmtexp, fmt, sz);
2649 /* finally pass the string through strftime(3) */
2650 #ifndef SWM_DENY_CLOCK_FORMAT
2651 if ((len = strftime(fmtexp, sz, fmt, &tm)) == 0)
2652 warnx("format too long");
2653 fmtexp[len] = '\0';
2654 #endif
2655 }
2656
2657 /* Redraws a region bar; need to follow with xcb_flush() or focus_flush(). */
2658 void
2659 bar_draw(struct swm_bar *bar)
2660 {
2661 struct swm_region *r;
2662 char fmtexp[SWM_BAR_MAX], fmtnew[SWM_BAR_MAX];
2663 char fmtrep[SWM_BAR_MAX];
2664
2665 /* expand the format by first passing it through strftime(3) */
2666 bar_fmt_expand(fmtexp, sizeof fmtexp);
2667
2668 if (bar == NULL)
2669 return;
2670
2671 r = bar->r;
2672
2673 if (bar_enabled && r->ws->bar_enabled)
2674 xcb_map_window(conn, bar->id);
2675 else {
2676 xcb_unmap_window(conn, bar->id);
2677 return;
2678 }
2679
2680 if (startup_exception)
2681 snprintf(fmtrep, sizeof fmtrep, "total "
2682 "exceptions: %d, first exception: %s",
2683 nr_exceptions,
2684 startup_exception);
2685 else {
2686 bar_fmt(fmtexp, fmtnew, r, sizeof fmtnew);
2687 bar_replace(fmtnew, fmtrep, r, sizeof fmtrep);
2688 }
2689
2690 if (bar_font_legacy)
2691 bar_print_legacy(r, fmtrep);
2692 else
2693 bar_print(r, fmtrep);
2694 }
2695
2696 /*
2697 * Reads external script output; call when stdin is readable.
2698 * Returns 1 if bar_ext was updated; otherwise 0.
2699 */
2700 int
2701 bar_extra_update(void)
2702 {
2703 size_t len;
2704 char b[SWM_BAR_MAX];
2705 bool changed = false;
2706
2707 if (!bar_extra)
2708 return changed;
2709
2710 while (fgets(b, sizeof(b), stdin) != NULL) {
2711 if (bar_enabled) {
2712 len = strlen(b);
2713 if (b[len - 1] == '\n') {
2714 /* Remove newline. */
2715 b[--len] = '\0';
2716
2717 /* "Clear" bar_ext. */
2718 bar_ext[0] = '\0';
2719
2720 /* Flush buffered output. */
2721 strlcpy(bar_ext, bar_ext_buf, sizeof(bar_ext));
2722 bar_ext_buf[0] = '\0';
2723
2724 /* Append new output to bar. */
2725 strlcat(bar_ext, b, sizeof(bar_ext));
2726
2727 changed = true;
2728 } else {
2729 /* Buffer output. */
2730 strlcat(bar_ext_buf, b, sizeof(bar_ext_buf));
2731 }
2732 }
2733 }
2734
2735 if (errno != EAGAIN) {
2736 warn("bar_action failed");
2737 bar_extra_stop();
2738 changed = true;
2739 }
2740
2741 return changed;
2742 }
2743
2744 void
2745 bar_toggle(struct binding *bp, struct swm_region *r, union arg *args)
2746 {
2747 struct swm_region *tmpr;
2748 int i, num_screens;
2749
2750 /* suppress unused warnings since vars are needed */
2751 (void)bp;
2752 (void)r;
2753 (void)args;
2754
2755 DNPRINTF(SWM_D_BAR, "bar_toggle\n");
2756
2757 switch (args->id) {
2758 case SWM_ARG_ID_BAR_TOGGLE_WS:
2759 /* Only change if master switch is enabled. */
2760 if (bar_enabled)
2761 r->ws->bar_enabled = !r->ws->bar_enabled;
2762 else
2763 bar_enabled = r->ws->bar_enabled = true;
2764 break;
2765 case SWM_ARG_ID_BAR_TOGGLE:
2766 bar_enabled = !bar_enabled;
2767 break;
2768 }
2769
2770 /* update bars as necessary */
2771 num_screens = get_screen_count();
2772 for (i = 0; i < num_screens; i++)
2773 TAILQ_FOREACH(tmpr, &screens[i].rl, entry)
2774 if (tmpr->bar) {
2775 if (bar_enabled && tmpr->ws->bar_enabled)
2776 xcb_map_window(conn, tmpr->bar->id);
2777 else
2778 xcb_unmap_window(conn, tmpr->bar->id);
2779 }
2780
2781 /* Restack all regions and redraw bar. */
2782 num_screens = get_screen_count();
2783 for (i = 0; i < num_screens; i++)
2784 TAILQ_FOREACH(tmpr, &screens[i].rl, entry) {
2785 stack(tmpr);
2786 bar_draw(tmpr->bar);
2787 }
2788
2789 focus_flush();
2790 }
2791
2792 void
2793 bar_extra_setup(void)
2794 {
2795 /* do this here because the conf file is in memory */
2796 if (!bar_extra && bar_argv[0]) {
2797 /* launch external status app */
2798 bar_extra = true;
2799 if (pipe(bar_pipe) == -1)
2800 err(1, "pipe error");
2801 socket_setnonblock(bar_pipe[0]);
2802 socket_setnonblock(bar_pipe[1]); /* XXX hmmm, really? */
2803
2804 /* Set stdin to read from the pipe. */
2805 if (dup2(bar_pipe[0], STDIN_FILENO) == -1)
2806 err(1, "dup2");
2807
2808 /* Set stdout to write to the pipe. */
2809 if (dup2(bar_pipe[1], STDOUT_FILENO) == -1)
2810 err(1, "dup2");
2811
2812 if (signal(SIGPIPE, SIG_IGN) == SIG_ERR)
2813 err(1, "could not disable SIGPIPE");
2814 switch (bar_pid = fork()) {
2815 case -1:
2816 err(1, "cannot fork");
2817 break;
2818 case 0: /* child */
2819 close(bar_pipe[0]);
2820 execvp(bar_argv[0], bar_argv);
2821 err(1, "%s external app failed", bar_argv[0]);
2822 break;
2823 default: /* parent */
2824 close(bar_pipe[1]);
2825 break;
2826 }
2827
2828 atexit(kill_bar_extra_atexit);
2829 }
2830 }
2831
2832 void
2833 kill_bar_extra_atexit(void)
2834 {
2835 if (bar_pid)
2836 kill(bar_pid, SIGTERM);
2837 }
2838
2839 bool
2840 isxlfd(char *s)
2841 {
2842 int count = 0;
2843
2844 while ((s = index(s, '-'))) {
2845 ++count;
2846 ++s;
2847 }
2848
2849 return (count == 14);
2850 }
2851
2852 void
2853 fontset_init(void)
2854 {
2855 char *default_string;
2856 char **missing_charsets;
2857 int num_missing_charsets = 0;
2858 int i;
2859
2860 if (bar_fs) {
2861 XFreeFontSet(display, bar_fs);
2862 bar_fs = NULL;
2863 }
2864
2865 DNPRINTF(SWM_D_INIT, "fontset_init: loading bar_fonts: %s\n",
2866 bar_fonts);
2867
2868 bar_fs = XCreateFontSet(display, bar_fonts, &missing_charsets,
2869 &num_missing_charsets, &default_string);
2870
2871 if (num_missing_charsets > 0) {
2872 warnx("Unable to load charset(s):");
2873
2874 for (i = 0; i < num_missing_charsets; ++i)
2875 warnx("%s", missing_charsets[i]);
2876
2877 XFreeStringList(missing_charsets);
2878
2879 if (strcmp(default_string, ""))
2880 warnx("Glyphs from those sets will be replaced "
2881 "by '%s'.", default_string);
2882 else
2883 warnx("Glyphs from those sets won't be drawn.");
2884 }
2885
2886 if (bar_fs == NULL)
2887 errx(1, "Error creating font set structure.");
2888
2889 bar_fs_extents = XExtentsOfFontSet(bar_fs);
2890
2891 bar_height = bar_fs_extents->max_logical_extent.height +
2892 2 * bar_border_width;
2893
2894 if (bar_height < 1)
2895 bar_height = 1;
2896 }
2897
2898 void
2899 xft_init(struct swm_region *r)
2900 {
2901 char *font, *str, *search;
2902 XRenderColor color;
2903
2904 if (bar_font == NULL) {
2905 if ((search = str = strdup(bar_fonts)) == NULL)
2906 errx(1, "insufficient memory.");
2907
2908 while ((font = strsep(&search, ",")) != NULL) {
2909 if (*font == '\0')
2910 continue;
2911
2912 DNPRINTF(SWM_D_INIT, "xft_init: try font %s\n", font);
2913
2914 if (isxlfd(font)) {
2915 bar_font = XftFontOpenXlfd(display, r->s->idx,
2916 font);
2917 } else {
2918 bar_font = XftFontOpenName(display, r->s->idx,
2919 font);
2920 }
2921
2922 if (bar_font == NULL) {
2923 warnx("unable to load font %s", font);
2924 continue;
2925 } else {
2926 DNPRINTF(SWM_D_INIT, "successfully opened "
2927 "font %s\n", font);
2928 break;
2929 }
2930 }
2931 free(str);
2932 }
2933
2934 if (bar_font == NULL)
2935 errx(1, "unable to open a font");
2936
2937 PIXEL_TO_XRENDERCOLOR(r->s->c[SWM_S_COLOR_BAR_FONT].pixel, color);
2938
2939 if (!XftColorAllocValue(display, DefaultVisual(display, r->s->idx),
2940 DefaultColormap(display, r->s->idx), &color, &bar_font_color))
2941 warn("Xft error: unable to allocate color.");
2942
2943 PIXEL_TO_XRENDERCOLOR(r->s->c[SWM_S_COLOR_BAR].pixel, color);
2944
2945 if (!XftColorAllocValue(display, DefaultVisual(display, r->s->idx),
2946 DefaultColormap(display, r->s->idx), &color, &search_font_color))
2947 warn("Xft error: unable to allocate color.");
2948
2949 bar_height = bar_font->height + 2 * bar_border_width;
2950
2951 if (bar_height < 1)
2952 bar_height = 1;
2953 }
2954
2955 void
2956 bar_setup(struct swm_region *r)
2957 {
2958 xcb_screen_t *screen;
2959 uint32_t wa[3];
2960
2961 DNPRINTF(SWM_D_BAR, "bar_setup: screen %d.\n",
2962 r->s->idx);
2963
2964 if ((screen = get_screen(r->s->idx)) == NULL)
2965 errx(1, "ERROR: can't get screen %d.", r->s->idx);
2966
2967 if (r->bar != NULL)
2968 return;
2969
2970 if ((r->bar = calloc(1, sizeof(struct swm_bar))) == NULL)
2971 err(1, "bar_setup: calloc: failed to allocate memory.");
2972
2973 if (bar_font_legacy)
2974 fontset_init();
2975 else
2976 xft_init(r);
2977
2978 r->bar->r = r;
2979 X(r->bar) = X(r);
2980 Y(r->bar) = bar_at_bottom ? (Y(r) + HEIGHT(r) - bar_height) : Y(r);
2981 WIDTH(r->bar) = WIDTH(r) - 2 * bar_border_width;
2982 HEIGHT(r->bar) = bar_height - 2 * bar_border_width;
2983
2984 /* Assume region is unfocused when we create the bar. */
2985 r->bar->id = xcb_generate_id(conn);
2986 wa[0] = r->s->c[SWM_S_COLOR_BAR].pixel;
2987 wa[1] = r->s->c[SWM_S_COLOR_BAR_BORDER_UNFOCUS].pixel;
2988 wa[2] = XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_POINTER_MOTION |
2989 XCB_EVENT_MASK_POINTER_MOTION_HINT;
2990
2991 xcb_create_window(conn, XCB_COPY_FROM_PARENT, r->bar->id, r->s->root,
2992 X(r->bar), Y(r->bar), WIDTH(r->bar), HEIGHT(r->bar),
2993 bar_border_width, XCB_WINDOW_CLASS_INPUT_OUTPUT,
2994 XCB_COPY_FROM_PARENT, XCB_CW_BACK_PIXEL | XCB_CW_BORDER_PIXEL
2995 | XCB_CW_EVENT_MASK, wa);
2996
2997 /* Stack bar window above region window to start. */
2998 wa[0] = r->id;
2999 wa[1] = XCB_STACK_MODE_ABOVE;
3000
3001 xcb_configure_window(conn, r->bar->id, XCB_CONFIG_WINDOW_SIBLING |
3002 XCB_CONFIG_WINDOW_STACK_MODE, wa);
3003
3004 r->bar->buffer = xcb_generate_id(conn);
3005 xcb_create_pixmap(conn, screen->root_depth, r->bar->buffer, r->bar->id,
3006 WIDTH(r->bar), HEIGHT(r->bar));
3007
3008 if (randr_support)
3009 xcb_randr_select_input(conn, r->bar->id,
3010 XCB_RANDR_NOTIFY_MASK_OUTPUT_CHANGE);
3011
3012 if (bar_enabled)
3013 xcb_map_window(conn, r->bar->id);
3014
3015 DNPRINTF(SWM_D_BAR, "bar_setup: win %#x, (x,y) w x h: (%d,%d) "
3016 "%d x %d\n", WINID(r->bar), X(r->bar), Y(r->bar), WIDTH(r->bar),
3017 HEIGHT(r->bar));
3018
3019 bar_extra_setup();
3020 }
3021
3022 void
3023 bar_cleanup(struct swm_region *r)
3024 {
3025 if (r->bar == NULL)
3026 return;
3027 xcb_destroy_window(conn, r->bar->id);
3028 xcb_free_pixmap(conn, r->bar->buffer);
3029 free(r->bar);
3030 r->bar = NULL;
3031 }
3032
3033 void
3034 set_win_state(struct ws_win *win, uint8_t state)
3035 {
3036 uint16_t data[2] = { state, XCB_ATOM_NONE };
3037
3038 DNPRINTF(SWM_D_EVENT, "set_win_state: win %#x, state: %u\n",
3039 WINID(win), state);
3040
3041 if (win == NULL)
3042 return;
3043
3044 xcb_change_property(conn, XCB_PROP_MODE_REPLACE, win->id, a_state,
3045 a_state, 32, 2, data);
3046 }
3047
3048 uint8_t
3049 get_win_state(xcb_window_t w)
3050 {
3051 xcb_get_property_reply_t *r;
3052 xcb_get_property_cookie_t c;
3053 uint32_t result = 0;
3054
3055 c = xcb_get_property(conn, 0, w, a_state, a_state, 0L, 2L);
3056 r = xcb_get_property_reply(conn, c, NULL);
3057 if (r) {
3058 if (r->type == a_state && r->format == 32 && r->length == 2)
3059 result = *((uint32_t *)xcb_get_property_value(r));
3060 free(r);
3061 }
3062
3063 DNPRINTF(SWM_D_MISC, "get_win_state property: win %#x state %u\n", w,
3064 result);
3065 return (result);
3066 }
3067
3068 void
3069 version(struct binding *bp, struct swm_region *r, union arg *args)
3070 {
3071 struct swm_region *tmpr;
3072 int i, num_screens;
3073
3074 /* suppress unused warnings since vars are needed */
3075 (void)bp;
3076 (void)r;
3077 (void)args;
3078
3079 bar_version = !bar_version;
3080 if (bar_version)
3081 snprintf(bar_vertext, sizeof bar_vertext,
3082 "Version: %s Build: %s", SPECTRWM_VERSION, buildstr);
3083 else
3084 strlcpy(bar_vertext, "", sizeof bar_vertext);
3085
3086 num_screens = get_screen_count();
3087 for (i = 0; i < num_screens; i++) {
3088 TAILQ_FOREACH(tmpr, &screens[i].rl, entry) {
3089 bar_draw(tmpr->bar);
3090 xcb_flush(conn);
3091 }
3092 }
3093 }
3094
3095 void
3096 client_msg(struct ws_win *win, xcb_atom_t a, xcb_timestamp_t t)
3097 {
3098 xcb_client_message_event_t ev;
3099 #ifdef SWM_DEBUG
3100 char *name;
3101 #endif
3102
3103 if (win == NULL)
3104 return;
3105 #ifdef SWM_DEBUG
3106 name = get_atom_name(a);
3107 DNPRINTF(SWM_D_EVENT, "client_msg: win %#x, atom: %s(%u), "
3108 "time: %#x\n",
3109 win->id, name, a, t);
3110 free(name);
3111 #endif
3112
3113 bzero(&ev, sizeof ev);
3114 ev.response_type = XCB_CLIENT_MESSAGE;
3115 ev.window = win->id;
3116 ev.type = a_prot;
3117 ev.format = 32;
3118 ev.data.data32[0] = a;
3119 ev.data.data32[1] = t;
3120
3121 xcb_send_event(conn, 0, win->id,
3122 XCB_EVENT_MASK_NO_EVENT, (const char *)&ev);
3123 }
3124
3125 /* synthetic response to a ConfigureRequest when not making a change */
3126 void
3127 config_win(struct ws_win *win, xcb_configure_request_event_t *ev)
3128 {
3129 xcb_configure_notify_event_t ce;
3130
3131 if (win == NULL)
3132 return;
3133
3134 /* send notification of unchanged state. */
3135 bzero(&ce, sizeof(ce));
3136 ce.response_type = XCB_CONFIGURE_NOTIFY;
3137 ce.x = X(win);
3138 ce.y = Y(win);
3139 ce.width = WIDTH(win);
3140 ce.height = HEIGHT(win);
3141 ce.border_width = 0;
3142 ce.override_redirect = 0;
3143
3144 if (ev == NULL) {
3145 /* EWMH */
3146 ce.event = win->id;
3147 ce.window = win->id;
3148 ce.above_sibling = XCB_WINDOW_NONE;
3149 } else {
3150 /* normal */
3151 ce.event = ev->window;
3152 ce.window = ev->window;
3153
3154 /* make response appear more WM_SIZE_HINTS-compliant */
3155 if (win->sh.flags) {
3156 DNPRINTF(SWM_D_MISC, "config_win: hints: win %#x,"
3157 " sh.flags: %u, min: %d x %d, max: %d x %d, inc: "
3158 "%d x %d\n", win->id, win->sh.flags, SH_MIN_W(win),
3159 SH_MIN_H(win), SH_MAX_W(win), SH_MAX_H(win),
3160 SH_INC_W(win), SH_INC_H(win));
3161 }
3162
3163 /* min size */
3164 if (SH_MIN(win)) {
3165 /* the hint may be set... to 0! */
3166 if (SH_MIN_W(win) > 0 && ce.width < SH_MIN_W(win))
3167 ce.width = SH_MIN_W(win);
3168 if (SH_MIN_H(win) > 0 && ce.height < SH_MIN_H(win))
3169 ce.height = SH_MIN_H(win);
3170 }
3171
3172 /* max size */
3173 if (SH_MAX(win)) {
3174 /* may also be advertized as 0 */
3175 if (SH_MAX_W(win) > 0 && ce.width > SH_MAX_W(win))
3176 ce.width = SH_MAX_W(win);
3177 if (SH_MAX_H(win) > 0 && ce.height > SH_MAX_H(win))
3178 ce.height = SH_MAX_H(win);
3179 }
3180
3181 /* resize increment. */
3182 if (SH_INC(win)) {
3183 if (SH_INC_W(win) > 1 && ce.width > SH_INC_W(win))
3184 ce.width -= (ce.width - SH_MIN_W(win)) %
3185 SH_INC_W(win);
3186 if (SH_INC_H(win) > 1 && ce.height > SH_INC_H(win))
3187 ce.height -= (ce.height - SH_MIN_H(win)) %
3188 SH_INC_H(win);
3189 }
3190
3191 /* adjust x and y for requested border_width. */
3192 ce.x += ev->border_width;
3193 ce.y += ev->border_width;
3194
3195 ce.above_sibling = ev->sibling;
3196 }
3197
3198 DNPRINTF(SWM_D_MISC, "config_win: ewmh: %s, win %#x, (x,y) w x h: "
3199 "(%d,%d) %d x %d, border: %d\n", YESNO(ev == NULL), win->id, ce.x,
3200 ce.y, ce.width, ce.height, ce.border_width);
3201
3202 xcb_send_event(conn, 0, win->id, XCB_EVENT_MASK_STRUCTURE_NOTIFY,
3203 (char *)&ce);
3204 }
3205
3206 int
3207 count_win(struct workspace *ws, bool count_transient)
3208 {
3209 struct ws_win *win;
3210 int count = 0;
3211
3212 TAILQ_FOREACH(win, &ws->winlist, entry) {
3213 if (!count_transient && FLOATING(win))
3214 continue;
3215 if (ICONIC(win))
3216 continue;
3217 count++;
3218 }
3219 DNPRINTF(SWM_D_MISC, "count_win: %d\n", count);
3220
3221 return (count);
3222 }
3223
3224 void
3225 quit(struct binding *bp, struct swm_region *r, union arg *args)
3226 {
3227 /* suppress unused warnings since vars are needed */
3228 (void)bp;
3229 (void)r;
3230 (void)args;
3231
3232 DNPRINTF(SWM_D_MISC, "quit\n");
3233 running = 0;
3234 }
3235
3236 void
3237 lower_window(struct ws_win *win)
3238 {
3239 struct ws_win *target = NULL;
3240 struct workspace *ws;
3241
3242 DNPRINTF(SWM_D_EVENT, "lower_window: win %#x\n", WINID(win));
3243
3244 if (win == NULL)
3245 return;
3246
3247 ws = win->ws;
3248
3249 TAILQ_FOREACH(target, &ws->stack, stack_entry) {
3250 if (target == win || ICONIC(target))
3251 continue;
3252 if (ws->cur_layout == &layouts[SWM_MAX_STACK])
3253 break;
3254 if (TRANS(win)) {
3255 if (win->transient == target->transient)
3256 continue;
3257 if (win->transient == target->id)
3258 break;
3259 }
3260 if (FULLSCREEN(target))
3261 continue;
3262 if (FULLSCREEN(win))
3263 break;
3264 if (MAXIMIZED(target))
3265 continue;
3266 if (MAXIMIZED(win))
3267 break;
3268 if (ABOVE(target) || TRANS(target))
3269 continue;
3270 if (ABOVE(win) || TRANS(win))
3271 break;
3272 }
3273
3274 /* Change stack position. */
3275 TAILQ_REMOVE(&ws->stack, win, stack_entry);
3276 if (target)
3277 TAILQ_INSERT_BEFORE(target, win, stack_entry);
3278 else
3279 TAILQ_INSERT_TAIL(&ws->stack, win, stack_entry);
3280
3281 update_win_stacking(win);
3282
3283 #ifdef SWM_DEBUG
3284 if (swm_debug & SWM_D_STACK) {
3285 DPRINTF("=== stacking order (top down) === \n");
3286 TAILQ_FOREACH(target, &ws->stack, stack_entry) {
3287 DPRINTF("win %#x, fs: %s, maximized: %s, above: %s, "
3288 "iconic: %s\n", target->id, YESNO(FULLSCREEN(target)),
3289 YESNO(MAXIMIZED(target)), YESNO(ABOVE(target)),
3290 YESNO(ICONIC(target)));
3291 }
3292 }
3293 #endif
3294 DNPRINTF(SWM_D_EVENT, "lower_window: done\n");
3295 }
3296
3297 void
3298 raise_window(struct ws_win *win)
3299 {
3300 struct ws_win *target = NULL;
3301 struct workspace *ws;
3302
3303 DNPRINTF(SWM_D_EVENT, "raise_window: win %#x\n", WINID(win));
3304
3305 if (win == NULL)
3306 return;
3307
3308 ws = win->ws;
3309
3310 TAILQ_FOREACH(target, &ws->stack, stack_entry) {
3311 if (target == win || ICONIC(target))
3312 continue;
3313 if (ws->cur_layout == &layouts[SWM_MAX_STACK])
3314 break;
3315 if (TRANS(win) && (win->transient == target->transient ||
3316 win->transient == target->id))
3317 break;
3318 if (FULLSCREEN(win))
3319 break;
3320 if (FULLSCREEN(target))
3321 continue;
3322 if (MAXIMIZED(win))
3323 break;
3324 if (MAXIMIZED(target))
3325 continue;
3326 if (ABOVE(win) || TRANS(win) ||
3327 (win->ws->focus == win && ws->always_raise))
3328 break;
3329 if (!ABOVE(target) && !TRANS(target))
3330 break;
3331 }
3332
3333 TAILQ_REMOVE(&ws->stack, win, stack_entry);
3334 if (target)
3335 TAILQ_INSERT_BEFORE(target, win, stack_entry);
3336 else
3337 TAILQ_INSERT_TAIL(&ws->stack, win, stack_entry);
3338
3339 update_win_stacking(win);
3340
3341 #ifdef SWM_DEBUG
3342 if (swm_debug & SWM_D_STACK) {
3343 DPRINTF("=== stacking order (top down) === \n");
3344 TAILQ_FOREACH(target, &ws->stack, stack_entry) {
3345 DPRINTF("win %#x, fs: %s, maximized: %s, above: %s, "
3346 "iconic: %s\n", target->id, YESNO(FULLSCREEN(target)),
3347 YESNO(MAXIMIZED(target)), YESNO(ABOVE(target)),
3348 YESNO(ICONIC(target)));
3349 }
3350 }
3351 #endif
3352 DNPRINTF(SWM_D_EVENT, "raise_window: done\n");
3353 }
3354
3355 void
3356 update_win_stacking(struct ws_win *win)
3357 {
3358 struct ws_win *sibling;
3359 #ifdef SWM_DEBUG
3360 struct ws_win *w;
3361 #endif
3362 struct swm_region *r;
3363 uint32_t val[2];
3364
3365 if (win == NULL || (r = win->ws->r) == NULL)
3366 return;
3367
3368 if (win->frame == XCB_WINDOW_NONE) {
3369 DNPRINTF(SWM_D_EVENT, "update_window_stacking: win %#x not "
3370 "reparented.\n", win->id);
3371 return;
3372 }
3373
3374 sibling = TAILQ_NEXT(win, stack_entry);
3375 if (sibling != NULL && (FLOATING(win) == FLOATING(sibling) ||
3376 (win->ws->always_raise && win->ws->focus == win)))
3377 val[0] = sibling->frame;
3378 else if (FLOATING(win) || (win->ws->always_raise &&
3379 win->ws->focus == win))
3380 val[0] = r->bar->id;
3381 else
3382 val[0] = r->id;
3383
3384 DNPRINTF(SWM_D_EVENT, "update_win_stacking: win %#x (%#x), "
3385 "sibling %#x\n", win->frame, win->id, val[0]);
3386
3387 val[1] = XCB_STACK_MODE_ABOVE;
3388
3389 xcb_configure_window(conn, win->frame, XCB_CONFIG_WINDOW_SIBLING |
3390 XCB_CONFIG_WINDOW_STACK_MODE, val);
3391
3392 #ifdef SWM_DEBUG
3393 TAILQ_FOREACH(w, &win->ws->winlist, entry)
3394 debug_refresh(w);
3395 #endif
3396 }
3397
3398 void
3399 map_window(struct ws_win *win)
3400 {
3401 if (win == NULL)
3402 return;
3403
3404 DNPRINTF(SWM_D_EVENT, "map_window: win %#x, mapped: %s\n",
3405 win->id, YESNO(win->mapped));
3406
3407 if (win->mapped)
3408 return;
3409
3410 xcb_map_window(conn, win->frame);
3411 xcb_map_window(conn, win->id);
3412 set_win_state(win, XCB_ICCCM_WM_STATE_NORMAL);
3413 win->mapped = true;
3414 }
3415
3416 void
3417 unmap_window(struct ws_win *win)
3418 {
3419 if (win == NULL)
3420 return;
3421
3422 DNPRINTF(SWM_D_EVENT, "unmap_window: win %#x, mapped: %s\n", win->id,
3423 YESNO(win->mapped));
3424
3425 if (!win->mapped)
3426 return;
3427
3428 xcb_unmap_window(conn, win->id);
3429 xcb_unmap_window(conn, win->frame);
3430 set_win_state(win, XCB_ICCCM_WM_STATE_ICONIC);
3431 win->mapped = false;
3432 }
3433
3434 void
3435 unmap_all(void)
3436 {
3437 struct ws_win *win;
3438 int i, j, num_screens;
3439
3440 num_screens = get_screen_count();
3441 for (i = 0; i < num_screens; i++)
3442 for (j = 0; j < workspace_limit; j++)
3443 TAILQ_FOREACH(win, &screens[i].ws[j].winlist, entry)
3444 unmap_window(win);
3445 }
3446
3447 void
3448 fake_keypress(struct ws_win *win, xcb_keysym_t keysym, uint16_t modifiers)
3449 {
3450 xcb_key_press_event_t event;
3451 xcb_keycode_t *keycode;
3452
3453 if (win == NULL)
3454 return;
3455
3456 keycode = xcb_key_symbols_get_keycode(syms, keysym);
3457
3458 DNPRINTF(SWM_D_MISC, "fake_keypress: win %#x, keycode %u\n",
3459 win->id, *keycode);
3460
3461 bzero(&event, sizeof(event));
3462 event.event = win->id;
3463 event.root = win->s->root;
3464 event.child = XCB_WINDOW_NONE;
3465 event.time = XCB_CURRENT_TIME;
3466 event.event_x = X(win);
3467 event.event_y = Y(win);
3468 event.root_x = 1;
3469 event.root_y = 1;
3470 event.same_screen = 1;
3471 event.detail = *keycode;
3472 event.state = modifiers;
3473
3474 event.response_type = XCB_KEY_PRESS;
3475 xcb_send_event(conn, 1, win->id,
3476 XCB_EVENT_MASK_KEY_PRESS, (const char *)&event);
3477
3478 event.response_type = XCB_KEY_RELEASE;
3479 xcb_send_event(conn, 1, win->id,
3480 XCB_EVENT_MASK_KEY_RELEASE, (const char *)&event);
3481
3482 free(keycode);
3483 }
3484
3485 void
3486 restart(struct binding *bp, struct swm_region *r, union arg *args)
3487 {
3488 /* suppress unused warning since var is needed */
3489 (void)bp;
3490 (void)r;
3491 (void)args;
3492
3493 DNPRINTF(SWM_D_MISC, "restart: %s\n", start_argv[0]);
3494
3495 shutdown_cleanup();
3496
3497 execvp(start_argv[0], start_argv);
3498 warn("execvp failed");
3499 quit(NULL, NULL, NULL);
3500 }
3501
3502 struct ws_win *
3503 get_pointer_win(xcb_window_t root)
3504 {
3505 struct ws_win *win = NULL;
3506 xcb_query_pointer_reply_t *r;
3507
3508 DNPRINTF(SWM_D_EVENT, "get_pointer_win: root: %#x.\n", root);
3509
3510 r = xcb_query_pointer_reply(conn, xcb_query_pointer(conn, root), NULL);
3511 if (r) {
3512 win = find_window(r->child);
3513 if (win) {
3514 DNPRINTF(SWM_D_EVENT, "get_pointer_win: %#x.\n",
3515 win->id);
3516 } else {
3517 DNPRINTF(SWM_D_EVENT, "get_pointer_win: none.\n");
3518 }
3519 free(r);
3520 }
3521
3522 return win;
3523 }
3524
3525 void
3526 center_pointer(struct swm_region *r)
3527 {
3528 struct ws_win *win;
3529
3530 if (!warp_pointer || r == NULL)
3531 return;
3532
3533 win = r->ws->focus;
3534
3535 DNPRINTF(SWM_D_EVENT, "center_pointer: win %#x.\n", WINID(win));
3536
3537 if (win && win->mapped)
3538 xcb_warp_pointer(conn, XCB_NONE, win->frame, 0, 0, 0, 0,
3539 WIDTH(win) / 2, HEIGHT(win) / 2);
3540 else
3541 xcb_warp_pointer(conn, XCB_NONE, r->id, 0, 0, 0, 0,
3542 WIDTH(r) / 2, HEIGHT(r) / 2);
3543 }
3544
3545 struct swm_region *
3546 root_to_region(xcb_window_t root, int check)
3547 {
3548 struct ws_win *cfw;
3549 struct swm_region *r = NULL;
3550 int i, num_screens;
3551 xcb_query_pointer_reply_t *qpr;
3552 xcb_get_input_focus_reply_t *gifr;
3553
3554 DNPRINTF(SWM_D_MISC, "root_to_region: win %#x\n", root);
3555
3556 num_screens = get_screen_count();
3557 for (i = 0; i < num_screens; i++)
3558 if (screens[i].root == root)
3559 break;
3560
3561 if (check & SWM_CK_REGION)
3562 r = screens[i].r_focus;
3563
3564 if (r == NULL && check & SWM_CK_FOCUS) {
3565 /* Try to find an actively focused window */
3566 gifr = xcb_get_input_focus_reply(conn,
3567 xcb_get_input_focus(conn), NULL);
3568 if (gifr) {
3569 cfw = find_window(gifr->focus);
3570 if (cfw && cfw->ws->r)
3571 r = cfw->ws->r;
3572
3573 free(gifr);
3574 }
3575 }
3576
3577 if (r == NULL && check & SWM_CK_POINTER) {
3578 /* No region with an active focus; try to use pointer. */
3579 qpr = xcb_query_pointer_reply(conn, xcb_query_pointer(conn,
3580 screens[i].root), NULL);
3581 if (qpr) {
3582 DNPRINTF(SWM_D_MISC, "root_to_region: pointer: "
3583 "(%d,%d)\n", qpr->root_x, qpr->root_y);
3584 TAILQ_FOREACH(r, &screens[i].rl, entry)
3585 if (X(r) <= qpr->root_x &&
3586 qpr->root_x < MAX_X(r) &&
3587 Y(r) <= qpr->root_y &&
3588 qpr->root_y < MAX_Y(r))
3589 break;
3590 free(qpr);
3591 }
3592 }
3593
3594 /* Last resort. */
3595 if (r == NULL && check & SWM_CK_FALLBACK)
3596 r = TAILQ_FIRST(&screens[i].rl);
3597
3598 DNPRINTF(SWM_D_MISC, "root_to_region: idx: %d\n", get_region_index(r));
3599
3600 return (r);
3601 }
3602
3603 struct swm_region *
3604 find_region(xcb_window_t id)
3605 {
3606 struct swm_region *r;
3607 int i, num_screens;
3608
3609 num_screens = get_screen_count();
3610 for (i = 0; i < num_screens; i++)
3611 TAILQ_FOREACH(r, &screens[i].rl, entry)
3612 if (r->id == id)
3613 return r;
3614
3615 return NULL;
3616 }
3617
3618 struct swm_bar *
3619 find_bar(xcb_window_t id)
3620 {
3621 struct swm_region *r;
3622 int i, num_screens;
3623
3624 num_screens = get_screen_count();
3625 for (i = 0; i < num_screens; i++)
3626 TAILQ_FOREACH(r, &screens[i].rl, entry)
3627 if (r->bar && r->bar->id == id)
3628 return r->bar;
3629
3630 return NULL;
3631 }
3632
3633 struct ws_win *
3634 find_frame_window(xcb_window_t id) {
3635 struct swm_region *r;
3636 struct ws_win *w;
3637 int i, num_screens;
3638
3639 num_screens = get_screen_count();
3640 for (i = 0; i < num_screens; i++)
3641 TAILQ_FOREACH(r, &screens[i].rl, entry)
3642 TAILQ_FOREACH(w, &r->ws->winlist, entry)
3643 if (w->frame == id)
3644 return w;
3645
3646 return NULL;
3647 }
3648
3649 struct ws_win *
3650 find_window(xcb_window_t id)
3651 {
3652 struct ws_win *win = NULL;
3653 int i, j, num_screens;
3654 xcb_query_tree_reply_t *qtr;
3655
3656 DNPRINTF(SWM_D_MISC, "find_window: id: %#x\n", id);
3657
3658 num_screens = get_screen_count();
3659 for (i = 0; i < num_screens; i++)
3660 for (j = 0; j < workspace_limit; j++)
3661 TAILQ_FOREACH(win, &screens[i].ws[j].winlist, entry)
3662 if (id == win->id || id == win->frame)
3663 return (win);
3664
3665
3666 /* If window isn't top-level, try to find managed ancestor. */
3667 qtr = xcb_query_tree_reply(conn, xcb_query_tree(conn, id), NULL);
3668 if (qtr) {
3669 if (qtr->parent != XCB_WINDOW_NONE && qtr->parent != qtr->root)
3670 win = find_window(qtr->parent);
3671
3672 #ifdef SWM_DEBUG
3673 if (win)
3674 DNPRINTF(SWM_D_MISC, "find_window: found child %#x "
3675 "of %#x.\n", win->id, qtr->parent);
3676 #endif
3677
3678 free(qtr);
3679 }
3680
3681 return (win);
3682 }
3683
3684 struct ws_win *
3685 find_unmanaged_window(xcb_window_t id)
3686 {
3687 struct ws_win *win;
3688 int i, j, num_screens;
3689
3690 num_screens = get_screen_count();
3691 for (i = 0; i < num_screens; i++)
3692 for (j = 0; j < workspace_limit; j++)
3693 TAILQ_FOREACH(win, &screens[i].ws[j].unmanagedlist,
3694 entry)
3695 if (id == win->id)
3696 return (win);
3697 return (NULL);
3698 }
3699
3700 void
3701 spawn(int ws_idx, union arg *args, bool close_fd)
3702 {
3703 int fd;
3704 char *ret = NULL;
3705
3706 DNPRINTF(SWM_D_MISC, "spawn: %s\n", args->argv[0]);
3707
3708 close(xcb_get_file_descriptor(conn));
3709
3710 setenv("LD_PRELOAD", SWM_LIB, 1);
3711
3712 if (asprintf(&ret, "%d", ws_idx) == -1) {
3713 warn("spawn: asprintf SWM_WS");
3714 _exit(1);
3715 }
3716 setenv("_SWM_WS", ret, 1);
3717 free(ret);
3718 ret = NULL;
3719
3720 if (asprintf(&ret, "%d", getpid()) == -1) {
3721 warn("spawn: asprintf _SWM_PID");
3722 _exit(1);
3723 }
3724 setenv("_SWM_PID", ret, 1);
3725 free(ret);
3726 ret = NULL;
3727
3728 if (setsid() == -1) {
3729 warn("spawn: setsid");
3730 _exit(1);
3731 }
3732
3733 if (close_fd) {
3734 /*
3735 * close stdin and stdout to prevent interaction between apps
3736 * and the baraction script
3737 * leave stderr open to record errors
3738 */
3739 if ((fd = open(_PATH_DEVNULL, O_RDWR, 0)) == -1) {
3740 warn("spawn: open");
3741 _exit(1);
3742 }
3743 dup2(fd, STDIN_FILENO);
3744 dup2(fd, STDOUT_FILENO);
3745 if (fd > 2)
3746 close(fd);
3747 }
3748
3749 execvp(args->argv[0], args->argv);
3750
3751 warn("spawn: execvp");
3752 _exit(1);
3753 }
3754
3755 void
3756 kill_refs(struct ws_win *win)
3757 {
3758 struct workspace *ws;
3759 struct ws_win *w;
3760 int i, j, num_screens;
3761
3762 if (win == NULL)
3763 return;
3764
3765 num_screens = get_screen_count();
3766 for (i = 0; i < num_screens; i++) {
3767 for (j = 0; j < workspace_limit; j++) {
3768 ws = &screens[i].ws[j];
3769
3770 if (win == ws->focus)
3771 ws->focus = NULL;
3772 if (win == ws->focus_prev)
3773 ws->focus_prev = NULL;
3774 if (win == ws->focus_pending)
3775 ws->focus_pending = NULL;
3776 if (win == ws->raised)
3777 ws->raised = NULL;
3778
3779 if (TRANS(win))
3780 TAILQ_FOREACH(w, &ws->winlist, entry)
3781 if (win == w->focus_child)
3782 w->focus_child = NULL;
3783 }
3784 }
3785 }
3786
3787 int
3788 validate_win(struct ws_win *testwin)
3789 {
3790 struct ws_win *win;
3791 struct workspace *ws;
3792 struct swm_region *r;
3793 int i, x, num_screens;
3794
3795 if (testwin == NULL)
3796 return (0);
3797
3798 num_screens = get_screen_count();
3799 for (i = 0; i < num_screens; i++)
3800 TAILQ_FOREACH(r, &screens[i].rl, entry)
3801 for (x = 0; x < workspace_limit; x++) {
3802 ws = &r->s->ws[x];
3803 TAILQ_FOREACH(win, &ws->winlist, entry)
3804 if (win == testwin)
3805 return (0);
3806 }
3807 return (1);
3808 }
3809
3810 int
3811 validate_ws(struct workspace *testws)
3812 {
3813 struct swm_region *r;
3814 struct workspace *ws;
3815 int i, x, num_screens;
3816
3817 /* validate all ws */
3818 num_screens = get_screen_count();
3819 for (i = 0; i < num_screens; i++)
3820 TAILQ_FOREACH(r, &screens[i].rl, entry)
3821 for (x = 0; x < workspace_limit; x++) {
3822 ws = &r->s->ws[x];
3823 if (ws == testws)
3824 return (0);
3825 }
3826 return (1);
3827 }
3828
3829 void
3830 unfocus_win(struct ws_win *win)
3831 {
3832 xcb_window_t none = XCB_WINDOW_NONE;
3833
3834 DNPRINTF(SWM_D_FOCUS, "unfocus_win: win %#x\n", WINID(win));
3835
3836 if (win == NULL)
3837 return;
3838
3839 if (win->ws == NULL) {
3840 DNPRINTF(SWM_D_FOCUS, "unfocus_win: NULL ws.\n");
3841 return;
3842 }
3843
3844 if (validate_ws(win->ws)) {
3845 DNPRINTF(SWM_D_FOCUS, "unfocus_win: invalid ws.\n");
3846 return;
3847 }
3848
3849 if (win->ws->r == NULL) {
3850 DNPRINTF(SWM_D_FOCUS, "unfocus_win: NULL region.\n");
3851 return;
3852 }
3853
3854 if (validate_win(win)) {
3855 DNPRINTF(SWM_D_FOCUS, "unfocus_win: invalid win.\n");
3856 kill_refs(win);
3857 return;
3858 }
3859
3860 if (win->ws->focus == win) {
3861 win->ws->focus = NULL;
3862 win->ws->focus_prev = win;
3863 if(win->ws->raised == win && !FLOATING(win)) {
3864 update_win_stacking(win);
3865 }
3866 }
3867
3868 if (validate_win(win->ws->focus)) {
3869 kill_refs(win->ws->focus);
3870 win->ws->focus = NULL;
3871 }
3872
3873 if (validate_win(win->ws->focus_prev)) {
3874 kill_refs(win->ws->focus_prev);
3875 win->ws->focus_prev = NULL;
3876 }
3877
3878 draw_frame(win);
3879
3880 /* Raise window to "top unfocused position." */
3881 if (win->ws->always_raise)
3882 raise_window(win);
3883
3884 /* Update border width */
3885 if (win->bordered && (win->quirks & SWM_Q_MINIMALBORDER) &&
3886 FLOATING(win)) {
3887 win->bordered = 0;
3888 X(win) += border_width;
3889 Y(win) += border_width;
3890 update_window(win);
3891 }
3892
3893 xcb_change_property(conn, XCB_PROP_MODE_REPLACE, win->s->root,
3894 ewmh[_NET_ACTIVE_WINDOW].atom, XCB_ATOM_WINDOW, 32, 1, &none);
3895
3896 DNPRINTF(SWM_D_FOCUS, "unfocus_win: done.\n");
3897 }
3898
3899 void
3900 focus_win(struct ws_win *win)
3901 {
3902 struct ws_win *cfw = NULL, *parent = NULL, *w, *tmpw;
3903 struct workspace *ws;
3904 xcb_get_input_focus_reply_t *gifr = NULL;
3905 xcb_get_window_attributes_reply_t *war = NULL;
3906
3907 DNPRINTF(SWM_D_FOCUS, "focus_win: win %#x\n", WINID(win));
3908
3909 if (win == NULL || win->ws == NULL || !win->mapped)
3910 goto out;
3911
3912 ws = win->ws;
3913
3914 if (validate_ws(ws))
3915 goto out;
3916
3917 if (validate_win(win)) {
3918 kill_refs(win);
3919 goto out;
3920 }
3921
3922 gifr = xcb_get_input_focus_reply(conn, xcb_get_input_focus(conn), NULL);
3923 if (gifr) {
3924 DNPRINTF(SWM_D_FOCUS, "focus_win: cur focus: %#x\n",
3925 gifr->focus);
3926
3927 cfw = find_window(gifr->focus);
3928 if (cfw) {
3929 if (cfw != win) {
3930 if (cfw->ws != ws && cfw->ws->r != NULL &&
3931 cfw->frame != XCB_WINDOW_NONE) {
3932 draw_frame(cfw);
3933 } else {
3934 unfocus_win(cfw);
3935 }
3936 }
3937 } else {
3938 war = xcb_get_window_attributes_reply(conn,
3939 xcb_get_window_attributes(conn, gifr->focus), NULL);
3940 if (war && war->override_redirect && ws->focus == win) {
3941 DNPRINTF(SWM_D_FOCUS, "focus_win: skip refocus "
3942 "from override_redirect.\n");
3943 goto out;
3944 }
3945 }
3946 }
3947
3948 if (ws->focus != win) {
3949 if (ws->focus && ws->focus != cfw)
3950 unfocus_win(ws->focus);
3951 ws->focus = win;
3952 }
3953
3954 /* Clear focus child redirect. */
3955 win->focus_child = NULL;
3956
3957 /* If transient, adjust parent's focus child for focus_magic. */
3958 if (TRANS(win)) {
3959 parent = find_window(win->transient);
3960 if (parent && parent->focus_child != win)
3961 parent->focus_child = win;
3962 }
3963
3964 /* Update window border even if workspace is hidden. */
3965 draw_frame(win);
3966
3967 if (cfw == win) {
3968 DNPRINTF(SWM_D_FOCUS, "focus_win: already focused.\n");
3969 goto out;
3970 }
3971
3972 if (ws->r) {
3973 /* Set input focus if no input hint, or indicated by hint. */
3974 if (ACCEPTS_FOCUS(win)) {
3975 DNPRINTF(SWM_D_FOCUS, "focus_win: set_input_focus: %#x,"
3976 " revert-to: parent, time: %#x\n", win->id,
3977 last_event_time);
3978 xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT,
3979 win->id, last_event_time);
3980 } else if (!win->take_focus) {
3981 DNPRINTF(SWM_D_FOCUS, "focus_win: set_input_focus: %#x,"
3982 " revert-to: parent, time: 0\n", ws->r->id);
3983 xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT,
3984 ws->r->id, XCB_CURRENT_TIME);
3985 }
3986
3987 /* Tell app it can adjust focus to a specific window. */
3988 if (win->take_focus) {
3989 /* java is special; always tell parent */
3990 if (TRANS(win) && win->java)
3991 client_msg(parent, a_takefocus,
3992 last_event_time);
3993 else
3994 client_msg(win, a_takefocus, last_event_time);
3995 }
3996
3997 if (ws->cur_layout->flags & SWM_L_MAPONFOCUS ||
3998 ws->always_raise) {
3999 /* If a parent exists, map it first. */
4000 if (parent) {
4001 raise_window(parent);
4002 map_window(parent);
4003
4004 /* Map siblings next. */
4005 TAILQ_FOREACH_SAFE(w, &ws->stack, stack_entry,
4006 tmpw)
4007 if (w != win && !ICONIC(w) &&
4008 w->transient == parent->id) {
4009 raise_window(w);
4010 map_window(w);
4011 }
4012 }
4013
4014 /* Map focused window. */
4015 raise_window(win);
4016 map_window(win);
4017
4018 /* Stack any children of focus window. */
4019 TAILQ_FOREACH_SAFE(w, &ws->stack, stack_entry, tmpw)
4020 if (w->transient == win->id && !ICONIC(w)) {
4021 raise_window(w);
4022 map_window(w);
4023 }
4024 } else if (tile_gap < 0 && !ABOVE(win)) {
4025 /*
4026 * Windows overlap in the layout.
4027 * Raise focused win above all tiled wins.
4028 */
4029 raise_window(win);
4030 map_window(win);
4031 }
4032
4033 set_region(ws->r);
4034
4035 xcb_change_property(conn, XCB_PROP_MODE_REPLACE, win->s->root,
4036 ewmh[_NET_ACTIVE_WINDOW].atom, XCB_ATOM_WINDOW, 32, 1,
4037 &win->id);
4038
4039 bar_draw(ws->r->bar);
4040 }
4041
4042 out:
4043 free(gifr);
4044 free(war);
4045 DNPRINTF(SWM_D_FOCUS, "focus_win: done.\n");
4046 }
4047
4048 /* If a child window should have focus instead, return it. */
4049 struct ws_win *
4050 get_focus_magic(struct ws_win *win)
4051 {
4052 struct ws_win *parent = NULL;
4053 struct ws_win *child = NULL;
4054
4055 DNPRINTF(SWM_D_FOCUS, "get_focus_magic: win %#x\n", WINID(win));
4056 if (win == NULL)
4057 return win;
4058
4059 if (TRANS(win)) {
4060 parent = find_window(win->transient);
4061
4062 /* If parent prefers focus elsewhere, then try to do so. */
4063 if (parent && (child = parent->focus_child)) {
4064 if (validate_win(child) == 0 && child->mapped)
4065 win = child;
4066 else
4067 parent->focus_child = NULL;
4068 }
4069 }
4070
4071 /* If this window prefers focus elsewhere, then try to do so. */
4072 if ((child = win->focus_child)) {
4073 if (validate_win(child) == 0 && child->mapped)
4074 win = child;
4075 else
4076 win->focus_child = NULL;
4077 }
4078
4079 return win;
4080 }
4081
4082 void
4083 event_drain(uint8_t rt)
4084 {
4085 xcb_generic_event_t *evt;
4086
4087 /* ensure all pending requests have been processed before filtering. */
4088 xcb_aux_sync(conn);
4089 while ((evt = get_next_event(false))) {
4090 if (XCB_EVENT_RESPONSE_TYPE(evt) != rt)
4091 event_handle(evt);
4092
4093 free(evt);
4094 }
4095 }
4096
4097 void
4098 set_region(struct swm_region *r)
4099 {
4100 struct swm_region *rf;
4101 int vals[2];
4102
4103 if (r == NULL)
4104 return;
4105
4106 rf = r->s->r_focus;
4107 /* Unfocus old region bar. */
4108 if (rf != NULL) {
4109 if (rf == r)
4110 return;
4111
4112 xcb_change_window_attributes(conn, rf->bar->id,
4113 XCB_CW_BORDER_PIXEL,
4114 &r->s->c[SWM_S_COLOR_BAR_BORDER_UNFOCUS].pixel);
4115 }
4116
4117 if (rf != NULL && rf != r && (X(rf) != X(r) || Y(rf) != Y(r) ||
4118 WIDTH(rf) != WIDTH(r) || HEIGHT(rf) != HEIGHT(r))) {
4119 /* Set _NET_DESKTOP_GEOMETRY. */
4120 vals[0] = WIDTH(r);
4121 vals[1] = HEIGHT(r);
4122 xcb_change_property(conn, XCB_PROP_MODE_REPLACE, r->s->root,
4123 ewmh[_NET_DESKTOP_GEOMETRY].atom, XCB_ATOM_CARDINAL, 32, 2,
4124 &vals);
4125 }
4126
4127 /* Set region bar border to focus_color. */
4128 xcb_change_window_attributes(conn, r->bar->id,
4129 XCB_CW_BORDER_PIXEL, &r->s->c[SWM_S_COLOR_BAR_BORDER].pixel);
4130
4131 r->s->r_focus = r;
4132
4133 ewmh_update_current_desktop();
4134 }
4135
4136 void
4137 focus_region(struct swm_region *r)
4138 {
4139 struct ws_win *nfw;
4140 struct swm_region *old_r;
4141
4142 if (r == NULL)
4143 return;
4144
4145 old_r = r->s->r_focus;
4146 set_region(r);
4147
4148 nfw = get_region_focus(r);
4149 if (nfw) {
4150 focus_win(nfw);
4151 } else {
4152 /* New region is empty; need to manually unfocus win. */
4153 if (old_r) {
4154 unfocus_win(old_r->ws->focus);
4155 /* Clear bar since empty. */
4156 bar_draw(old_r->bar);
4157 }
4158
4159 DNPRINTF(SWM_D_FOCUS, "focus_region: set_input_focus: %#x, "
4160 "revert-to: parent, time: 0\n", r->id);
4161 xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT, r->id,
4162 XCB_CURRENT_TIME);
4163
4164 }
4165 }
4166
4167 void
4168 switchws(struct binding *bp, struct swm_region *r, union arg *args)
4169 {
4170 struct swm_region *this_r, *other_r;
4171 struct ws_win *win;
4172 struct workspace *new_ws, *old_ws;
4173 xcb_window_t none = XCB_WINDOW_NONE;
4174 int wsid = args->id;
4175 bool unmap_old = false;
4176
4177 if (!(r && r->s))
4178 return;
4179
4180 if (wsid >= workspace_limit)
4181 return;
4182
4183 this_r = r;
4184 old_ws = this_r->ws;
4185 new_ws = &this_r->s->ws[wsid];
4186
4187 DNPRINTF(SWM_D_WS, "switchws: screen[%d]:%dx%d+%d+%d: %d -> %d\n",
4188 r->s->idx, WIDTH(r), HEIGHT(r), X(r), Y(r), old_ws->idx, wsid);
4189
4190 if (new_ws == NULL || old_ws == NULL)
4191 return;
4192 if (new_ws == old_ws)
4193 return;
4194
4195 other_r = new_ws->r;
4196 if (other_r && workspace_clamp &&
4197 bp->action != FN_RG_MOVE_NEXT && bp->action != FN_RG_MOVE_PREV) {
4198 DNPRINTF(SWM_D_WS, "switchws: ws clamped.\n");
4199
4200 if (warp_focus) {
4201 DNPRINTF(SWM_D_WS, "switchws: warping focus to region "
4202 "with ws %d.\n", wsid);
4203 focus_region(other_r);
4204 center_pointer(other_r);
4205 focus_flush();
4206 }
4207 return;
4208 }
4209
4210 if ((win = old_ws->focus) != NULL) {
4211 draw_frame(win);
4212
4213 xcb_change_property(conn, XCB_PROP_MODE_REPLACE, win->s->root,
4214 ewmh[_NET_ACTIVE_WINDOW].atom, XCB_ATOM_WINDOW, 32, 1,
4215 &none);
4216 }
4217
4218 if (other_r) {
4219 /* the other ws is visible in another region, exchange them */
4220 other_r->ws_prior = new_ws;
4221 other_r->ws = old_ws;
4222 old_ws->r = other_r;
4223 } else {
4224 /* the other workspace is hidden, hide this one */
4225 old_ws->r = NULL;
4226 unmap_old = true;
4227 }
4228
4229 this_r->ws_prior = old_ws;
4230 this_r->ws = new_ws;
4231 new_ws->r = this_r;
4232
4233 /* Set focus_pending before stacking, if needed. */
4234 if (focus_mode != SWM_FOCUS_FOLLOW && (!new_ws->focus_pending ||
4235 validate_win(new_ws->focus_pending))) {
4236 new_ws->focus_pending = get_region_focus(new_ws->r);
4237 new_ws->focus = new_ws->focus_prev;
4238 new_ws->focus_prev = NULL;
4239 }
4240
4241 new_ws->state = SWM_WS_STATE_MAPPING;
4242
4243 stack(other_r);
4244 stack(this_r);
4245
4246 /* unmap old windows */
4247 if (unmap_old) {
4248 TAILQ_FOREACH(win, &old_ws->winlist, entry)
4249 unmap_window(win);
4250 old_ws->state = SWM_WS_STATE_HIDDEN;
4251 }
4252
4253 /* if workspaces were swapped, then don't wait to set focus */
4254 if (old_ws->r && focus_mode != SWM_FOCUS_FOLLOW) {
4255 if (new_ws->focus_pending) {
4256 focus_win(new_ws->focus_pending);
4257 new_ws->focus_pending = NULL;
4258 }
4259 }
4260
4261 /* Clear bar and set focus on region input win if new ws is empty. */
4262 if (new_ws->focus_pending == NULL && new_ws->focus == NULL) {
4263 DNPRINTF(SWM_D_FOCUS, "switchws: set_input_focus: %#x, "
4264 "revert-to: parent, time: 0\n", r->id);
4265 xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT, r->id,
4266 XCB_CURRENT_TIME);
4267 bar_draw(r->bar);
4268 }
4269
4270 ewmh_update_current_desktop();
4271
4272 center_pointer(r);
4273 focus_flush();
4274 new_ws->state = SWM_WS_STATE_MAPPED;
4275
4276 DNPRINTF(SWM_D_WS, "switchws: done.\n");
4277 }
4278
4279 void
4280 cyclews(struct binding *bp, struct swm_region *r, union arg *args)
4281 {
4282 union arg a;
4283 struct swm_screen *s = r->s;
4284 bool cycle_all = false, mv = false;
4285
4286 DNPRINTF(SWM_D_WS, "cyclews: id: %d, screen[%d]:%dx%d+%d+%d, ws: %d\n",
4287 args->id, r->s->idx, WIDTH(r), HEIGHT(r), X(r), Y(r), r->ws->idx);
4288
4289 a.id = r->ws->idx;
4290
4291 do {
4292 switch (args->id) {
4293 case SWM_ARG_ID_CYCLEWS_MOVE_UP:
4294 mv = true;
4295 /* FALLTHROUGH */
4296 case SWM_ARG_ID_CYCLEWS_UP_ALL:
4297 cycle_all = true;
4298 /* FALLTHROUGH */
4299 case SWM_ARG_ID_CYCLEWS_UP:
4300 a.id = (a.id < workspace_limit - 1) ? a.id + 1 : 0;
4301 break;
4302 case SWM_ARG_ID_CYCLEWS_MOVE_DOWN:
4303 mv = true;
4304 /* FALLTHROUGH */
4305 case SWM_ARG_ID_CYCLEWS_DOWN_ALL:
4306 cycle_all = true;
4307 /* FALLTHROUGH */
4308 case SWM_ARG_ID_CYCLEWS_DOWN:
4309 a.id = (a.id > 0) ? a.id - 1 : workspace_limit - 1;
4310 break;
4311 default:
4312 return;
4313 };
4314
4315 if (!cycle_all &&
4316 (!cycle_empty && TAILQ_EMPTY(&s->ws[a.id].winlist)))
4317 continue;
4318 if (!cycle_visible && s->ws[a.id].r != NULL)
4319 continue;
4320
4321 if (mv)
4322 send_to_ws(bp, r, &a);
4323
4324 switchws(bp, r, &a);
4325 } while (a.id != r->ws->idx);
4326
4327 DNPRINTF(SWM_D_FOCUS, "cyclews: done\n");
4328 }
4329
4330 void
4331 priorws(struct binding *bp, struct swm_region *r, union arg *args)
4332 {
4333 union arg a;
4334
4335 (void)args;
4336
4337 DNPRINTF(SWM_D_WS, "priorws: id: %d, screen[%d]:%dx%d+%d+%d, ws: %d\n",
4338 args->id, r->s->idx, WIDTH(r), HEIGHT(r), X(r), Y(r), r->ws->idx);
4339
4340 if (r->ws_prior == NULL)
4341 return;
4342
4343 a.id = r->ws_prior->idx;
4344 switchws(bp, r, &a);
4345 DNPRINTF(SWM_D_FOCUS, "priorws: done\n");
4346 }
4347
4348 void
4349 focusrg(struct binding *bp, struct swm_region *r, union arg *args)
4350 {
4351 int ridx = args->id, i, num_screens;
4352 struct swm_region *rr = NULL;
4353
4354 (void)bp;
4355
4356 num_screens = get_screen_count();
4357 /* do nothing if we don't have more than one screen */
4358 if (!(num_screens > 1 || outputs > 1))
4359 return;
4360
4361 DNPRINTF(SWM_D_FOCUS, "focusrg: id: %d\n", ridx);
4362
4363 rr = TAILQ_FIRST(&r->s->rl);
4364 for (i = 0; i < ridx && rr != NULL; ++i)
4365 rr = TAILQ_NEXT(rr, entry);
4366
4367 if (rr == NULL)
4368 return;
4369
4370 focus_region(rr);
4371 center_pointer(rr);
4372 focus_flush();
4373 DNPRINTF(SWM_D_FOCUS, "focusrg: done\n");
4374 }
4375
4376 void
4377 cyclerg(struct binding *bp, struct swm_region *r, union arg *args)
4378 {
4379 union arg a;
4380 struct swm_region *rr = NULL;
4381 int i, num_screens;
4382
4383 num_screens = get_screen_count();
4384 /* do nothing if we don't have more than one screen */
4385 if (!(num_screens > 1 || outputs > 1))
4386 return;
4387
4388 i = r->s->idx;
4389 DNPRINTF(SWM_D_FOCUS, "cyclerg: id: %d, region: %d\n", args->id, i);
4390
4391 switch (args->id) {
4392 case SWM_ARG_ID_CYCLERG_UP:
4393 case SWM_ARG_ID_CYCLERG_MOVE_UP:
4394 rr = TAILQ_NEXT(r, entry);
4395 if (rr == NULL)
4396 rr = TAILQ_FIRST(&screens[i].rl);
4397 break;
4398 case SWM_ARG_ID_CYCLERG_DOWN:
4399 case SWM_ARG_ID_CYCLERG_MOVE_DOWN:
4400 rr = TAILQ_PREV(r, swm_region_list, entry);
4401 if (rr == NULL)
4402 rr = TAILQ_LAST(&screens[i].rl, swm_region_list);
4403 break;
4404 default:
4405 return;
4406 };
4407 if (rr == NULL)
4408 return;
4409
4410 switch (args->id) {
4411 case SWM_ARG_ID_CYCLERG_UP:
4412 case SWM_ARG_ID_CYCLERG_DOWN:
4413 focus_region(rr);
4414 center_pointer(rr);
4415 focus_flush();
4416 break;
4417 case SWM_ARG_ID_CYCLERG_MOVE_UP:
4418 case SWM_ARG_ID_CYCLERG_MOVE_DOWN:
4419 a.id = rr->ws->idx;
4420 switchws(bp, r, &a);
4421 break;
4422 default:
4423 return;
4424 };
4425
4426 DNPRINTF(SWM_D_FOCUS, "cyclerg: done\n");
4427 }
4428
4429 /* Sorts transients after parent. */
4430 void
4431 sort_windows(struct ws_win_list *wl)
4432 {
4433 struct ws_win *win, *parent, *nxt;
4434
4435 if (wl == NULL)
4436 return;
4437
4438 for (win = TAILQ_FIRST(wl); win != TAILQ_END(wl); win = nxt) {
4439 nxt = TAILQ_NEXT(win, entry);
4440 if (TRANS(win)) {
4441 parent = find_window(win->transient);
4442 if (parent == NULL) {
4443 warnx("not possible bug");
4444 continue;
4445 }
4446 TAILQ_REMOVE(wl, win, entry);
4447 TAILQ_INSERT_AFTER(wl, parent, win, entry);
4448 }
4449 }
4450 }
4451
4452 void
4453 swapwin(struct binding *bp, struct swm_region *r, union arg *args)
4454 {
4455 struct ws_win *target, *source;
4456 struct ws_win *cur_focus;
4457 struct ws_win_list *wl;
4458
4459 (void)bp;
4460
4461 DNPRINTF(SWM_D_WS, "swapwin: id: %d, screen[%d]:%dx%d+%d+%d, ws: %d\n",
4462 args->id, r->s->idx, WIDTH(r), HEIGHT(r), X(r), Y(r), r->ws->idx);
4463
4464 cur_focus = r->ws->focus;
4465 if (cur_focus == NULL || FULLSCREEN(cur_focus))
4466 return;
4467
4468 /* Adjust stacking in floating layer. */
4469 if (ABOVE(cur_focus)) {
4470 switch (args->id) {
4471 case SWM_ARG_ID_SWAPPREV:
4472 target = TAILQ_PREV(cur_focus, ws_win_stack,
4473 stack_entry);
4474 if (target != NULL && FLOATING(target)) {
4475 TAILQ_REMOVE(&cur_focus->ws->stack, cur_focus,
4476 stack_entry);
4477 TAILQ_INSERT_BEFORE(target, cur_focus,
4478 stack_entry);
4479 update_win_stacking(cur_focus);
4480 focus_flush();
4481 }
4482 break;
4483 case SWM_ARG_ID_SWAPNEXT:
4484 target = TAILQ_NEXT(cur_focus, stack_entry);
4485 if (target != NULL && FLOATING(target)) {
4486 TAILQ_REMOVE(&cur_focus->ws->stack, cur_focus,
4487 stack_entry);
4488 TAILQ_INSERT_AFTER(&cur_focus->ws->stack,
4489 target, cur_focus, stack_entry);
4490 update_win_stacking(cur_focus);
4491 focus_flush();
4492 }
4493 break;
4494 }
4495 goto out;
4496 }
4497
4498 if (r->ws->cur_layout == &layouts[SWM_MAX_STACK])
4499 return;
4500
4501 clear_maximized(r->ws);
4502
4503 source = cur_focus;
4504 wl = &source->ws->winlist;
4505
4506 switch (args->id) {
4507 case SWM_ARG_ID_SWAPPREV:
4508 if (TRANS(source))
4509 source = find_window(source->transient);
4510 target = TAILQ_PREV(source, ws_win_list, entry);
4511 if (target && target->transient)
4512 target = find_window(target->transient);
4513 TAILQ_REMOVE(wl, source, entry);
4514 if (target == NULL)
4515 TAILQ_INSERT_TAIL(wl, source, entry);
4516 else
4517 TAILQ_INSERT_BEFORE(target, source, entry);
4518 break;
4519 case SWM_ARG_ID_SWAPNEXT:
4520 target = TAILQ_NEXT(source, entry);
4521 /* move the parent and let the sort handle the move */
4522 if (TRANS(source))
4523 source = find_window(source->transient);
4524 TAILQ_REMOVE(wl, source, entry);
4525 if (target == NULL)
4526 TAILQ_INSERT_HEAD(wl, source, entry);
4527 else
4528 TAILQ_INSERT_AFTER(wl, target, source, entry);
4529 break;
4530 case SWM_ARG_ID_SWAPMAIN:
4531 target = TAILQ_FIRST(wl);
4532 if (target == source) {
4533 if (source->ws->focus_prev != NULL &&
4534 source->ws->focus_prev != target)
4535 source = source->ws->focus_prev;
4536 else
4537 return;
4538 }
4539 if (target == NULL || source == NULL)
4540 return;
4541 source->ws->focus_prev = target;
4542 TAILQ_REMOVE(wl, target, entry);
4543 TAILQ_INSERT_BEFORE(source, target, entry);
4544 TAILQ_REMOVE(wl, source, entry);
4545 TAILQ_INSERT_HEAD(wl, source, entry);
4546 break;
4547 case SWM_ARG_ID_MOVELAST:
4548 TAILQ_REMOVE(wl, source, entry);
4549 TAILQ_INSERT_TAIL(wl, source, entry);
4550 break;
4551 default:
4552 DNPRINTF(SWM_D_MOVE, "swapwin: invalid id: %d\n", args->id);
4553 return;
4554 }
4555
4556 sort_windows(wl);
4557 ewmh_update_client_list();
4558
4559 stack(r);
4560 center_pointer(r);
4561 focus_flush();
4562 out:
4563 DNPRINTF(SWM_D_MOVE, "swapwin: done\n");
4564 }
4565
4566 struct ws_win *
4567 get_focus_prev(struct ws_win *win)
4568 {
4569 struct ws_win *winfocus = NULL;
4570 struct ws_win *cur_focus = NULL;
4571 struct ws_win_list *wl = NULL;
4572 struct workspace *ws = NULL;
4573
4574 if (!(win && win->ws))
4575 return NULL;
4576
4577 ws = win->ws;
4578 wl = &ws->winlist;
4579 cur_focus = ws->focus;
4580
4581 DNPRINTF(SWM_D_FOCUS, "get_focus_prev: win %#x, cur_focus: %#x, "
4582 "focus_prev: %#x\n", WINID(win), WINID(cur_focus),
4583 WINID(ws->focus_prev));
4584
4585 /* pickle, just focus on whatever */
4586 if (cur_focus == NULL) {
4587 /* use prev_focus if valid */
4588 if (ws->focus_prev && find_window(ws->focus_prev->id))
4589 winfocus = ws->focus_prev;
4590 goto done;
4591 }
4592
4593 /* if transient focus on parent */
4594 if (TRANS(cur_focus)) {
4595 winfocus = find_window(cur_focus->transient);
4596 goto done;
4597 }
4598
4599 /* if in max_stack try harder */
4600 if ((win->quirks & SWM_Q_FOCUSPREV) ||
4601 (ws->cur_layout->flags & SWM_L_FOCUSPREV)) {
4602 if (cur_focus != ws->focus_prev)
4603 winfocus = ws->focus_prev;
4604 else
4605 winfocus = TAILQ_PREV(win, ws_win_list, entry);
4606 if (winfocus)
4607 goto done;
4608 }
4609
4610 DNPRINTF(SWM_D_FOCUS, "get_focus_prev: focus_close: %d\n", focus_close);
4611
4612 if (winfocus == NULL || winfocus == win) {
4613 switch (focus_close) {
4614 case SWM_STACK_BOTTOM:
4615 TAILQ_FOREACH(winfocus, wl, entry)
4616 if (!ICONIC(winfocus) && winfocus != cur_focus)
4617 break;
4618 break;
4619 case SWM_STACK_TOP:
4620 TAILQ_FOREACH_REVERSE(winfocus, wl, ws_win_list, entry)
4621 if (!ICONIC(winfocus) && winfocus != cur_focus)
4622 break;
4623 break;
4624 case SWM_STACK_ABOVE:
4625 winfocus = TAILQ_NEXT(cur_focus, entry);
4626 while (winfocus && ICONIC(winfocus))
4627 winfocus = TAILQ_NEXT(winfocus, entry);
4628
4629 if (winfocus == NULL) {
4630 if (focus_close_wrap) {
4631 TAILQ_FOREACH(winfocus, wl, entry)
4632 if (!ICONIC(winfocus) &&
4633 winfocus != cur_focus)
4634 break;
4635 } else {
4636 TAILQ_FOREACH_REVERSE(winfocus, wl,
4637 ws_win_list, entry)
4638 if (!ICONIC(winfocus) &&
4639 winfocus != cur_focus)
4640 break;
4641 }
4642 }
4643 break;
4644 case SWM_STACK_BELOW:
4645 winfocus = TAILQ_PREV(cur_focus, ws_win_list, entry);
4646 while (winfocus && ICONIC(winfocus))
4647 winfocus = TAILQ_PREV(winfocus, ws_win_list,
4648 entry);
4649
4650 if (winfocus == NULL) {
4651 if (focus_close_wrap) {
4652 TAILQ_FOREACH_REVERSE(winfocus, wl,
4653 ws_win_list, entry)
4654 if (!ICONIC(winfocus) &&
4655 winfocus != cur_focus)
4656 break;
4657 } else {
4658 TAILQ_FOREACH(winfocus, wl, entry)
4659 if (!ICONIC(winfocus) &&
4660 winfocus != cur_focus)
4661 break;
4662 }
4663 }
4664 break;
4665 }
4666 }
4667 done:
4668 if (winfocus == NULL ||
4669 (winfocus && (ICONIC(winfocus) || winfocus == cur_focus))) {
4670 if (focus_default == SWM_STACK_TOP) {
4671 TAILQ_FOREACH_REVERSE(winfocus, wl, ws_win_list, entry)
4672 if (!ICONIC(winfocus) && winfocus != cur_focus)
4673 break;
4674 } else {
4675 TAILQ_FOREACH(winfocus, wl, entry)
4676 if (!ICONIC(winfocus) && winfocus != cur_focus)
4677 break;
4678 }
4679 }
4680
4681 kill_refs(win);
4682
4683 return get_focus_magic(winfocus);
4684 }
4685
4686 struct ws_win *
4687 get_region_focus(struct swm_region *r)
4688 {
4689 struct ws_win *winfocus = NULL;
4690
4691 if (!(r && r->ws))
4692 return NULL;
4693
4694 if (r->ws->focus && !ICONIC(r->ws->focus))
4695 winfocus = r->ws->focus;
4696 else if (r->ws->focus_prev && !ICONIC(r->ws->focus_prev))
4697 winfocus = r->ws->focus_prev;
4698 else
4699 TAILQ_FOREACH(winfocus, &r->ws->winlist, entry)
4700 if (!ICONIC(winfocus))
4701 break;
4702
4703 return get_focus_magic(winfocus);
4704 }
4705
4706 void
4707 focus(struct binding *bp, struct swm_region *r, union arg *args)
4708 {
4709 struct ws_win *head, *cur_focus = NULL, *winfocus = NULL;
4710 struct ws_win_list *wl = NULL;
4711 struct workspace *ws = NULL;
4712 union arg a;
4713 int i;
4714
4715 if (!(r && r->ws))
4716 goto out;
4717
4718 cur_focus = r->ws->focus;
4719 ws = r->ws;
4720 wl = &ws->winlist;
4721
4722 DNPRINTF(SWM_D_FOCUS, "focus: id: %d, cur_focus: %#x\n", args->id,
4723 WINID(cur_focus));
4724
4725 /* Make sure an uniconified window has focus, if one exists. */
4726 if (cur_focus == NULL) {
4727 cur_focus = TAILQ_FIRST(wl);
4728 while (cur_focus != NULL && ICONIC(cur_focus))
4729 cur_focus = TAILQ_NEXT(cur_focus, entry);
4730
4731 DNPRINTF(SWM_D_FOCUS, "focus: new cur_focus: %#x\n",
4732 WINID(cur_focus));
4733 }
4734
4735 switch (args->id) {
4736 case SWM_ARG_ID_FOCUSPREV:
4737 if (cur_focus == NULL)
4738 goto out;
4739
4740 winfocus = cur_focus;
4741 do {
4742 winfocus = TAILQ_PREV(winfocus, ws_win_list, entry);
4743 if (winfocus == NULL)
4744 winfocus = TAILQ_LAST(wl, ws_win_list);
4745 if (winfocus == cur_focus)
4746 break;
4747 } while (winfocus && (ICONIC(winfocus) ||
4748 winfocus->id == cur_focus->transient ||
4749 (cur_focus->transient != XCB_WINDOW_NONE &&
4750 winfocus->transient == cur_focus->transient) ||
4751 (winfocus->quirks & SWM_Q_NOFOCUSCYCLE)));
4752 break;
4753 case SWM_ARG_ID_FOCUSNEXT:
4754 if (cur_focus == NULL)
4755 goto out;
4756
4757 winfocus = cur_focus;
4758 do {
4759 winfocus = TAILQ_NEXT(winfocus, entry);
4760 if (winfocus == NULL)
4761 winfocus = TAILQ_FIRST(wl);
4762 if (winfocus == cur_focus)
4763 break;
4764 } while (winfocus && (ICONIC(winfocus) ||
4765 winfocus->id == cur_focus->transient ||
4766 (cur_focus->transient != XCB_WINDOW_NONE &&
4767 winfocus->transient == cur_focus->transient) ||
4768 (winfocus->quirks & SWM_Q_NOFOCUSCYCLE)));
4769 break;
4770 case SWM_ARG_ID_FOCUSMAIN:
4771 if (cur_focus == NULL)
4772 goto out;
4773
4774 winfocus = TAILQ_FIRST(wl);
4775 if (winfocus == cur_focus)
4776 winfocus = cur_focus->ws->focus_prev;
4777 break;
4778 case SWM_ARG_ID_FOCUSURGENT:
4779 /* Search forward for the next urgent window. */
4780 winfocus = NULL;
4781 head = cur_focus;
4782
4783 for (i = 0; i <= workspace_limit; ++i) {
4784 if (head == NULL)
4785 head = TAILQ_FIRST(&r->s->ws[(ws->idx + i) %
4786 workspace_limit].winlist);
4787
4788 while (head) {
4789 if (head == cur_focus) {
4790 if (i > 0) {
4791 winfocus = cur_focus;
4792 break;
4793 }
4794 } else if (get_urgent(head)) {
4795 winfocus = head;
4796 break;
4797 }
4798
4799 head = TAILQ_NEXT(head, entry);
4800 }
4801
4802 if (winfocus)
4803 break;
4804 }
4805
4806 /* Switch ws if new focus is on a different ws. */
4807 if (winfocus && winfocus->ws != ws) {
4808 a.id = winfocus->ws->idx;
4809 switchws(bp, r, &a);
4810 }
4811 break;
4812 default:
4813 goto out;
4814 }
4815
4816 if (clear_maximized(ws) > 0)
4817 stack(r);
4818
4819 focus_win(get_focus_magic(winfocus));
4820 center_pointer(r);
4821 focus_flush();
4822
4823 out:
4824 DNPRINTF(SWM_D_FOCUS, "focus: done\n");
4825 }
4826
4827 void
4828 focus_pointer(struct binding *bp, struct swm_region *r, union arg *args)
4829 {
4830 (void)args;
4831
4832 /* Not needed for buttons since this is already done in buttonpress. */
4833 if (bp->type == KEYBIND) {
4834 focus_win(get_pointer_win(r->s->root));
4835 focus_flush();
4836 }
4837 }
4838
4839 void
4840 cycle_layout(struct binding *bp, struct swm_region *r, union arg *args)
4841 {
4842 struct workspace *ws = r->ws;
4843
4844 /* suppress unused warning since var is needed */
4845 (void)bp;
4846 (void)args;
4847
4848 DNPRINTF(SWM_D_EVENT, "cycle_layout: workspace: %d\n", ws->idx);
4849
4850 ws->cur_layout++;
4851 if (ws->cur_layout->l_stack == NULL)
4852 ws->cur_layout = &layouts[0];
4853
4854 clear_maximized(ws);
4855
4856 stack(r);
4857 bar_draw(r->bar);
4858
4859 focus_win(get_region_focus(r));
4860
4861 center_pointer(r);
4862 focus_flush();
4863 }
4864
4865 void
4866 stack_config(struct binding *bp, struct swm_region *r, union arg *args)
4867 {
4868 struct workspace *ws = r->ws;
4869
4870 (void)bp;
4871
4872 DNPRINTF(SWM_D_STACK, "stack_config: id: %d workspace: %d\n",
4873 args->id, ws->idx);
4874
4875 if (clear_maximized(ws) > 0)
4876 stack(r);
4877
4878 if (ws->cur_layout->l_config != NULL)
4879 ws->cur_layout->l_config(ws, args->id);
4880
4881 if (args->id != SWM_ARG_ID_STACKINIT)
4882 stack(r);
4883 bar_draw(r->bar);
4884
4885 center_pointer(r);
4886 focus_flush();
4887 }
4888
4889 void
4890 stack(struct swm_region *r) {
4891 struct swm_geometry g;
4892 struct swm_region *r_prev;
4893 uint32_t val[2];
4894
4895 if (r == NULL)
4896 return;
4897
4898 DNPRINTF(SWM_D_STACK, "stack: begin\n");
4899
4900 /* Adjust stack area for region bar and padding. */
4901 g = r->g;
4902 g.x += region_padding;
4903 g.y += region_padding;
4904 g.w -= 2 * border_width + 2 * region_padding;
4905 g.h -= 2 * border_width + 2 * region_padding;
4906 if (bar_enabled && r->ws->bar_enabled) {
4907 if (!bar_at_bottom)
4908 g.y += bar_height;
4909 g.h -= bar_height;
4910 }
4911
4912 DNPRINTF(SWM_D_STACK, "stack: workspace: %d (screen: "
4913 "%d, region: %d), (x,y) WxH: (%d,%d) %d x %d\n",
4914 r->ws->idx, r->s->idx, get_region_index(r), g.x, g.y, g.w, g.h);
4915
4916 r_prev = TAILQ_PREV(r, swm_region_list, entry);
4917 if (r_prev) {
4918 /* Stack bar/input relative to prev. region. */
4919 val[1] = XCB_STACK_MODE_ABOVE;
4920
4921 val[0] = r_prev->id;
4922 DNPRINTF(SWM_D_STACK, "stack: region input %#x "
4923 "relative to %#x.\n", r->id, val[0]);
4924 xcb_configure_window(conn, r->id,
4925 XCB_CONFIG_WINDOW_SIBLING |
4926 XCB_CONFIG_WINDOW_STACK_MODE, val);
4927
4928 val[0] = r_prev->bar->id;
4929 DNPRINTF(SWM_D_STACK, "stack: region bar %#x "
4930 "relative to %#x.\n", r->bar->id, val[0]);
4931 xcb_configure_window(conn, r->bar->id,
4932 XCB_CONFIG_WINDOW_SIBLING |
4933 XCB_CONFIG_WINDOW_STACK_MODE, val);
4934 }
4935
4936 r->ws->cur_layout->l_stack(r->ws, &g);
4937 r->ws->cur_layout->l_string(r->ws);
4938 /* save r so we can track region changes */
4939 r->ws->old_r = r;
4940
4941 if (font_adjusted)
4942 font_adjusted--;
4943
4944 DNPRINTF(SWM_D_STACK, "stack: end\n");
4945 }
4946
4947 void
4948 store_float_geom(struct ws_win *win)
4949 {
4950 if (win == NULL || win->ws->r == NULL)
4951 return;
4952
4953 /* retain window geom and region geom */
4954 win->g_float = win->g;
4955 win->g_float.x -= X(win->ws->r);
4956 win->g_float.y -= Y(win->ws->r);
4957 win->g_floatvalid = true;
4958 DNPRINTF(SWM_D_MISC, "store_float_geom: win %#x, g: (%d,%d)"
4959 " %d x %d, g_float: (%d,%d) %d x %d\n", win->id, X(win), Y(win),
4960 WIDTH(win), HEIGHT(win), win->g_float.x, win->g_float.y,
4961 win->g_float.w, win->g_float.h);
4962 }
4963
4964 void
4965 load_float_geom(struct ws_win *win)
4966 {
4967 if (win == NULL || win->ws->r == NULL)
4968 return;
4969
4970 if (win->g_floatvalid) {
4971 win->g = win->g_float;
4972 X(win) += X(win->ws->r);
4973 Y(win) += Y(win->ws->r);
4974 DNPRINTF(SWM_D_MISC, "load_float_geom: win %#x, g: (%d,%d)"
4975 "%d x %d\n", win->id, X(win), Y(win), WIDTH(win),
4976 HEIGHT(win));
4977 } else {
4978 DNPRINTF(SWM_D_MISC, "load_float_geom: win %#x, g_float "
4979 "is not set.\n", win->id);
4980 }
4981 }
4982
4983 void
4984 update_floater(struct ws_win *win)
4985 {
4986 struct workspace *ws;
4987 struct swm_region *r;
4988
4989 DNPRINTF(SWM_D_MISC, "update_floater: win %#x\n", WINID(win));
4990
4991 if (win == NULL)
4992 return;
4993
4994 ws = win->ws;
4995
4996 if ((r = ws->r) == NULL)
4997 return;
4998
4999 win->bordered = true;
5000
5001 if (FULLSCREEN(win)) {
5002 /* _NET_WM_FULLSCREEN: fullscreen without border. */
5003 if (!win->g_floatvalid)
5004 store_float_geom(win);
5005
5006 win->g = r->g;
5007 win->bordered = false;
5008 } else if (MAXIMIZED(win)) {
5009 /* Maximize: like a single stacked window. */
5010 if (!win->g_floatvalid)
5011 store_float_geom(win);
5012
5013 win->g = r->g;
5014
5015 if (bar_enabled && ws->bar_enabled && !maximize_hide_bar) {
5016 if (!bar_at_bottom)
5017 Y(win) += bar_height;
5018 HEIGHT(win) -= bar_height;
5019 } else if (disable_border) {
5020 win->bordered = false;
5021 }
5022
5023 if (win->bordered) {
5024 /* Window geometry excludes frame. */
5025 X(win) += border_width;
5026 Y(win) += border_width;
5027 HEIGHT(win) -= 2 * border_width;
5028 WIDTH(win) -= 2 * border_width;
5029 }
5030 } else {
5031 /* Normal floating window. */
5032 /* Update geometry if new region. */
5033 if (r != ws->old_r)
5034 load_float_geom(win);
5035
5036 if (((win->quirks & SWM_Q_FULLSCREEN) &&
5037 WIDTH(win) >= WIDTH(r) && HEIGHT(win) >= HEIGHT(r)) ||
5038 ((!WS_FOCUSED(win->ws) || win->ws->focus != win) &&
5039 (win->quirks & SWM_Q_MINIMALBORDER))) {
5040 /* Remove border */
5041 win->bordered = false;
5042 } else if (!MANUAL(win)) {
5043 if (TRANS(win) && (win->quirks & SWM_Q_TRANSSZ)) {
5044 /* Adjust size on TRANSSZ quirk. */
5045 WIDTH(win) = (double)WIDTH(r) * dialog_ratio;
5046 HEIGHT(win) = (double)HEIGHT(r) * dialog_ratio;
5047 }
5048
5049 if (!(win->quirks & SWM_Q_ANYWHERE)) {
5050 /*
5051 * Floaters and transients are auto-centred
5052 * unless manually moved, resized or ANYWHERE
5053 * quirk is set.
5054 */
5055 X(win) = X(r) + (WIDTH(r) - WIDTH(win)) / 2;
5056 Y(win) = Y(r) + (HEIGHT(r) - HEIGHT(win)) / 2;
5057 store_float_geom(win);
5058 }
5059 }
5060 }
5061
5062 /* Ensure at least 1 pixel of the window is in the region. */
5063 region_containment(win, r, SWM_CW_ALLSIDES);
5064
5065 update_window(win);
5066 }
5067
5068 /*
5069 * Send keystrokes to terminal to decrease/increase the font size as the
5070 * window size changes.
5071 */
5072 void
5073 adjust_font(struct ws_win *win)
5074 {
5075 if (!(win->quirks & SWM_Q_XTERM_FONTADJ) ||
5076 ABOVE(win) || TRANS(win))
5077 return;
5078
5079 if (win->sh.width_inc && win->last_inc != win->sh.width_inc &&
5080 WIDTH(win) / win->sh.width_inc < term_width &&
5081 win->font_steps < SWM_MAX_FONT_STEPS) {
5082 win->font_size_boundary[win->font_steps] =
5083 (win->sh.width_inc * term_width) + win->sh.base_width;
5084 win->font_steps++;
5085 font_adjusted++;
5086 win->last_inc = win->sh.width_inc;
5087 fake_keypress(win, XK_KP_Subtract, XCB_MOD_MASK_SHIFT);
5088 } else if (win->font_steps && win->last_inc != win->sh.width_inc &&
5089 WIDTH(win) > win->font_size_boundary[win->font_steps - 1]) {
5090 win->font_steps--;
5091 font_adjusted++;
5092 win->last_inc = win->sh.width_inc;
5093 fake_keypress(win, XK_KP_Add, XCB_MOD_MASK_SHIFT);
5094 }
5095 }
5096
5097 #define SWAPXY(g) do { \
5098 int tmp; \
5099 tmp = (g)->y; (g)->y = (g)->x; (g)->x = tmp; \
5100 tmp = (g)->h; (g)->h = (g)->w; (g)->w = tmp; \
5101 } while (0)
5102 void
5103 stack_master(struct workspace *ws, struct swm_geometry *g, int rot, bool flip)
5104 {
5105 struct swm_geometry cell, r_g = *g;
5106 struct ws_win *win;
5107 int i = 0, j = 0, s = 0, stacks = 0;
5108 int w_inc = 1, h_inc, w_base = 1, h_base;
5109 int hrh = 0, extra = 0, h_slice = 0, last_h = 0;
5110 int split = 0, colno = 0;
5111 int winno, mwin = 0, msize = 0;
5112 int remain, missing, v_slice, mscale;
5113 bool bordered = true, reconfigure = false;
5114
5115 /*
5116 * cell: geometry for window, including frame.
5117 * mwin: # of windows in master area.
5118 * mscale: size increment of master area.
5119 * stacks: # of stack columns
5120 */
5121
5122 DNPRINTF(SWM_D_STACK, "stack_master: workspace: %d, rot: %s, "
5123 "flip: %s\n", ws->idx, YESNO(rot), YESNO(flip));
5124
5125 memset(&cell, 0, sizeof(cell));
5126
5127 /* Prepare tiling variables, if needed. */
5128 if ((winno = count_win(ws, false)) > 0) {
5129 /* Find first tiled window. */
5130 TAILQ_FOREACH(win, &ws->winlist, entry)
5131 if (!FLOATING(win) && !ICONIC(win))
5132 break;
5133
5134 /* Take into account size hints of first tiled window. */
5135 if (rot) {
5136 w_inc = win->sh.width_inc;
5137 w_base = win->sh.base_width;
5138 mwin = ws->l_state.horizontal_mwin;
5139 mscale = ws->l_state.horizontal_msize;
5140 stacks = ws->l_state.horizontal_stacks;
5141 SWAPXY(&r_g);
5142 } else {
5143 w_inc = win->sh.height_inc;
5144 w_base = win->sh.base_height;
5145 mwin = ws->l_state.vertical_mwin;
5146 mscale = ws->l_state.vertical_msize;
5147 stacks = ws->l_state.vertical_stacks;
5148 }
5149
5150 cell = r_g;
5151 cell.x += border_width;
5152 cell.y += border_width;
5153
5154 if (stacks > winno - mwin)
5155 stacks = winno - mwin;
5156 if (stacks < 1)
5157 stacks = 1;
5158
5159 h_slice = r_g.h / SWM_H_SLICE;
5160 if (mwin && winno > mwin) {
5161 v_slice = r_g.w / SWM_V_SLICE;
5162
5163 split = mwin;
5164 colno = split;
5165 cell.w = v_slice * mscale;
5166
5167 if (w_inc > 1 && w_inc < v_slice) {
5168 /* Adjust for requested size increment. */
5169 remain = (cell.w - w_base) % w_inc;
5170 cell.w -= remain;
5171 }
5172
5173 msize = cell.w;
5174 if (flip)
5175 cell.x += r_g.w - msize;
5176 } else {
5177 msize = -2;
5178 colno = split = winno / stacks;
5179 cell.w = ((r_g.w - (stacks * 2 * border_width) +
5180 2 * border_width) / stacks);
5181 }
5182
5183 hrh = r_g.h / colno;
5184 extra = r_g.h - (colno * hrh);
5185 cell.h = hrh - 2 * border_width;
5186 i = j = 0, s = stacks;
5187 }
5188
5189 /* Update window geometry. */
5190 TAILQ_FOREACH(win, &ws->winlist, entry) {
5191 if (ICONIC(win))
5192 continue;
5193
5194 if (FLOATING(win)) {
5195 update_floater(win);
5196 continue;
5197 }
5198
5199 /* Tiled. */
5200 if (split && i == split) {
5201 colno = (winno - mwin) / stacks;
5202 if (s <= (winno - mwin) % stacks)
5203 colno++;
5204 split += colno;
5205 hrh = r_g.h / colno;
5206 extra = r_g.h - (colno * hrh);
5207
5208 if (!flip)
5209 cell.x += cell.w + 2 * border_width +
5210 tile_gap;
5211
5212 cell.w = (r_g.w - msize -
5213 (stacks * (2 * border_width + tile_gap))) / stacks;
5214 if (s == 1)
5215 cell.w += (r_g.w - msize -
5216 (stacks * (2 * border_width + tile_gap))) %
5217 stacks;
5218
5219 if (flip)
5220 cell.x -= cell.w + 2 * border_width +
5221 tile_gap;
5222 s--;
5223 j = 0;
5224 }
5225
5226 cell.h = hrh - 2 * border_width - tile_gap;
5227
5228 if (rot) {
5229 h_inc = win->sh.width_inc;
5230 h_base = win->sh.base_width;
5231 } else {
5232 h_inc = win->sh.height_inc;
5233 h_base = win->sh.base_height;
5234 }
5235
5236 if (j == colno - 1) {
5237 cell.h = hrh + extra;
5238 } else if (h_inc > 1 && h_inc < h_slice) {
5239 /* adjust for window's requested size increment */
5240 remain = (cell.h - h_base) % h_inc;
5241 missing = h_inc - remain;
5242
5243 if (missing <= extra || j == 0) {
5244 extra -= missing;
5245 cell.h += missing;
5246 } else {
5247 cell.h -= remain;
5248 extra += remain;
5249 }
5250 }
5251
5252 if (j == 0)
5253 cell.y = r_g.y + border_width;
5254 else
5255 cell.y += last_h + 2 * border_width + tile_gap;
5256
5257 /* Window coordinates exclude frame. */
5258
5259 if (winno > 1 || !disable_border ||
5260 (bar_enabled && ws->bar_enabled)) {
5261 bordered = true;
5262 } else {
5263 bordered = false;
5264 }
5265
5266 if (rot) {
5267 if (X(win) != cell.y || Y(win) != cell.x ||
5268 WIDTH(win) != cell.h || HEIGHT(win) != cell.w) {
5269 reconfigure = true;
5270 X(win) = cell.y;
5271 Y(win) = cell.x;
5272 WIDTH(win) = cell.h;
5273 HEIGHT(win) = cell.w;
5274 }
5275 } else {
5276 if (X(win) != cell.x || Y(win) != cell.y ||
5277 WIDTH(win) != cell.w || HEIGHT(win) != cell.h) {
5278 reconfigure = true;
5279 X(win) = cell.x;
5280 Y(win) = cell.y;
5281 WIDTH(win) = cell.w;
5282 HEIGHT(win) = cell.h;
5283 }
5284 }
5285
5286 if (!bordered) {
5287 X(win) -= border_width;
5288 Y(win) -= border_width;
5289 WIDTH(win) += 2 * border_width;
5290 HEIGHT(win) += 2 * border_width;
5291 }
5292
5293 if (bordered != win->bordered) {
5294 reconfigure = true;
5295 win->bordered = bordered;
5296 }
5297
5298 if (reconfigure) {
5299 adjust_font(win);
5300 update_window(win);
5301 }
5302
5303 last_h = cell.h;
5304 i++;
5305 j++;
5306 }
5307
5308 /* Stack all windows from bottom up. */
5309 TAILQ_FOREACH_REVERSE(win, &ws->stack, ws_win_stack, stack_entry)
5310 if (!ICONIC(win))
5311 update_win_stacking(win);
5312
5313 /* Map all windows from top down. */
5314 TAILQ_FOREACH(win, &ws->stack, stack_entry)
5315 if (!ICONIC(win))
5316 map_window(win);
5317
5318 DNPRINTF(SWM_D_STACK, "stack_master: done\n");
5319 }
5320
5321 void
5322 vertical_config(struct workspace *ws, int id)
5323 {
5324 DNPRINTF(SWM_D_STACK, "vertical_config: id: %d, workspace: %d\n",
5325 id, ws->idx);
5326
5327 switch (id) {
5328 case SWM_ARG_ID_STACKRESET:
5329 case SWM_ARG_ID_STACKINIT:
5330 ws->l_state.vertical_msize = SWM_V_SLICE / 2;
5331 ws->l_state.vertical_mwin = 1;
5332 ws->l_state.vertical_stacks = 1;
5333 break;
5334 case SWM_ARG_ID_MASTERSHRINK:
5335 if (ws->l_state.vertical_msize > 1)
5336 ws->l_state.vertical_msize--;
5337 break;
5338 case SWM_ARG_ID_MASTERGROW:
5339 if (ws->l_state.vertical_msize < SWM_V_SLICE - 1)
5340 ws->l_state.vertical_msize++;
5341 break;
5342 case SWM_ARG_ID_MASTERADD:
5343 ws->l_state.vertical_mwin++;
5344 break;
5345 case SWM_ARG_ID_MASTERDEL:
5346 if (ws->l_state.vertical_mwin > 0)
5347 ws->l_state.vertical_mwin--;
5348 break;
5349 case SWM_ARG_ID_STACKBALANCE:
5350 ws->l_state.vertical_msize = SWM_V_SLICE / (ws->l_state.vertical_stacks + 1);
5351 break;
5352 case SWM_ARG_ID_STACKINC:
5353 ws->l_state.vertical_stacks++;
5354 break;
5355 case SWM_ARG_ID_STACKDEC:
5356 if (ws->l_state.vertical_stacks > 1)
5357 ws->l_state.vertical_stacks--;
5358 break;
5359 case SWM_ARG_ID_FLIPLAYOUT:
5360 ws->l_state.vertical_flip = !ws->l_state.vertical_flip;
5361 break;
5362 default:
5363 return;
5364 }
5365 }
5366
5367 void
5368 vertical_stack(struct workspace *ws, struct swm_geometry *g)
5369 {
5370 DNPRINTF(SWM_D_STACK, "vertical_stack: workspace: %d\n", ws->idx);
5371
5372 stack_master(ws, g, 0, ws->l_state.vertical_flip);
5373 }
5374
5375 void
5376 horizontal_config(struct workspace *ws, int id)
5377 {
5378 DNPRINTF(SWM_D_STACK, "horizontal_config: workspace: %d\n", ws->idx);
5379
5380 switch (id) {
5381 case SWM_ARG_ID_STACKRESET:
5382 case SWM_ARG_ID_STACKINIT:
5383 ws->l_state.horizontal_mwin = 1;
5384 ws->l_state.horizontal_msize = SWM_H_SLICE / 2;
5385 ws->l_state.horizontal_stacks = 1;
5386 break;
5387 case SWM_ARG_ID_MASTERSHRINK:
5388 if (ws->l_state.horizontal_msize > 1)
5389 ws->l_state.horizontal_msize--;
5390 break;
5391 case SWM_ARG_ID_MASTERGROW:
5392 if (ws->l_state.horizontal_msize < SWM_H_SLICE - 1)
5393 ws->l_state.horizontal_msize++;
5394 break;
5395 case SWM_ARG_ID_MASTERADD:
5396 ws->l_state.horizontal_mwin++;
5397 break;
5398 case SWM_ARG_ID_MASTERDEL:
5399 if (ws->l_state.horizontal_mwin > 0)
5400 ws->l_state.horizontal_mwin--;
5401 break;
5402 case SWM_ARG_ID_STACKBALANCE:
5403 ws->l_state.horizontal_msize = SWM_H_SLICE / (ws->l_state.horizontal_stacks + 1);
5404 break;
5405 case SWM_ARG_ID_STACKINC:
5406 ws->l_state.horizontal_stacks++;
5407 break;
5408 case SWM_ARG_ID_STACKDEC:
5409 if (ws->l_state.horizontal_stacks > 1)
5410 ws->l_state.horizontal_stacks--;
5411 break;
5412 case SWM_ARG_ID_FLIPLAYOUT:
5413 ws->l_state.horizontal_flip = !ws->l_state.horizontal_flip;
5414 break;
5415 default:
5416 return;
5417 }
5418 }
5419
5420 void
5421 horizontal_stack(struct workspace *ws, struct swm_geometry *g)
5422 {
5423 DNPRINTF(SWM_D_STACK, "horizontal_stack: workspace: %d\n", ws->idx);
5424
5425 stack_master(ws, g, 1, ws->l_state.horizontal_flip);
5426 }
5427
5428 /* fullscreen view */
5429 void
5430 max_stack(struct workspace *ws, struct swm_geometry *g)
5431 {
5432 struct swm_geometry gg = *g;
5433 struct ws_win *w, *win = NULL, *parent = NULL, *tmpw;
5434 int winno;
5435
5436 DNPRINTF(SWM_D_STACK, "max_stack: workspace: %d\n", ws->idx);
5437
5438 if (ws == NULL)
5439 return;
5440
5441 winno = count_win(ws, false);
5442 if (winno == 0 && count_win(ws, true) == 0)
5443 return;
5444
5445 /* Figure out which top level window should be visible. */
5446 if (ws->focus_pending)
5447 win = ws->focus_pending;
5448 else if (ws->focus)
5449 win = ws->focus;
5450 else if (ws->focus_prev)
5451 win = ws->focus_prev;
5452 else
5453 win = TAILQ_FIRST(&ws->winlist);
5454
5455 DNPRINTF(SWM_D_STACK, "max_stack: focus_pending: %#x, focus: %#x, "
5456 "focus_prev: %#x, first: %#x, win: %#x\n", WINID(ws->focus_pending),
5457 WINID(ws->focus), WINID(ws->focus_prev),
5458 WINID(TAILQ_FIRST(&ws->winlist)), win->id);
5459
5460 /* Update window geometry. */
5461 TAILQ_FOREACH(w, &ws->winlist, entry) {
5462 if (ICONIC(w))
5463 continue;
5464
5465 if (TRANS(w)) {
5466 update_floater(w);
5467 continue;
5468 }
5469
5470 /* Set maximized flag for all maxed windows. */
5471 if (!MAXIMIZED(w)) {
5472 /* Preserve floating geometry. */
5473 if (ABOVE(w))
5474 store_float_geom(w);
5475
5476 ewmh_apply_flags(w, w->ewmh_flags | EWMH_F_MAXIMIZED);
5477 ewmh_update_wm_state(w);
5478 }
5479
5480 /* Only reconfigure if necessary. */
5481 if (X(w) != gg.x || Y(w) != gg.y || WIDTH(w) != gg.w ||
5482 HEIGHT(w) != gg.h) {
5483 w->g = gg;
5484
5485 if (disable_border && !(bar_enabled && ws->bar_enabled)) {
5486 w->bordered = false;
5487 WIDTH(w) += 2 * border_width;
5488 HEIGHT(w) += 2 * border_width;
5489 } else {
5490 w->bordered = true;
5491 X(w) += border_width;
5492 Y(w) += border_width;
5493 }
5494
5495 update_window(w);
5496 }
5497 }
5498
5499 /* If transient, stack parent and its children. */
5500 if (TRANS(win) && (parent = find_window(win->transient))) {
5501 raise_window(parent);
5502
5503 TAILQ_FOREACH_SAFE(w, &ws->stack, stack_entry, tmpw)
5504 if (w->transient == parent->id)
5505 raise_window(w);
5506 }
5507
5508 /* Make sure focus window is on top. */
5509 raise_window(win);
5510
5511 /* Stack any children of focus window. */
5512 TAILQ_FOREACH_SAFE(w, &ws->stack, stack_entry, tmpw)
5513 if (w->transient == win->id)
5514 raise_window(w);
5515
5516 /* Map all windows. */
5517 TAILQ_FOREACH(w, &ws->stack, stack_entry)
5518 if (!ICONIC(w))
5519 map_window(w);
5520 }
5521
5522 void
5523 send_to_rg(struct binding *bp, struct swm_region *r, union arg *args)
5524 {
5525 int ridx = args->id, i, num_screens;
5526 struct swm_region *rr = NULL;
5527 union arg a;
5528
5529 num_screens = get_screen_count();
5530 /* do nothing if we don't have more than one screen */
5531 if (!(num_screens > 1 || outputs > 1))
5532 return;
5533
5534 DNPRINTF(SWM_D_FOCUS, "send_to_rg: id: %d\n", ridx);
5535
5536 rr = TAILQ_FIRST(&r->s->rl);
5537 for (i = 0; i < ridx && rr != NULL; ++i)
5538 rr = TAILQ_NEXT(rr, entry);
5539
5540 if (rr == NULL)
5541 return;
5542
5543 a.id = rr->ws->idx;
5544
5545 send_to_ws(bp, r, &a);
5546 }
5547
5548 struct swm_region *
5549 region_under(struct swm_screen *s, int x, int y)
5550 {
5551 struct swm_region *r = NULL;
5552
5553 TAILQ_FOREACH(r, &s->rl, entry) {
5554 DNPRINTF(SWM_D_MISC, "region_under: ws: %d, region g: (%d,%d) "
5555 "%d x %d, coords: (%d,%d)\n", r->ws->idx, X(r), Y(r),
5556 WIDTH(r), HEIGHT(r), x, y);
5557 if (X(r) <= x && x < MAX_X(r))
5558 if (Y(r) <= y && y < MAX_Y(r))
5559 return (r);
5560 }
5561
5562 return (NULL);
5563 }
5564
5565 /* Transfer focused window to target workspace and focus. */
5566 void
5567 send_to_ws(struct binding *bp, struct swm_region *r, union arg *args)
5568 {
5569 int wsid = args->id;
5570 struct ws_win *win = NULL;
5571
5572 (void)bp;
5573
5574 if (r && r->ws && r->ws->focus)
5575 win = r->ws->focus;
5576 else
5577 return;
5578
5579 DNPRINTF(SWM_D_MOVE, "send_to_ws: win %#x, ws %d\n", win->id, wsid);
5580
5581 if (wsid < 0 || wsid >= workspace_limit)
5582 return;
5583
5584 if (win->ws->idx == wsid)
5585 return;
5586
5587 win_to_ws(win, wsid, true);
5588
5589 /* Set new focus on target ws. */
5590 if (focus_mode != SWM_FOCUS_FOLLOW) {
5591 win->ws->focus_prev = win->ws->focus;
5592 win->ws->focus = win;
5593 win->ws->focus_pending = NULL;
5594
5595 if (win->ws->focus_prev)
5596 draw_frame(win->ws->focus_prev);
5597 }
5598
5599 DNPRINTF(SWM_D_STACK, "send_to_ws: focus_pending: %#x, focus: %#x, "
5600 "focus_prev: %#x, first: %#x, win: %#x\n",
5601 WINID(r->ws->focus_pending), WINID(r->ws->focus),
5602 WINID(r->ws->focus_prev), WINID(TAILQ_FIRST(&r->ws->winlist)),
5603 win->id);
5604
5605 ewmh_apply_flags(win, win->ewmh_flags & ~EWMH_F_MAXIMIZED);
5606 ewmh_update_wm_state(win);
5607
5608 /* Restack focused region. */
5609 stack(r);
5610
5611 /* If destination ws has a region, restack. */
5612 if (win->ws->r) {
5613 if (FLOATING(win))
5614 load_float_geom(win);
5615
5616 stack(win->ws->r);
5617 }
5618
5619 /* Set new focus on current ws. */
5620 if (focus_mode != SWM_FOCUS_FOLLOW) {
5621 if (r->ws->focus != NULL) {
5622 focus_win(r->ws->focus);
5623 } else {
5624 DNPRINTF(SWM_D_FOCUS, "send_to_ws: set_input_focus: "
5625 "%#x, revert-to: parent, time: 0\n", r->id);
5626 xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT,
5627 r->id, XCB_CURRENT_TIME);
5628 bar_draw(r->bar);
5629 }
5630 }
5631
5632 center_pointer(r);
5633 focus_flush();
5634 }
5635
5636 void
5637 win_to_ws(struct ws_win *win, int wsid, bool unfocus)
5638 {
5639 struct ws_win *parent;
5640 struct workspace *ws, *nws, *pws;
5641
5642 if (wsid < 0 || wsid >= workspace_limit)
5643 return;
5644
5645 if (win->ws->idx == wsid)
5646 return;
5647
5648 ws = win->ws;
5649 nws = &win->s->ws[wsid];
5650
5651 DNPRINTF(SWM_D_MOVE, "win_to_ws: win %#x, ws %d -> %d\n", win->id,
5652 ws->idx, wsid);
5653
5654 /* Cleanup focus on source ws. */
5655 if (focus_mode != SWM_FOCUS_FOLLOW &&
5656 (ws->focus == win || ws->focus_pending == win))
5657 ws->focus_pending = get_focus_prev(win);
5658
5659 /* Move the parent if this is a transient window. */
5660 if (TRANS(win)) {
5661 parent = find_window(win->transient);
5662 if (parent) {
5663 pws = parent->ws;
5664 /* Set new focus in parent's ws if needed. */
5665 if (pws->focus == parent) {
5666 if (focus_mode != SWM_FOCUS_FOLLOW)
5667 pws->focus_pending =
5668 get_focus_prev(parent);
5669
5670 unfocus_win(parent);
5671
5672 if (focus_mode != SWM_FOCUS_FOLLOW) {
5673 pws->focus = pws->focus_pending;
5674 pws->focus_pending = NULL;
5675 }
5676 }
5677
5678 /* Don't unmap parent if new ws is visible */
5679 if (nws->r == NULL)
5680 unmap_window(parent);
5681
5682 /* Transfer */
5683 TAILQ_REMOVE(&ws->winlist, parent, entry);
5684 TAILQ_REMOVE(&ws->stack, parent, stack_entry);
5685 TAILQ_INSERT_TAIL(&nws->winlist, parent, entry);
5686 TAILQ_INSERT_TAIL(&nws->stack, parent, stack_entry);
5687 parent->ws = nws;
5688
5689 DNPRINTF(SWM_D_PROP, "win_to_ws: set property: "
5690 "_NET_WM_DESKTOP: %d\n", wsid);
5691 xcb_change_property(conn, XCB_PROP_MODE_REPLACE,
5692 parent->id, ewmh[_NET_WM_DESKTOP].atom,
5693 XCB_ATOM_CARDINAL, 32, 1, &wsid);
5694 }
5695 }
5696
5697 if (unfocus)
5698 unfocus_win(win);
5699
5700 if (ws->focus_prev == win)
5701 ws->focus_prev = NULL;
5702
5703 if (focus_mode != SWM_FOCUS_FOLLOW && ws->focus_pending != NULL) {
5704 ws->focus = ws->focus_pending;
5705 ws->focus_pending = NULL;
5706 }
5707
5708 /* Don't unmap if new ws is visible */
5709 if (nws->r == NULL)
5710 unmap_window(win);
5711
5712 /* Transfer */
5713 TAILQ_REMOVE(&ws->winlist, win, entry);
5714 TAILQ_REMOVE(&ws->stack, win, stack_entry);
5715 TAILQ_INSERT_TAIL(&nws->winlist, win, entry);
5716 TAILQ_INSERT_TAIL(&nws->stack, win, stack_entry);
5717 win->ws = nws;
5718
5719 /* Update the window's workspace property: _NET_WM_DESKTOP */
5720 DNPRINTF(SWM_D_PROP, "win_to_ws: set property: "
5721 "_NET_WM_DESKTOP: %d\n", wsid);
5722 xcb_change_property(conn, XCB_PROP_MODE_REPLACE, win->id,
5723 ewmh[_NET_WM_DESKTOP].atom, XCB_ATOM_CARDINAL, 32, 1, &wsid);
5724
5725 ewmh_update_client_list();
5726
5727 DNPRINTF(SWM_D_MOVE, "win_to_ws: done.\n");
5728 }
5729
5730 void
5731 pressbutton(struct binding *bp, struct swm_region *r, union arg *args)
5732 {
5733 /* suppress unused warning since var is needed */
5734 (void)bp;
5735 (void)r;
5736
5737 xcb_test_fake_input(conn, XCB_BUTTON_PRESS, args->id,
5738 XCB_CURRENT_TIME, XCB_WINDOW_NONE, 0, 0, 0);
5739 xcb_test_fake_input(conn, XCB_BUTTON_RELEASE, args->id,
5740 XCB_CURRENT_TIME, XCB_WINDOW_NONE, 0, 0, 0);
5741 }
5742
5743 void
5744 raise_focused(struct binding *bp, struct swm_region *r, union arg *args)
5745 {
5746 struct ws_win *win;
5747 uint32_t val;
5748
5749 /* Suppress warning. */
5750 (void)bp;
5751 (void)args;
5752
5753 if (r == NULL || r->ws == NULL || r->ws->focus == NULL)
5754 return;
5755
5756 win = r->ws->focus;
5757 r->ws->raised = win;
5758 raise_window(win);
5759
5760 /* Temporarily override stacking order also in the stack */
5761 if (!FLOATING(win)) {
5762 val = XCB_STACK_MODE_ABOVE;
5763 xcb_configure_window(conn, win->frame,
5764 XCB_CONFIG_WINDOW_STACK_MODE, &val);
5765 }
5766 }
5767
5768 void
5769 raise_toggle(struct binding *bp, struct swm_region *r, union arg *args)
5770 {
5771 /* Suppress warning. */
5772 (void)bp;
5773 (void)args;
5774
5775 if (r == NULL || r->ws == NULL)
5776 return;
5777
5778 if (r->ws->focus && MAXIMIZED(r->ws->focus))
5779 return;
5780
5781 r->ws->always_raise = !r->ws->always_raise;
5782
5783 /* Update focused win stacking order based on new always_raise value. */
5784 raise_window(r->ws->focus);
5785
5786 focus_flush();
5787 }
5788
5789 void
5790 iconify(struct binding *bp, struct swm_region *r, union arg *args)
5791 {
5792 struct ws_win *w;
5793
5794 /* Suppress warning. */
5795 (void)bp;
5796 (void)args;
5797
5798 if ((w = r->ws->focus) == NULL)
5799 return;
5800
5801 ewmh_apply_flags(w, w->ewmh_flags | EWMH_F_HIDDEN);
5802 ewmh_update_wm_state(w);
5803
5804 stack(r);
5805
5806 focus_flush();
5807 }
5808
5809 char *
5810 get_win_name(xcb_window_t win)
5811 {
5812 char *name = NULL;
5813 xcb_get_property_cookie_t c;
5814 xcb_get_property_reply_t *r;
5815
5816 /* First try _NET_WM_NAME for UTF-8. */
5817 c = xcb_get_property(conn, 0, win, ewmh[_NET_WM_NAME].atom,
5818 XCB_GET_PROPERTY_TYPE_ANY, 0, UINT_MAX);
5819 r = xcb_get_property_reply(conn, c, NULL);
5820 if (r && r->type == XCB_NONE) {
5821 free(r);
5822 /* Use WM_NAME instead; no UTF-8. */
5823 c = xcb_get_property(conn, 0, win, XCB_ATOM_WM_NAME,
5824 XCB_GET_PROPERTY_TYPE_ANY, 0, UINT_MAX);
5825 r = xcb_get_property_reply(conn, c, NULL);
5826 }
5827
5828 if (r && r->type != XCB_NONE && r->length > 0)
5829 name = strndup(xcb_get_property_value(r),
5830 xcb_get_property_value_length(r));
5831 else
5832 name = strdup("");
5833
5834 if (name == NULL)
5835 err(1, "get_win_name: failed to allocate memory.");
5836
5837 free(r);
5838
5839 return (name);
5840 }
5841
5842 void
5843 uniconify(struct binding *bp, struct swm_region *r, union arg *args)
5844 {
5845 struct ws_win *win;
5846 FILE *lfile;
5847 char *name;
5848 int count = 0;
5849
5850 (void)bp;
5851
5852 DNPRINTF(SWM_D_MISC, "uniconify\n");
5853
5854 if (r == NULL || r->ws == NULL)
5855 return;
5856
5857 /* make sure we have anything to uniconify */
5858 TAILQ_FOREACH(win, &r->ws->winlist, entry) {
5859 if (win->ws == NULL)
5860 continue; /* should never happen */
5861 if (!ICONIC(win))
5862 continue;
5863 count++;
5864 }
5865
5866 DNPRINTF(SWM_D_MISC, "uniconify: count: %d\n", count);
5867
5868 if (count == 0)
5869 return;
5870
5871 search_r = r;
5872 search_resp_action = SWM_SEARCH_UNICONIFY;
5873
5874 spawn_select(r, args, "search", &searchpid);
5875
5876 if ((lfile = fdopen(select_list_pipe[1], "w")) == NULL)
5877 return;
5878
5879 TAILQ_FOREACH(win, &r->ws->winlist, entry) {
5880 if (win->ws == NULL)
5881 continue; /* should never happen */
5882 if (!ICONIC(win))
5883 continue;
5884
5885 name = get_win_name(win->id);
5886 fprintf(lfile, "%s.%u\n", name, win->id);
5887 free(name);
5888 }
5889
5890 fclose(lfile);
5891 }
5892
5893 void
5894 name_workspace(struct binding *bp, struct swm_region *r, union arg *args)
5895 {
5896 FILE *lfile;
5897
5898 (void)bp;
5899
5900 DNPRINTF(SWM_D_MISC, "name_workspace\n");
5901
5902 if (r == NULL)
5903 return;
5904
5905 search_r = r;
5906 search_resp_action = SWM_SEARCH_NAME_WORKSPACE;
5907
5908 spawn_select(r, args, "name_workspace", &searchpid);
5909
5910 if ((lfile = fdopen(select_list_pipe[1], "w")) == NULL)
5911 return;
5912
5913 fprintf(lfile, "%s", "");
5914 fclose(lfile);
5915 }
5916
5917 void
5918 search_workspace(struct binding *bp, struct swm_region *r, union arg *args)
5919 {
5920 int i;
5921 struct workspace *ws;
5922 FILE *lfile;
5923
5924 (void)bp;
5925
5926 DNPRINTF(SWM_D_MISC, "search_workspace\n");
5927
5928 if (r == NULL)
5929 return;
5930
5931 search_r = r;
5932 search_resp_action = SWM_SEARCH_SEARCH_WORKSPACE;
5933
5934 spawn_select(r, args, "search", &searchpid);
5935
5936 if ((lfile = fdopen(select_list_pipe[1], "w")) == NULL)
5937 return;
5938
5939 for (i = 0; i < workspace_limit; i++) {
5940 ws = &r->s->ws[i];
5941 if (ws == NULL)
5942 continue;
5943 fprintf(lfile, "%d%s%s\n", ws->idx + 1,
5944 (ws->name ? ":" : ""), (ws->name ? ws->name : ""));
5945 }
5946
5947 fclose(lfile);
5948 }
5949
5950 void
5951 search_win_cleanup(void)
5952 {
5953 struct search_window *sw = NULL;
5954
5955 while ((sw = TAILQ_FIRST(&search_wl)) != NULL) {
5956 xcb_destroy_window(conn, sw->indicator);
5957 TAILQ_REMOVE(&search_wl, sw, entry);
5958 free(sw);
5959 }
5960 }
5961
5962 void
5963 search_win(struct binding *bp, struct swm_region *r, union arg *args)
5964 {
5965 struct ws_win *win = NULL;
5966 struct search_window *sw = NULL;
5967 xcb_window_t w;
5968 uint32_t wa[3];
5969 xcb_screen_t *screen;
5970 int i, width, height;
5971 char s[8];
5972 FILE *lfile;
5973 size_t len;
5974 XftDraw *draw;
5975 XGlyphInfo info;
5976 GC l_draw;
5977 XGCValues l_gcv;
5978 XRectangle l_ibox, l_lbox;
5979
5980 (void)bp;
5981
5982 DNPRINTF(SWM_D_MISC, "search_win\n");
5983
5984 search_r = r;
5985 search_resp_action = SWM_SEARCH_SEARCH_WINDOW;
5986
5987 spawn_select(r, args, "search", &searchpid);
5988
5989 if ((lfile = fdopen(select_list_pipe[1], "w")) == NULL)
5990 return;
5991
5992 if ((screen = get_screen(r->s->idx)) == NULL)
5993 errx(1, "ERROR: can't get screen %d.", r->s->idx);
5994
5995 TAILQ_INIT(&search_wl);
5996
5997 i = 1;
5998 TAILQ_FOREACH(win, &r->ws->winlist, entry) {
5999 if (ICONIC(win))
6000 continue;
6001
6002 sw = calloc(1, sizeof(struct search_window));
6003 if (sw == NULL) {
6004 warn("search_win: calloc");
6005 fclose(lfile);
6006 search_win_cleanup();
6007 return;
6008 }
6009 sw->idx = i;
6010 sw->win = win;
6011
6012 snprintf(s, sizeof s, "%d", i);
6013 len = strlen(s);
6014
6015 w = xcb_generate_id(conn);
6016 wa[0] = r->s->c[SWM_S_COLOR_FOCUS].pixel;
6017 wa[1] = r->s->c[SWM_S_COLOR_UNFOCUS].pixel;
6018 wa[2] = screen->default_colormap;
6019
6020 if (bar_font_legacy) {
6021 XmbTextExtents(bar_fs, s, len, &l_ibox, &l_lbox);
6022 width = l_lbox.width + 4;
6023 height = bar_fs_extents->max_logical_extent.height + 4;
6024 } else {
6025 XftTextExtentsUtf8(display, bar_font, (FcChar8 *)s, len,
6026 &info);
6027 width = info.width + 4;
6028 height = bar_font->height + 4;
6029 }
6030
6031 xcb_create_window(conn, screen->root_depth, w, win->frame, 0, 0,
6032 width, height, 1, XCB_WINDOW_CLASS_INPUT_OUTPUT,
6033 screen->root_visual, XCB_CW_BACK_PIXEL |
6034 XCB_CW_BORDER_PIXEL | XCB_CW_COLORMAP, wa);
6035
6036 xcb_map_window(conn, w);
6037
6038 sw->indicator = w;
6039 TAILQ_INSERT_TAIL(&search_wl, sw, entry);
6040
6041 if (bar_font_legacy) {
6042 l_gcv.graphics_exposures = 0;
6043 l_draw = XCreateGC(display, w, 0, &l_gcv);
6044
6045 XSetForeground(display, l_draw,
6046 r->s->c[SWM_S_COLOR_BAR].pixel);
6047
6048 DRAWSTRING(display, w, bar_fs, l_draw, 2,
6049 (bar_fs_extents->max_logical_extent.height -
6050 l_lbox.height) / 2 - l_lbox.y, s, len);
6051
6052 XFreeGC(display, l_draw);
6053 } else {
6054
6055 draw = XftDrawCreate(display, w,
6056 DefaultVisual(display, r->s->idx),
6057 DefaultColormap(display, r->s->idx));
6058
6059 XftDrawStringUtf8(draw, &search_font_color, bar_font, 2,
6060 (HEIGHT(r->bar) + bar_font->height) / 2 -
6061 bar_font->descent, (FcChar8 *)s, len);
6062
6063 XftDrawDestroy(draw);
6064 }
6065
6066 DNPRINTF(SWM_D_MISC, "search_win: mapped win %#x\n", w);
6067
6068 fprintf(lfile, "%d\n", i);
6069 i++;
6070 }
6071
6072 fclose(lfile);
6073
6074 xcb_flush(conn);
6075 }
6076
6077 void
6078 search_resp_uniconify(const char *resp, size_t len)
6079 {
6080 char *name;
6081 struct ws_win *win;
6082 char *s;
6083
6084 DNPRINTF(SWM_D_MISC, "search_resp_uniconify: resp: %s\n", resp);
6085
6086 TAILQ_FOREACH(win, &search_r->ws->winlist, entry) {
6087 if (!ICONIC(win))
6088 continue;
6089 name = get_win_name(win->id);
6090 if (asprintf(&s, "%s.%u", name, win->id) == -1) {
6091 free(name);
6092 continue;
6093 }
6094 free(name);
6095 if (strncmp(s, resp, len) == 0) {
6096 /* XXX this should be a callback to generalize */
6097 ewmh_apply_flags(win, win->ewmh_flags & ~EWMH_F_HIDDEN);
6098 ewmh_update_wm_state(win);
6099 stack(search_r);
6100 free(s);
6101 break;
6102 }
6103 free(s);
6104 }
6105 }
6106
6107 void
6108 search_resp_name_workspace(const char *resp, size_t len)
6109 {
6110 struct workspace *ws;
6111
6112 DNPRINTF(SWM_D_MISC, "search_resp_name_workspace: resp: %s\n", resp);
6113
6114 if (search_r->ws == NULL)
6115 return;
6116 ws = search_r->ws;
6117
6118 if (ws->name) {
6119 free(search_r->ws->name);
6120 search_r->ws->name = NULL;
6121 }
6122
6123 if (len > 1) {
6124 ws->name = strdup(resp);
6125 if (ws->name == NULL) {
6126 DNPRINTF(SWM_D_MISC, "search_resp_name_workspace: "
6127 "strdup: %s", strerror(errno));
6128 return;
6129 }
6130
6131 ewmh_update_desktop_names();
6132 ewmh_get_desktop_names();
6133 }
6134 }
6135
6136 void
6137 ewmh_update_desktop_names(void)
6138 {
6139 char *name_list = NULL, *p;
6140 int num_screens, i, j, len = 0, tot = 0;
6141
6142 num_screens = get_screen_count();
6143 for (i = 0; i < num_screens; ++i) {
6144 for (j = 0; j < workspace_limit; ++j) {
6145 if (screens[i].ws[j].name != NULL)
6146 len += strlen(screens[i].ws[j].name);
6147 ++len;
6148 }
6149
6150 if((name_list = calloc(len, sizeof(char))) == NULL)
6151 err(1, "update_desktop_names: calloc: failed to "
6152 "allocate memory.");
6153
6154 p = name_list;
6155 for (j = 0; j < workspace_limit; ++j) {
6156 if (screens[i].ws[j].name != NULL) {
6157 len = strlen(screens[i].ws[j].name);
6158 memcpy(p, screens[i].ws[j].name, len);
6159 } else {
6160 len = 0;
6161 }
6162
6163 p += len + 1;
6164 tot += len + 1;
6165 }
6166
6167 xcb_change_property(conn, XCB_PROP_MODE_REPLACE,
6168 screens[i].root, ewmh[_NET_DESKTOP_NAMES].atom,
6169 a_utf8_string, 8, tot, name_list);
6170
6171 free(name_list);
6172 name_list = NULL;
6173 }
6174
6175 free(name_list);
6176 }
6177
6178 void
6179 ewmh_get_desktop_names(void)
6180 {
6181 char *names = NULL;
6182 xcb_get_property_cookie_t c;
6183 xcb_get_property_reply_t *r;
6184 int num_screens, i, j, n, k;
6185
6186 num_screens = get_screen_count();
6187 for (i = 0; i < num_screens; ++i) {
6188 for (j = 0; j < workspace_limit; ++j) {
6189 free(screens[i].ws[j].name);
6190 screens[i].ws[j].name = NULL;
6191 }
6192
6193 c = xcb_get_property(conn, 0, screens[i].root,
6194 ewmh[_NET_DESKTOP_NAMES].atom,
6195 a_utf8_string, 0, UINT32_MAX);
6196 r = xcb_get_property_reply(conn, c, NULL);
6197 if (r == NULL)
6198 continue;
6199
6200 names = xcb_get_property_value(r);
6201 n = xcb_get_property_value_length(r);
6202
6203 for (j = 0, k = 0; j < n; ++j) {
6204 if (*(names + j) != '\0') {
6205 screens[i].ws[k].name = strdup(names + j);
6206 j += strlen(names + j);
6207 }
6208 ++k;
6209 }
6210 free(r);
6211 }
6212 }
6213
6214 void
6215 ewmh_update_client_list(void)
6216 {
6217 struct ws_win *win;
6218 int num_screens, i, j, k = 0, count = 0;
6219 xcb_window_t *wins;
6220
6221 num_screens = get_screen_count();
6222 for (i = 0; i < num_screens; ++i) {
6223 for (j = 0; j < workspace_limit; ++j)
6224 TAILQ_FOREACH(win, &screens[i].ws[j].winlist, entry)
6225 ++count;
6226
6227 DNPRINTF(SWM_D_PROP, "ewmh_update_client_list: win count: %d\n",
6228 count);
6229
6230 if (count == 0)
6231 continue;
6232
6233 wins = calloc(count, sizeof(xcb_window_t));
6234 if (wins == NULL)
6235 err(1, "ewmh_update_client_list: calloc: failed to "
6236 "allocate memory.");
6237
6238 for (j = 0, k = 0; j < workspace_limit; ++j)
6239 TAILQ_FOREACH(win, &screens[i].ws[j].winlist, entry)
6240 wins[k++] = win->id;
6241
6242 xcb_change_property(conn, XCB_PROP_MODE_REPLACE,
6243 screens[i].root, ewmh[_NET_CLIENT_LIST].atom,
6244 XCB_ATOM_WINDOW, 32, count, wins);
6245
6246 free(wins);
6247 }
6248 }
6249
6250 void
6251 ewmh_update_current_desktop(void)
6252 {
6253 int num_screens, i;
6254
6255 num_screens = get_screen_count();
6256 for (i = 0; i < num_screens; ++i) {
6257 if (screens[i].r_focus)
6258 xcb_change_property(conn, XCB_PROP_MODE_REPLACE,
6259 screens[i].root, ewmh[_NET_CURRENT_DESKTOP].atom,
6260 XCB_ATOM_CARDINAL, 32, 1,
6261 &screens[i].r_focus->ws->idx);
6262 }
6263 }
6264
6265 void
6266 ewmh_update_desktops(void)
6267 {
6268 int num_screens, i, j;
6269 uint32_t *vals;
6270
6271 vals = calloc(workspace_limit * 2, sizeof(uint32_t));
6272 if (vals == NULL)
6273 err(1, "ewmh_update_desktops: calloc: failed to allocate "
6274 "memory.");
6275
6276 num_screens = get_screen_count();
6277 for (i = 0; i < num_screens; i++) {
6278 xcb_change_property(conn, XCB_PROP_MODE_REPLACE,
6279 screens[i].root, ewmh[_NET_NUMBER_OF_DESKTOPS].atom,
6280 XCB_ATOM_CARDINAL, 32, 1, &workspace_limit);
6281
6282 for (j = 0; j < workspace_limit; ++j) {
6283 if (screens[i].ws[j].r != NULL) {
6284 vals[j * 2] = X(screens[i].ws[j].r);
6285 vals[j * 2 + 1] = Y(screens[i].ws[j].r);
6286 } else if (screens[i].ws[j].old_r != NULL) {
6287 vals[j * 2] = X(screens[i].ws[j].old_r);
6288 vals[j * 2 + 1] = Y(screens[i].ws[j].old_r);
6289 } else {
6290 vals[j * 2] = vals[j * 2 + 1] = 0;
6291 }
6292 }
6293
6294 xcb_change_property(conn, XCB_PROP_MODE_REPLACE,
6295 screens[i].root, ewmh[_NET_DESKTOP_VIEWPORT].atom,
6296 XCB_ATOM_CARDINAL, 32, workspace_limit * 2, vals);
6297 }
6298
6299 free(vals);
6300 }
6301
6302 void
6303 search_resp_search_workspace(const char *resp)
6304 {
6305 char *p, *q;
6306 int ws_idx;
6307 const char *errstr;
6308 union arg a;
6309
6310 DNPRINTF(SWM_D_MISC, "search_resp_search_workspace: resp: %s\n", resp);
6311
6312 q = strdup(resp);
6313 if (q == NULL) {
6314 DNPRINTF(SWM_D_MISC, "search_resp_search_workspace: strdup: %s",
6315 strerror(errno));
6316 return;
6317 }
6318 p = strchr(q, ':');
6319 if (p != NULL)
6320 *p = '\0';
6321 ws_idx = (int)strtonum(q, 1, workspace_limit, &errstr);
6322 if (errstr) {
6323 DNPRINTF(SWM_D_MISC, "workspace idx is %s: %s",
6324 errstr, q);
6325 free(q);
6326 return;
6327 }
6328 free(q);
6329 a.id = ws_idx - 1;
6330 switchws(NULL, search_r, &a);
6331 }
6332
6333 void
6334 search_resp_search_window(const char *resp)
6335 {
6336 char *s;
6337 int idx;
6338 const char *errstr;
6339 struct search_window *sw;
6340
6341 DNPRINTF(SWM_D_MISC, "search_resp_search_window: resp: %s\n", resp);
6342
6343 s = strdup(resp);
6344 if (s == NULL) {
6345 DNPRINTF(SWM_D_MISC, "search_resp_search_window: strdup: %s",
6346 strerror(errno));
6347 return;
6348 }
6349
6350 idx = (int)strtonum(s, 1, INT_MAX, &errstr);
6351 if (errstr) {
6352 DNPRINTF(SWM_D_MISC, "window idx is %s: %s",
6353 errstr, s);
6354 free(s);
6355 return;
6356 }
6357 free(s);
6358
6359 TAILQ_FOREACH(sw, &search_wl, entry)
6360 if (idx == sw->idx) {
6361 focus_win(sw->win);
6362 break;
6363 }
6364 }
6365
6366 #define MAX_RESP_LEN 1024
6367
6368 void
6369 search_do_resp(void)
6370 {
6371 ssize_t rbytes;
6372 char *resp;
6373 size_t len;
6374
6375 DNPRINTF(SWM_D_MISC, "search_do_resp:\n");
6376
6377 search_resp = 0;
6378 searchpid = 0;
6379
6380 if ((resp = calloc(1, MAX_RESP_LEN + 1)) == NULL) {
6381 warn("search: calloc");
6382 goto done;
6383 }
6384
6385 rbytes = read(select_resp_pipe[0], resp, MAX_RESP_LEN);
6386 if (rbytes <= 0) {
6387 warn("search: read error");
6388 goto done;
6389 }
6390 resp[rbytes] = '\0';
6391
6392 /* XXX:
6393 * Older versions of dmenu (Atleast pre 4.4.1) do not send a
6394 * newline, so work around that by sanitizing the resp now.
6395 */
6396 resp[strcspn(resp, "\n")] = '\0';
6397 len = strlen(resp);
6398
6399 switch (search_resp_action) {
6400 case SWM_SEARCH_UNICONIFY:
6401 search_resp_uniconify(resp, len);
6402 break;
6403 case SWM_SEARCH_NAME_WORKSPACE:
6404 search_resp_name_workspace(resp, len);
6405 break;
6406 case SWM_SEARCH_SEARCH_WORKSPACE:
6407 search_resp_search_workspace(resp);
6408 break;
6409 case SWM_SEARCH_SEARCH_WINDOW:
6410 search_resp_search_window(resp);
6411 break;
6412 }
6413
6414 done:
6415 if (search_resp_action == SWM_SEARCH_SEARCH_WINDOW)
6416 search_win_cleanup();
6417
6418 search_resp_action = SWM_SEARCH_NONE;
6419 close(select_resp_pipe[0]);
6420 free(resp);
6421
6422 xcb_flush(conn);
6423 }
6424
6425 void
6426 wkill(struct binding *bp, struct swm_region *r, union arg *args)
6427 {
6428 (void)bp;
6429
6430 DNPRINTF(SWM_D_MISC, "wkill: win %#x, id: %d\n", WINID(r->ws->focus),
6431 args->id);
6432
6433 if (r->ws->focus == NULL)
6434 return;
6435
6436 if (args->id == SWM_ARG_ID_KILLWINDOW)
6437 xcb_kill_client(conn, r->ws->focus->id);
6438 else
6439 if (r->ws->focus->can_delete)
6440 client_msg(r->ws->focus, a_delete, 0);
6441
6442 focus_flush();
6443 }
6444
6445 int
6446 clear_maximized(struct workspace *ws)
6447 {
6448 struct ws_win *w;
6449 int count = 0;
6450
6451 /* Clear any maximized win(s) on ws, from bottom up. */
6452 TAILQ_FOREACH_REVERSE(w, &ws->stack, ws_win_stack, stack_entry)
6453 if (MAXIMIZED(w)) {
6454 ewmh_apply_flags(w, w->ewmh_flags & ~EWMH_F_MAXIMIZED);
6455 ewmh_update_wm_state(w);
6456 ++count;
6457 }
6458
6459 return count;
6460 }
6461
6462 void
6463 maximize_toggle(struct binding *bp, struct swm_region *r, union arg *args)
6464 {
6465 struct ws_win *w = r->ws->focus;
6466
6467 /* suppress unused warning since var is needed */
6468 (void)bp;
6469 (void)args;
6470
6471 if (w == NULL)
6472 return;
6473
6474 DNPRINTF(SWM_D_MISC, "maximize_toggle: win %#x\n", w->id);
6475
6476 if (FULLSCREEN(w))
6477 return;
6478
6479 if (w->ws->cur_layout == &layouts[SWM_MAX_STACK])
6480 return;
6481
6482 ewmh_apply_flags(w, w->ewmh_flags ^ EWMH_F_MAXIMIZED);
6483 ewmh_update_wm_state(w);
6484
6485 stack(r);
6486
6487 if (w == w->ws->focus)
6488 focus_win(w);
6489
6490 center_pointer(r);
6491 focus_flush();
6492 DNPRINTF(SWM_D_MISC, "maximize_toggle: done\n");
6493 }
6494
6495 void
6496 floating_toggle(struct binding *bp, struct swm_region *r, union arg *args)
6497 {
6498 struct ws_win *w = r->ws->focus;
6499
6500 /* suppress unused warning since var is needed */
6501 (void)bp;
6502 (void)args;
6503
6504 if (w == NULL)
6505 return;
6506
6507 DNPRINTF(SWM_D_MISC, "floating_toggle: win %#x\n", w->id);
6508
6509 if (FULLSCREEN(w) || TRANS(w))
6510 return;
6511
6512 if (w->ws->cur_layout == &layouts[SWM_MAX_STACK])
6513 return;
6514
6515 ewmh_apply_flags(w, w->ewmh_flags ^ EWMH_F_ABOVE);
6516 ewmh_update_wm_state(w);
6517
6518 stack(r);
6519
6520 if (w == w->ws->focus)
6521 focus_win(w);
6522
6523 center_pointer(r);
6524 focus_flush();
6525 DNPRINTF(SWM_D_MISC, "floating_toggle: done\n");
6526 }
6527
6528 void
6529 region_containment(struct ws_win *win, struct swm_region *r, int opts)
6530 {
6531 struct swm_geometry g = r->g;
6532 int rt, lt, tp, bm, bw;
6533
6534 bw = (opts & SWM_CW_SOFTBOUNDARY) ? boundary_width : 0;
6535
6536 /*
6537 * Perpendicular distance of each side of the window to the respective
6538 * side of the region boundary. Positive values indicate the side of
6539 * the window has passed beyond the region boundary.
6540 */
6541 rt = opts & SWM_CW_RIGHT ? MAX_X(win) - MAX_X(r) : bw;
6542 lt = opts & SWM_CW_LEFT ? X(r) - X(win) : bw;
6543 bm = opts & SWM_CW_BOTTOM ? MAX_Y(win) - MAX_Y(r) : bw;
6544 tp = opts & SWM_CW_TOP ? Y(r) - Y(win) : bw;
6545
6546 DNPRINTF(SWM_D_MISC, "region_containment: win %#x, rt: %d, lt: %d, "
6547 "bm: %d, tp: %d, SOFTBOUNDARY: %s, HARDBOUNDARY: %s\n", win->id, rt,
6548 lt, bm, tp, YESNO(opts & SWM_CW_SOFTBOUNDARY),
6549 YESNO(opts & SWM_CW_HARDBOUNDARY));
6550
6551 /*
6552 * Disable containment if any of the flagged sides went beyond the
6553 * containment boundary, or if containment is disabled.
6554 */
6555 if (!(opts & SWM_CW_HARDBOUNDARY || opts & SWM_CW_SOFTBOUNDARY) ||
6556 (bw != 0 && ((rt > bw) || (lt > bw) || (bm > bw) || (tp > bw)))) {
6557 /* Make sure window has at least 1 pixel in the region */
6558 g.x += 1 - WIDTH(win);
6559 g.y += 1 - HEIGHT(win);
6560 g.w += 2 * WIDTH(win) - 2;
6561 g.h += 2 * HEIGHT(win) - 2;
6562 }
6563
6564 constrain_window(win, &g, &opts);
6565 }
6566
6567 /* Move or resize a window so that flagged side(s) fit into the supplied box. */
6568 void
6569 constrain_window(struct ws_win *win, struct swm_geometry *b, int *opts)
6570 {
6571 DNPRINTF(SWM_D_MISC, "constrain_window: win %#x, (x,y) w x h: "
6572 "(%d,%d) %d x %d, box: (x,y) w x h: (%d,%d) %d x %d, rt: %s, "
6573 "lt: %s, bt: %s, tp: %s, allow resize: %s\n", win->id, X(win),
6574 Y(win), WIDTH(win), HEIGHT(win), b->x, b->y, b->w, b->h,
6575 YESNO(*opts & SWM_CW_RIGHT), YESNO(*opts & SWM_CW_LEFT),
6576 YESNO(*opts & SWM_CW_BOTTOM), YESNO(*opts & SWM_CW_TOP),
6577 YESNO(*opts & SWM_CW_RESIZABLE));
6578
6579 if ((*opts & SWM_CW_RIGHT) && MAX_X(win) > b->x + b->w) {
6580 if (*opts & SWM_CW_RESIZABLE)
6581 WIDTH(win) = b->x + b->w - X(win);
6582 else
6583 X(win) = b->x + b->w - WIDTH(win);
6584 }
6585
6586 if ((*opts & SWM_CW_LEFT) && X(win) < b->x) {
6587 if (*opts & SWM_CW_RESIZABLE)
6588 WIDTH(win) -= b->x - X(win);
6589
6590 X(win) = b->x;
6591 }
6592
6593 if ((*opts & SWM_CW_BOTTOM) && MAX_Y(win) > b->y + b->h) {
6594 if (*opts & SWM_CW_RESIZABLE)
6595 HEIGHT(win) = b->y + b->h - Y(win);
6596 else
6597 Y(win) = b->y + b->h - HEIGHT(win);
6598 }
6599
6600 if ((*opts & SWM_CW_TOP) && Y(win) < b->y) {
6601 if (*opts & SWM_CW_RESIZABLE)
6602 HEIGHT(win) -= b->y - Y(win);
6603
6604 Y(win) = b->y;
6605 }
6606
6607 if (*opts & SWM_CW_RESIZABLE) {
6608 if (WIDTH(win) < 1)
6609 WIDTH(win) = 1;
6610 if (HEIGHT(win) < 1)
6611 HEIGHT(win) = 1;
6612 }
6613 }
6614
6615 void
6616 draw_frame(struct ws_win *win)
6617 {
6618 xcb_rectangle_t rect[4];
6619 uint32_t *pixel;
6620 int n = 0;
6621
6622 if (win->frame == XCB_WINDOW_NONE) {
6623 DNPRINTF(SWM_D_EVENT, "draw_frame: win %#x not "
6624 "reparented.\n", win->id);
6625 return;
6626 }
6627
6628 if (!win->bordered) {
6629 DNPRINTF(SWM_D_EVENT, "draw_frame: win %#x frame "
6630 "disabled\n", win->id);
6631 }
6632
6633 if (WS_FOCUSED(win->ws) && win->ws->focus == win)
6634 pixel = MAXIMIZED(win) ?
6635 &win->s->c[SWM_S_COLOR_FOCUS_MAXIMIZED].pixel :
6636 &win->s->c[SWM_S_COLOR_FOCUS].pixel;
6637 else
6638 pixel = MAXIMIZED(win) ?
6639 &win->s->c[SWM_S_COLOR_UNFOCUS_MAXIMIZED].pixel :
6640 &win->s->c[SWM_S_COLOR_UNFOCUS].pixel;
6641
6642 /* Top (with corners) */
6643 rect[n].x = 0;
6644 rect[n].y = 0;
6645 rect[n].width = WIDTH(win) + 2 * border_width;
6646 rect[n].height = border_width;
6647 /* Left (without corners) */
6648 rect[++n].x = 0;
6649 rect[n].y = border_width;
6650 rect[n].width = border_width;
6651 rect[n].height = HEIGHT(win);
6652 /* Right (without corners) */
6653 rect[++n].x = border_width + WIDTH(win);
6654 rect[n].y = border_width;
6655 rect[n].width = border_width;
6656 rect[n].height = HEIGHT(win);
6657 /* Bottom (with corners)*/
6658 rect[++n].x = 0;
6659 rect[n].y = border_width + HEIGHT(win);
6660 rect[n].width = WIDTH(win) + 2 * border_width;
6661 rect[n].height = border_width;
6662
6663 xcb_change_gc(conn, win->s->bar_gc, XCB_GC_FOREGROUND, pixel);
6664 xcb_poly_fill_rectangle(conn, win->frame, win->s->bar_gc, 4, rect);
6665 }
6666
6667 void
6668 update_window(struct ws_win *win)
6669 {
6670 uint16_t mask;
6671 uint32_t wc[5];
6672
6673 if (win->frame == XCB_WINDOW_NONE) {
6674 DNPRINTF(SWM_D_EVENT, "update_window: skip win %#x; "
6675 "not reparented.\n", win->id);
6676 return;
6677 }
6678
6679 mask = XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y |
6680 XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT |
6681 XCB_CONFIG_WINDOW_BORDER_WIDTH;
6682
6683 /* Reconfigure frame. */
6684 if (win->bordered) {
6685 wc[0] = X(win) - border_width;
6686 wc[1] = Y(win) - border_width;
6687 wc[2] = WIDTH(win) + 2 * border_width;
6688 wc[3] = HEIGHT(win) + 2 * border_width;
6689 } else {
6690 wc[0] = X(win);
6691 wc[1] = Y(win);
6692 wc[2] = WIDTH(win);
6693 wc[3] = HEIGHT(win);
6694 }
6695
6696 wc[4] = 0;
6697
6698 DNPRINTF(SWM_D_EVENT, "update_window: win %#x frame %#x, (x,y) w x h: "
6699 "(%d,%d) %d x %d, bordered: %s\n", win->id, win->frame, wc[0],
6700 wc[1], wc[2], wc[3], YESNO(win->bordered));
6701
6702 xcb_configure_window(conn, win->frame, mask, wc);
6703
6704 /* Reconfigure client window. */
6705 wc[0] = wc[1] = win->bordered ? border_width : 0;
6706 wc[2] = WIDTH(win);
6707 wc[3] = HEIGHT(win);
6708
6709 DNPRINTF(SWM_D_EVENT, "update_window: win %#x, (x,y) w x h: "
6710 "(%d,%d) %d x %d, bordered: %s\n", win->id, wc[0], wc[1], wc[2],
6711 wc[3], YESNO(win->bordered));
6712 xcb_configure_window(conn, win->id, mask, wc);
6713
6714 /* ICCCM 4.2.3 Synthetic ConfigureNotify when moved and not resized. */
6715 if ((X(win) != win->g_prev.x || Y(win) != win->g_prev.y) &&
6716 (win->java || (WIDTH(win) == win->g_prev.w &&
6717 HEIGHT(win) == win->g_prev.h))) {
6718 /* Java has special needs when moved together with a resize. */
6719 config_win(win, NULL);
6720 }
6721
6722 win->g_prev = win->g;
6723 }
6724
6725 struct event {
6726 SIMPLEQ_ENTRY(event) entry;
6727 xcb_generic_event_t *ev;
6728 };
6729 SIMPLEQ_HEAD(event_queue, event) events = SIMPLEQ_HEAD_INITIALIZER(events);
6730
6731 xcb_generic_event_t *
6732 get_next_event(bool dowait)
6733 {
6734 struct event *ep;
6735 xcb_generic_event_t *evt;
6736
6737 /* Try queue first. */
6738 if ((ep = SIMPLEQ_FIRST(&events))) {
6739 evt = ep->ev;
6740 SIMPLEQ_REMOVE_HEAD(&events, entry);
6741 free(ep);
6742 } else if (dowait)
6743 evt = xcb_wait_for_event(conn);
6744 else
6745 evt = xcb_poll_for_event(conn);
6746
6747 return evt;
6748 }
6749
6750 void
6751 put_back_event(xcb_generic_event_t *evt)
6752 {
6753 struct event *ep;
6754 if ((ep = malloc(sizeof (struct event))) == NULL)
6755 err(1, "put_back_event: failed to allocate memory.");
6756 ep->ev = evt;
6757 SIMPLEQ_INSERT_HEAD(&events, ep, entry);
6758 }
6759
6760 /* Peeks at next event to detect auto-repeat. */
6761 bool
6762 keyrepeating(xcb_key_release_event_t *kre)
6763 {
6764 xcb_generic_event_t *evt;
6765
6766 /* Ensure repeating keypress is finished processing. */
6767 xcb_aux_sync(conn);
6768
6769 if ((evt = get_next_event(false))) {
6770 put_back_event(evt);
6771
6772 if (XCB_EVENT_RESPONSE_TYPE(evt) == XCB_KEY_PRESS &&
6773 kre->sequence == evt->sequence &&
6774 kre->detail == ((xcb_key_press_event_t *)evt)->detail)
6775 return true;
6776 }
6777
6778 return false;
6779 }
6780
6781 bool
6782 keybindreleased(struct binding *bp, xcb_key_release_event_t *kre)
6783 {
6784 if (bp->type == KEYBIND && !keyrepeating(kre) &&
6785 bp->value == xcb_key_press_lookup_keysym(syms, kre, 0))
6786 return true;
6787
6788 return false;
6789 }
6790
6791 #define SWM_RESIZE_STEPS (50)
6792
6793 void
6794 resize_win(struct ws_win *win, struct binding *bp, int opt)
6795 {
6796 xcb_timestamp_t timestamp = 0;
6797 struct swm_region *r = NULL;
6798 struct swm_geometry g;
6799 int top = 0, left = 0;
6800 int dx, dy;
6801 xcb_cursor_t cursor;
6802 xcb_query_pointer_reply_t *xpr = NULL;
6803 xcb_generic_event_t *evt;
6804 xcb_motion_notify_event_t *mne;
6805 bool resizing, step = false;
6806
6807 if (win == NULL)
6808 return;
6809 r = win->ws->r;
6810
6811 if (FULLSCREEN(win))
6812 return;
6813
6814 /* In max_stack mode, should only resize transients. */
6815 if (win->ws->cur_layout == &layouts[SWM_MAX_STACK] && !TRANS(win))
6816 return;
6817
6818 DNPRINTF(SWM_D_EVENT, "resize: win %#x, floating: %s, "
6819 "transient: %#x\n", win->id, YESNO(ABOVE(win)),
6820 win->transient);
6821
6822 if (MAXIMIZED(win))
6823 store_float_geom(win);
6824 else if (!(TRANS(win) || ABOVE(win)))
6825 return;
6826
6827 ewmh_apply_flags(win, (win->ewmh_flags | SWM_F_MANUAL | EWMH_F_ABOVE) &
6828 ~EWMH_F_MAXIMIZED);
6829 ewmh_update_wm_state(win);
6830
6831 stack(r);
6832
6833 focus_flush();
6834
6835 /* It's possible for win to have been freed during focus_flush(). */
6836 if (validate_win(win)) {
6837 DNPRINTF(SWM_D_EVENT, "resize: invalid win.\n");
6838 goto out;
6839 }
6840
6841 switch (opt) {
6842 case SWM_ARG_ID_WIDTHSHRINK:
6843 WIDTH(win) -= SWM_RESIZE_STEPS;
6844 step = true;
6845 break;
6846 case SWM_ARG_ID_WIDTHGROW:
6847 WIDTH(win) += SWM_RESIZE_STEPS;
6848 step = true;
6849 break;
6850 case SWM_ARG_ID_HEIGHTSHRINK:
6851 HEIGHT(win) -= SWM_RESIZE_STEPS;
6852 step = true;
6853 break;
6854 case SWM_ARG_ID_HEIGHTGROW:
6855 HEIGHT(win) += SWM_RESIZE_STEPS;
6856 step = true;
6857 break;
6858 default:
6859 break;
6860 }
6861 if (step) {
6862 region_containment(win, r, SWM_CW_ALLSIDES | SWM_CW_RESIZABLE |
6863 SWM_CW_HARDBOUNDARY);
6864 update_window(win);
6865 store_float_geom(win);
6866 return;
6867 }
6868
6869 region_containment(win, r, SWM_CW_ALLSIDES | SWM_CW_RESIZABLE |
6870 SWM_CW_SOFTBOUNDARY);
6871 update_window(win);
6872
6873 /* get cursor offset from window root */
6874 xpr = xcb_query_pointer_reply(conn, xcb_query_pointer(conn, win->id),
6875 NULL);
6876 if (xpr == NULL)
6877 return;
6878
6879 g = win->g;
6880
6881 if (xpr->win_x < WIDTH(win) / 2)
6882 left = 1;
6883
6884 if (xpr->win_y < HEIGHT(win) / 2)
6885 top = 1;
6886
6887 if (opt == SWM_ARG_ID_CENTER)
6888 cursor = cursors[XC_SIZING].cid;
6889 else if (top)
6890 cursor = cursors[left ? XC_TOP_LEFT_CORNER :
6891 XC_TOP_RIGHT_CORNER].cid;
6892 else
6893 cursor = cursors[left ? XC_BOTTOM_LEFT_CORNER :
6894 XC_BOTTOM_RIGHT_CORNER].cid;
6895
6896 xcb_grab_pointer(conn, 0, win->id, MOUSEMASK,
6897 XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC, XCB_WINDOW_NONE, cursor,
6898 XCB_CURRENT_TIME);
6899
6900 /* Release keyboard freeze if called via keybind. */
6901 if (bp->type == KEYBIND)
6902 xcb_allow_events(conn, XCB_ALLOW_ASYNC_KEYBOARD,
6903 XCB_CURRENT_TIME);
6904
6905 xcb_flush(conn);
6906 resizing = true;
6907 while (resizing && (evt = get_next_event(true))) {
6908 switch (XCB_EVENT_RESPONSE_TYPE(evt)) {
6909 case XCB_BUTTON_RELEASE:
6910 if (bp->type == BTNBIND && bp->value ==
6911 ((xcb_button_release_event_t *)evt)->detail)
6912 resizing = false;
6913 break;
6914 case XCB_KEY_RELEASE:
6915 if (keybindreleased(bp, (xcb_key_release_event_t *)evt))
6916 resizing = false;
6917 break;
6918 case XCB_MOTION_NOTIFY:
6919 mne = (xcb_motion_notify_event_t *)evt;
6920 DNPRINTF(SWM_D_EVENT, "resize: MOTION_NOTIFY: "
6921 "root: %#x\n", mne->root);
6922
6923 /* cursor offset/delta from start of the operation */
6924 dx = mne->root_x - xpr->root_x;
6925 dy = mne->root_y - xpr->root_y;
6926
6927 /* vertical */
6928 if (top)
6929 dy = -dy;
6930
6931 if (opt == SWM_ARG_ID_CENTER) {
6932 if (g.h / 2 + dy < 1)
6933 dy = 1 - g.h / 2;
6934
6935 Y(win) = g.y - dy;
6936 HEIGHT(win) = g.h + 2 * dy;
6937 } else {
6938 if (g.h + dy < 1)
6939 dy = 1 - g.h;
6940
6941 if (top)
6942 Y(win) = g.y - dy;
6943
6944 HEIGHT(win) = g.h + dy;
6945 }
6946
6947 /* horizontal */
6948 if (left)
6949 dx = -dx;
6950
6951 if (opt == SWM_ARG_ID_CENTER) {
6952 if (g.w / 2 + dx < 1)
6953 dx = 1 - g.w / 2;
6954
6955 X(win) = g.x - dx;
6956 WIDTH(win) = g.w + 2 * dx;
6957 } else {
6958 if (g.w + dx < 1)
6959 dx = 1 - g.w;
6960
6961 if (left)
6962 X(win) = g.x - dx;
6963
6964 WIDTH(win) = g.w + dx;
6965 }
6966
6967 /* not free, don't sync more than 120 times / second */
6968 if ((mne->time - timestamp) > (1000 / 120) ) {
6969 timestamp = mne->time;
6970 regionize(win, mne->root_x, mne->root_y);
6971 region_containment(win, r, SWM_CW_ALLSIDES |
6972 SWM_CW_RESIZABLE | SWM_CW_HARDBOUNDARY |
6973 SWM_CW_SOFTBOUNDARY);
6974 update_window(win);
6975 xcb_flush(conn);
6976 }
6977 break;
6978 case XCB_BUTTON_PRESS:
6979 /* Ignore. */
6980 DNPRINTF(SWM_D_EVENT, "resize: BUTTON_PRESS ignored\n");
6981 xcb_allow_events(conn, XCB_ALLOW_ASYNC_POINTER,
6982 ((xcb_button_press_event_t *)evt)->time);
6983 xcb_flush(conn);
6984 break;
6985 case XCB_KEY_PRESS:
6986 /* Ignore. */
6987 DNPRINTF(SWM_D_EVENT, "resize: KEY_PRESS ignored\n");
6988 xcb_allow_events(conn, XCB_ALLOW_ASYNC_KEYBOARD,
6989 ((xcb_key_press_event_t *)evt)->time);
6990 xcb_flush(conn);
6991 break;
6992 default:
6993 event_handle(evt);
6994
6995 /* It's possible for win to have been freed above. */
6996 if (validate_win(win)) {
6997 DNPRINTF(SWM_D_EVENT, "resize: invalid win.\n");
6998 goto out;
6999 }
7000 break;
7001 }
7002 free(evt);
7003 }
7004 if (timestamp) {
7005 region_containment(win, r, SWM_CW_ALLSIDES | SWM_CW_RESIZABLE |
7006 SWM_CW_HARDBOUNDARY | SWM_CW_SOFTBOUNDARY);
7007 update_window(win);
7008 xcb_flush(conn);
7009 }
7010 store_float_geom(win);
7011 out:
7012 xcb_ungrab_pointer(conn, XCB_CURRENT_TIME);
7013 free(xpr);
7014 DNPRINTF(SWM_D_EVENT, "resize: done.\n");
7015 }
7016
7017 void
7018 resize(struct binding *bp, struct swm_region *r, union arg *args)
7019 {
7020 struct ws_win *win = NULL;
7021 xcb_query_pointer_reply_t *qpr = NULL;
7022
7023 if (r == NULL)
7024 return;
7025
7026 if (args->id != SWM_ARG_ID_DONTCENTER &&
7027 args->id != SWM_ARG_ID_CENTER) {
7028 /* {height,width}_{grow,shrink} use the focus window. */
7029 if (r->ws)
7030 win = r->ws->focus;
7031 } else {
7032 /* resize uses window under pointer. */
7033 qpr = xcb_query_pointer_reply(conn,
7034 xcb_query_pointer(conn, r->s->root), NULL);
7035 if (qpr)
7036 win = find_window(qpr->child);
7037 }
7038
7039 if (win == NULL)
7040 return;
7041
7042 resize_win(win, bp, args->id);
7043
7044 if (args->id && bp->type == KEYBIND)
7045 center_pointer(r);
7046
7047 focus_flush();
7048 }
7049
7050 /* Try to set window region based on supplied coordinates or window center. */
7051 void
7052 regionize(struct ws_win *win, int x, int y)
7053 {
7054 struct swm_region *r = NULL;
7055
7056 r = region_under(win->s, x, y);
7057 if (r == NULL)
7058 r = region_under(win->s, X(win) + WIDTH(win) / 2,
7059 Y(win) + HEIGHT(win) / 2);
7060
7061 if (r && r != win->ws->r) {
7062 clear_maximized(r->ws);
7063
7064 win_to_ws(win, r->ws->idx, false);
7065
7066 /* Stack old and new region. */
7067 stack(r);
7068 stack(win->ws->r);
7069
7070 /* Set focus on new ws. */
7071 unfocus_win(r->ws->focus);
7072 r->ws->focus = win;
7073
7074 set_region(r);
7075 raise_window(win);
7076 }
7077 }
7078
7079 #define SWM_MOVE_STEPS (50)
7080
7081 void
7082 move_win(struct ws_win *win, struct binding *bp, int opt)
7083 {
7084 struct swm_region *r;
7085 xcb_timestamp_t timestamp = 0;
7086 xcb_query_pointer_reply_t *qpr = NULL;
7087 xcb_generic_event_t *evt;
7088 xcb_motion_notify_event_t *mne;
7089 bool moving, restack = false, step = false;
7090
7091 if (win == NULL)
7092 return;
7093
7094 if ((r = win->ws->r) == NULL)
7095 return;
7096
7097 if (FULLSCREEN(win))
7098 return;
7099
7100 DNPRINTF(SWM_D_EVENT, "move: win %#x, floating: %s, transient: "
7101 "%#x\n", win->id, YESNO(ABOVE(win)), win->transient);
7102
7103 /* in max_stack mode should only move transients */
7104 if (win->ws->cur_layout == &layouts[SWM_MAX_STACK] && !TRANS(win))
7105 return;
7106
7107 if (!(ABOVE(win) || TRANS(win)) || MAXIMIZED(win)) {
7108 store_float_geom(win);
7109 restack = true;
7110 }
7111
7112 ewmh_apply_flags(win, (win->ewmh_flags | SWM_F_MANUAL | EWMH_F_ABOVE) &
7113 ~EWMH_F_MAXIMIZED);
7114 ewmh_update_wm_state(win);
7115
7116 if (restack)
7117 stack(r);
7118
7119 focus_flush();
7120
7121 /* It's possible for win to have been freed during focus_flush(). */
7122 if (validate_win(win)) {
7123 DNPRINTF(SWM_D_EVENT, "move: invalid win.\n");
7124 goto out;
7125 }
7126
7127 switch (opt) {
7128 case SWM_ARG_ID_MOVELEFT:
7129 X(win) -= (SWM_MOVE_STEPS - border_width);
7130 step = true;
7131 break;
7132 case SWM_ARG_ID_MOVERIGHT:
7133 X(win) += (SWM_MOVE_STEPS - border_width);
7134 step = true;
7135 break;
7136 case SWM_ARG_ID_MOVEUP:
7137 Y(win) -= (SWM_MOVE_STEPS - border_width);
7138 step = true;
7139 break;
7140 case SWM_ARG_ID_MOVEDOWN:
7141 Y(win) += (SWM_MOVE_STEPS - border_width);
7142 step = true;
7143 break;
7144 default:
7145 break;
7146 }
7147 if (step) {
7148 regionize(win, -1, -1);
7149 region_containment(win, win->ws->r, SWM_CW_ALLSIDES);
7150 update_window(win);
7151 store_float_geom(win);
7152 return;
7153 }
7154
7155 xcb_grab_pointer(conn, 0, win->id, MOUSEMASK,
7156 XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC,
7157 XCB_WINDOW_NONE, cursors[XC_FLEUR].cid, XCB_CURRENT_TIME);
7158
7159 /* get cursor offset from window root */
7160 qpr = xcb_query_pointer_reply(conn, xcb_query_pointer(conn, win->id),
7161 NULL);
7162 if (qpr == NULL) {
7163 xcb_ungrab_pointer(conn, XCB_CURRENT_TIME);
7164 return;
7165 }
7166
7167 /* Release keyboard freeze if called via keybind. */
7168 if (bp->type == KEYBIND)
7169 xcb_allow_events(conn, XCB_ALLOW_ASYNC_KEYBOARD,
7170 XCB_CURRENT_TIME);
7171
7172 regionize(win, qpr->root_x, qpr->root_y);
7173 region_containment(win, win->ws->r, SWM_CW_ALLSIDES |
7174 SWM_CW_SOFTBOUNDARY);
7175 update_window(win);
7176 xcb_flush(conn);
7177 moving = true;
7178 while (moving && (evt = get_next_event(true))) {
7179 switch (XCB_EVENT_RESPONSE_TYPE(evt)) {
7180 case XCB_BUTTON_RELEASE:
7181 if (bp->type == BTNBIND && bp->value ==
7182 ((xcb_button_release_event_t *)evt)->detail)
7183 moving = false;
7184
7185 xcb_allow_events(conn, XCB_ALLOW_ASYNC_POINTER,
7186 ((xcb_button_release_event_t *)evt)->time);
7187 xcb_flush(conn);
7188 break;
7189 case XCB_KEY_RELEASE:
7190 if (keybindreleased(bp, (xcb_key_release_event_t *)evt))
7191 moving = false;
7192
7193 xcb_allow_events(conn, XCB_ALLOW_ASYNC_KEYBOARD,
7194 ((xcb_key_release_event_t *)evt)->time);
7195 xcb_flush(conn);
7196 break;
7197 case XCB_MOTION_NOTIFY:
7198 mne = (xcb_motion_notify_event_t *)evt;
7199 DNPRINTF(SWM_D_EVENT, "move: MOTION_NOTIFY: "
7200 "root: %#x\n", mne->root);
7201 X(win) = mne->root_x - qpr->win_x - border_width;
7202 Y(win) = mne->root_y - qpr->win_y - border_width;
7203
7204 /* not free, don't sync more than 120 times / second */
7205 if ((mne->time - timestamp) > (1000 / 120) ) {
7206 timestamp = mne->time;
7207 regionize(win, mne->root_x, mne->root_y);
7208 region_containment(win, win->ws->r,
7209 SWM_CW_ALLSIDES | SWM_CW_SOFTBOUNDARY);
7210 update_window(win);
7211 xcb_flush(conn);
7212 }
7213 break;
7214 case XCB_BUTTON_PRESS:
7215 /* Thaw and ignore. */
7216 xcb_allow_events(conn, XCB_ALLOW_ASYNC_POINTER,
7217 ((xcb_button_press_event_t *)evt)->time);
7218 xcb_flush(conn);
7219 break;
7220 case XCB_KEY_PRESS:
7221 /* Ignore. */
7222 xcb_allow_events(conn, XCB_ALLOW_ASYNC_KEYBOARD,
7223 ((xcb_key_press_event_t *)evt)->time);
7224 xcb_flush(conn);
7225 break;
7226 default:
7227 event_handle(evt);
7228
7229 /* It's possible for win to have been freed. */
7230 if (validate_win(win)) {
7231 DNPRINTF(SWM_D_EVENT, "move: invalid win.\n");
7232 goto out;
7233 }
7234 break;
7235 }
7236 free(evt);
7237 }
7238 if (timestamp) {
7239 region_containment(win, win->ws->r, SWM_CW_ALLSIDES |
7240 SWM_CW_SOFTBOUNDARY);
7241 update_window(win);
7242 xcb_flush(conn);
7243 }
7244 store_float_geom(win);
7245
7246 /* New region set to fullscreen layout. */
7247 if (win->ws->r && win->ws->cur_layout == &layouts[SWM_MAX_STACK]) {
7248 stack(win->ws->r);
7249 focus_flush();
7250 }
7251
7252 out:
7253 free(qpr);
7254 xcb_ungrab_pointer(conn, XCB_CURRENT_TIME);
7255 DNPRINTF(SWM_D_EVENT, "move: done.\n");
7256 }
7257
7258 void
7259 move(struct binding *bp, struct swm_region *r, union arg *args)
7260 {
7261 struct ws_win *win = NULL;
7262 xcb_query_pointer_reply_t *qpr = NULL;
7263
7264 if (r == NULL)
7265 return;
7266
7267 if (args->id) {
7268 /* move_* uses focus window. */
7269 if (r->ws)
7270 win = r->ws->focus;
7271
7272 /* Disallow move_ on tiled. */
7273 if (win && !(TRANS(win) || ABOVE(win)))
7274 return;
7275 } else {
7276 /* move uses window under pointer. */
7277 qpr = xcb_query_pointer_reply(conn,
7278 xcb_query_pointer(conn, r->s->root), NULL);
7279 if (qpr)
7280 win = find_window(qpr->child);
7281 }
7282
7283 if (win == NULL)
7284 return;
7285
7286 move_win(win, bp, args->id);
7287
7288 if (args->id && bp->type == KEYBIND)
7289 center_pointer(r);
7290
7291 focus_flush();
7292 }
7293
7294 /* action definitions */
7295 struct action {
7296 char name[SWM_FUNCNAME_LEN];
7297 void (*func)(struct binding *, struct swm_region *,
7298 union arg *);
7299 uint32_t flags;
7300 union arg args;
7301 } actions[FN_INVALID + 2] = {
7302 /* name function argument */
7303 { "bar_toggle", bar_toggle, 0, {.id = SWM_ARG_ID_BAR_TOGGLE} },
7304 { "bar_toggle_ws", bar_toggle, 0, {.id = SWM_ARG_ID_BAR_TOGGLE_WS} },
7305 { "button2", pressbutton, 0, {.id = 2} },
7306 { "cycle_layout", cycle_layout, 0, {0} },
7307 { "flip_layout", stack_config, 0, {.id = SWM_ARG_ID_FLIPLAYOUT} },
7308 { "float_toggle", floating_toggle,0, {0} },
7309 { "focus", focus_pointer, 0, {0} },
7310 { "focus_main", focus, 0, {.id = SWM_ARG_ID_FOCUSMAIN} },
7311 { "focus_next", focus, 0, {.id = SWM_ARG_ID_FOCUSNEXT} },
7312 { "focus_prev", focus, 0, {.id = SWM_ARG_ID_FOCUSPREV} },
7313 { "focus_urgent", focus, 0, {.id = SWM_ARG_ID_FOCUSURGENT} },
7314 { "maximize_toggle", maximize_toggle,0, {0} },
7315 { "height_grow", resize, 0, {.id = SWM_ARG_ID_HEIGHTGROW} },
7316 { "height_shrink", resize, 0, {.id = SWM_ARG_ID_HEIGHTSHRINK} },
7317 { "iconify", iconify, 0, {0} },
7318 { "master_shrink", stack_config, 0, {.id = SWM_ARG_ID_MASTERSHRINK} },
7319 { "master_grow", stack_config, 0, {.id = SWM_ARG_ID_MASTERGROW} },
7320 { "master_add", stack_config, 0, {.id = SWM_ARG_ID_MASTERADD} },
7321 { "master_del", stack_config, 0, {.id = SWM_ARG_ID_MASTERDEL} },
7322 { "move", move, FN_F_NOREPLAY, {0} },
7323 { "move_down", move, 0, {.id = SWM_ARG_ID_MOVEDOWN} },
7324 { "move_left", move, 0, {.id = SWM_ARG_ID_MOVELEFT} },
7325 { "move_right", move, 0, {.id = SWM_ARG_ID_MOVERIGHT} },
7326 { "move_up", move, 0, {.id = SWM_ARG_ID_MOVEUP} },
7327 { "mvrg_1", send_to_rg, 0, {.id = 0} },
7328 { "mvrg_2", send_to_rg, 0, {.id = 1} },
7329 { "mvrg_3", send_to_rg, 0, {.id = 2} },
7330 { "mvrg_4", send_to_rg, 0, {.id = 3} },
7331 { "mvrg_5", send_to_rg, 0, {.id = 4} },
7332 { "mvrg_6", send_to_rg, 0, {.id = 5} },
7333 { "mvrg_7", send_to_rg, 0, {.id = 6} },
7334 { "mvrg_8", send_to_rg, 0, {.id = 7} },
7335 { "mvrg_9", send_to_rg, 0, {.id = 8} },
7336 { "mvws_1", send_to_ws, 0, {.id = 0} },
7337 { "mvws_2", send_to_ws, 0, {.id = 1} },
7338 { "mvws_3", send_to_ws, 0, {.id = 2} },
7339 { "mvws_4", send_to_ws, 0, {.id = 3} },
7340 { "mvws_5", send_to_ws, 0, {.id = 4} },
7341 { "mvws_6", send_to_ws, 0, {.id = 5} },
7342 { "mvws_7", send_to_ws, 0, {.id = 6} },
7343 { "mvws_8", send_to_ws, 0, {.id = 7} },
7344 { "mvws_9", send_to_ws, 0, {.id = 8} },
7345 { "mvws_10", send_to_ws, 0, {.id = 9} },
7346 { "mvws_11", send_to_ws, 0, {.id = 10} },
7347 { "mvws_12", send_to_ws, 0, {.id = 11} },
7348 { "mvws_13", send_to_ws, 0, {.id = 12} },
7349 { "mvws_14", send_to_ws, 0, {.id = 13} },
7350 { "mvws_15", send_to_ws, 0, {.id = 14} },
7351 { "mvws_16", send_to_ws, 0, {.id = 15} },
7352 { "mvws_17", send_to_ws, 0, {.id = 16} },
7353 { "mvws_18", send_to_ws, 0, {.id = 17} },
7354 { "mvws_19", send_to_ws, 0, {.id = 18} },
7355 { "mvws_20", send_to_ws, 0, {.id = 19} },
7356 { "mvws_21", send_to_ws, 0, {.id = 20} },
7357 { "mvws_22", send_to_ws, 0, {.id = 21} },
7358 { "name_workspace", name_workspace, 0, {0} },
7359 { "quit", quit, 0, {0} },
7360 { "raise_focused", raise_focused, 0, {0} },
7361 { "raise_toggle", raise_toggle, 0, {0} },
7362 { "resize", resize, FN_F_NOREPLAY, {.id = SWM_ARG_ID_DONTCENTER} },
7363 { "resize_centered", resize, FN_F_NOREPLAY, {.id = SWM_ARG_ID_CENTER} },
7364 { "restart", restart, 0, {0} },
7365 { "rg_1", focusrg, 0, {.id = 0} },
7366 { "rg_2", focusrg, 0, {.id = 1} },
7367 { "rg_3", focusrg, 0, {.id = 2} },
7368 { "rg_4", focusrg, 0, {.id = 3} },
7369 { "rg_5", focusrg, 0, {.id = 4} },
7370 { "rg_6", focusrg, 0, {.id = 5} },
7371 { "rg_7", focusrg, 0, {.id = 6} },
7372 { "rg_8", focusrg, 0, {.id = 7} },
7373 { "rg_9", focusrg, 0, {.id = 8} },
7374 { "rg_move_next", cyclerg, 0, {.id = SWM_ARG_ID_CYCLERG_MOVE_UP} },
7375 { "rg_move_prev", cyclerg, 0, {.id = SWM_ARG_ID_CYCLERG_MOVE_DOWN} },
7376 { "rg_next", cyclerg, 0, {.id = SWM_ARG_ID_CYCLERG_UP} },
7377 { "rg_prev", cyclerg, 0, {.id = SWM_ARG_ID_CYCLERG_DOWN} },
7378 { "screen_next", cyclerg, 0, {.id = SWM_ARG_ID_CYCLERG_UP} },
7379 { "screen_prev", cyclerg, 0, {.id = SWM_ARG_ID_CYCLERG_DOWN} },
7380 { "search_win", search_win, 0, {0} },
7381 { "search_workspace", search_workspace, 0, {0} },
7382 { "spawn_custom", NULL, 0, {0} },
7383 { "stack_balance", stack_config, 0, {.id = SWM_ARG_ID_STACKBALANCE} },
7384 { "stack_inc", stack_config, 0, {.id = SWM_ARG_ID_STACKINC} },
7385 { "stack_dec", stack_config, 0, {.id = SWM_ARG_ID_STACKDEC} },
7386 { "stack_reset", stack_config, 0, {.id = SWM_ARG_ID_STACKRESET} },
7387 { "swap_main", swapwin, 0, {.id = SWM_ARG_ID_SWAPMAIN} },
7388 { "swap_next", swapwin, 0, {.id = SWM_ARG_ID_SWAPNEXT} },
7389 { "swap_prev", swapwin, 0, {.id = SWM_ARG_ID_SWAPPREV} },
7390 { "uniconify", uniconify, 0, {0} },
7391 { "version", version, 0, {0} },
7392 { "width_grow", resize, 0, {.id = SWM_ARG_ID_WIDTHGROW} },
7393 { "width_shrink", resize, 0, {.id = SWM_ARG_ID_WIDTHSHRINK} },
7394 { "wind_del", wkill, 0, {.id = SWM_ARG_ID_DELETEWINDOW} },
7395 { "wind_kill", wkill, 0, {.id = SWM_ARG_ID_KILLWINDOW} },
7396 { "ws_1", switchws, 0, {.id = 0} },
7397 { "ws_2", switchws, 0, {.id = 1} },
7398 { "ws_3", switchws, 0, {.id = 2} },
7399 { "ws_4", switchws, 0, {.id = 3} },
7400 { "ws_5", switchws, 0, {.id = 4} },
7401 { "ws_6", switchws, 0, {.id = 5} },
7402 { "ws_7", switchws, 0, {.id = 6} },
7403 { "ws_8", switchws, 0, {.id = 7} },
7404 { "ws_9", switchws, 0, {.id = 8} },
7405 { "ws_10", switchws, 0, {.id = 9} },
7406 { "ws_11", switchws, 0, {.id = 10} },
7407 { "ws_12", switchws, 0, {.id = 11} },
7408 { "ws_13", switchws, 0, {.id = 12} },
7409 { "ws_14", switchws, 0, {.id = 13} },
7410 { "ws_15", switchws, 0, {.id = 14} },
7411 { "ws_16", switchws, 0, {.id = 15} },
7412 { "ws_17", switchws, 0, {.id = 16} },
7413 { "ws_18", switchws, 0, {.id = 17} },
7414 { "ws_19", switchws, 0, {.id = 18} },
7415 { "ws_20", switchws, 0, {.id = 19} },
7416 { "ws_21", switchws, 0, {.id = 20} },
7417 { "ws_22", switchws, 0, {.id = 21} },
7418 { "ws_next", cyclews, 0, {.id = SWM_ARG_ID_CYCLEWS_UP} },
7419 { "ws_next_all", cyclews, 0, {.id = SWM_ARG_ID_CYCLEWS_UP_ALL} },
7420 { "ws_next_move", cyclews, 0, {.id = SWM_ARG_ID_CYCLEWS_MOVE_UP} },
7421 { "ws_prev", cyclews, 0, {.id = SWM_ARG_ID_CYCLEWS_DOWN} },
7422 { "ws_prev_all", cyclews, 0, {.id = SWM_ARG_ID_CYCLEWS_DOWN_ALL} },
7423 { "ws_prev_move", cyclews, 0, {.id = SWM_ARG_ID_CYCLEWS_MOVE_DOWN} },
7424 { "ws_prior", priorws, 0, {0} },
7425 /* SWM_DEBUG actions MUST be here: */
7426 { "debug_toggle", debug_toggle, 0, {0} },
7427 { "dumpwins", dumpwins, 0, {0} },
7428 /* ALWAYS last: */
7429 { "invalid action", NULL, 0, {0} },
7430 };
7431
7432 void
7433 update_modkey(uint16_t mod)
7434 {
7435 struct binding *bp;
7436
7437 /* Replace all instances of the old mod key. */
7438 RB_FOREACH(bp, binding_tree, &bindings)
7439 if (bp->mod & mod_key)
7440 bp->mod = (bp->mod & ~mod_key) | mod;
7441 mod_key = mod;
7442 }
7443
7444 int
7445 spawn_expand(struct swm_region *r, union arg *args, const char *spawn_name,
7446 char ***ret_args)
7447 {
7448 struct spawn_prog *prog = NULL;
7449 int i, c;
7450 char *ap, **real_args;
7451
7452 /* suppress unused warning since var is needed */
7453 (void)args;
7454
7455 DNPRINTF(SWM_D_SPAWN, "spawn_expand: %s\n", spawn_name);
7456
7457 /* find program */
7458 TAILQ_FOREACH(prog, &spawns, entry) {
7459 if (strcasecmp(spawn_name, prog->name) == 0)
7460 break;
7461 }
7462 if (prog == NULL) {
7463 warnx("spawn_custom: program %s not found", spawn_name);
7464 return (-1);
7465 }
7466
7467 /* make room for expanded args */
7468 if ((real_args = calloc(prog->argc + 1, sizeof(char *))) == NULL)
7469 err(1, "spawn_custom: calloc real_args");
7470
7471 /* expand spawn_args into real_args */
7472 for (i = c = 0; i < prog->argc; i++) {
7473 ap = prog->argv[i];
7474 DNPRINTF(SWM_D_SPAWN, "spawn_custom: raw arg: %s\n", ap);
7475 if (strcasecmp(ap, "$bar_border") == 0) {
7476 if ((real_args[c] =
7477 strdup(r->s->c[SWM_S_COLOR_BAR_BORDER].name))
7478 == NULL)
7479 err(1, "spawn_custom border color");
7480 } else if (strcasecmp(ap, "$bar_color") == 0) {
7481 if ((real_args[c] =
7482 strdup(r->s->c[SWM_S_COLOR_BAR].name))
7483 == NULL)
7484 err(1, "spawn_custom bar color");
7485 } else if (strcasecmp(ap, "$bar_font") == 0) {
7486 if ((real_args[c] = strdup(bar_fonts))
7487 == NULL)
7488 err(1, "spawn_custom bar fonts");
7489 } else if (strcasecmp(ap, "$bar_font_color") == 0) {
7490 if ((real_args[c] =
7491 strdup(r->s->c[SWM_S_COLOR_BAR_FONT].name))
7492 == NULL)
7493 err(1, "spawn_custom color font");
7494 } else if (strcasecmp(ap, "$color_focus") == 0) {
7495 if ((real_args[c] =
7496 strdup(r->s->c[SWM_S_COLOR_FOCUS].name))
7497 == NULL)
7498 err(1, "spawn_custom color focus");
7499 } else if (strcasecmp(ap, "$color_focus_maximized") == 0) {
7500 if ((real_args[c] =
7501 strdup(r->s->c[SWM_S_COLOR_FOCUS_MAXIMIZED].name))
7502 == NULL)
7503 err(1, "spawn_custom color focus maximized");
7504 } else if (strcasecmp(ap, "$color_unfocus") == 0) {
7505 if ((real_args[c] =
7506 strdup(r->s->c[SWM_S_COLOR_UNFOCUS].name))
7507 == NULL)
7508 err(1, "spawn_custom color unfocus");
7509 } else if (strcasecmp(ap, "$color_unfocus_maximized") == 0) {
7510 if ((real_args[c] =
7511 strdup(r->s->c[SWM_S_COLOR_UNFOCUS_MAXIMIZED].name))
7512 == NULL)
7513 err(1, "spawn_custom color unfocus maximized");
7514 } else if (strcasecmp(ap, "$region_index") == 0) {
7515 if (asprintf(&real_args[c], "%d",
7516 get_region_index(r) + 1) < 1)
7517 err(1, "spawn_custom region index");
7518 } else if (strcasecmp(ap, "$workspace_index") == 0) {
7519 if (asprintf(&real_args[c], "%d", r->ws->idx + 1) < 1)
7520 err(1, "spawn_custom workspace index");
7521 } else if (strcasecmp(ap, "$dmenu_bottom") == 0) {
7522 if (!bar_at_bottom)
7523 continue;
7524 if ((real_args[c] = strdup("-b")) == NULL)
7525 err(1, "spawn_custom workspace index");
7526 } else {
7527 /* no match --> copy as is */
7528 if ((real_args[c] = strdup(ap)) == NULL)
7529 err(1, "spawn_custom strdup(ap)");
7530 }
7531 DNPRINTF(SWM_D_SPAWN, "spawn_custom: cooked arg: %s\n",
7532 real_args[c]);
7533 ++c;
7534 }
7535
7536 #ifdef SWM_DEBUG
7537 DNPRINTF(SWM_D_SPAWN, "spawn_custom: result: ");
7538 for (i = 0; i < c; ++i)
7539 DPRINTF("\"%s\" ", real_args[i]);
7540 DPRINTF("\n");
7541 #endif
7542 *ret_args = real_args;
7543 return (c);
7544 }
7545
7546 void
7547 spawn_custom(struct swm_region *r, union arg *args, const char *spawn_name)
7548 {
7549 union arg a;
7550 char **real_args;
7551 int spawn_argc, i;
7552
7553 if ((spawn_argc = spawn_expand(r, args, spawn_name, &real_args)) < 0)
7554 return;
7555 a.argv = real_args;
7556 if (fork() == 0)
7557 spawn(r->ws->idx, &a, true);
7558
7559 for (i = 0; i < spawn_argc; i++)
7560 free(real_args[i]);
7561 free(real_args);
7562 }
7563
7564 void
7565 spawn_select(struct swm_region *r, union arg *args, const char *spawn_name,
7566 int *pid)
7567 {
7568 union arg a;
7569 char **real_args;
7570 int i, spawn_argc;
7571
7572 if ((spawn_argc = spawn_expand(r, args, spawn_name, &real_args)) < 0)
7573 return;
7574 a.argv = real_args;
7575
7576 if (pipe(select_list_pipe) == -1)
7577 err(1, "pipe error");
7578 if (pipe(select_resp_pipe) == -1)
7579 err(1, "pipe error");
7580
7581 if (signal(SIGPIPE, SIG_IGN) == SIG_ERR)
7582 err(1, "could not disable SIGPIPE");
7583 switch (*pid = fork()) {
7584 case -1:
7585 err(1, "cannot fork");
7586 break;
7587 case 0: /* child */
7588 if (dup2(select_list_pipe[0], STDIN_FILENO) == -1)
7589 err(1, "dup2");
7590 if (dup2(select_resp_pipe[1], STDOUT_FILENO) == -1)
7591 err(1, "dup2");
7592 close(select_list_pipe[1]);
7593 close(select_resp_pipe[0]);
7594 spawn(r->ws->idx, &a, false);
7595 break;
7596 default: /* parent */
7597 close(select_list_pipe[0]);
7598 close(select_resp_pipe[1]);
7599 break;
7600 }
7601
7602 for (i = 0; i < spawn_argc; i++)
7603 free(real_args[i]);
7604 free(real_args);
7605 }
7606
7607 /* Argument tokenizer. */
7608 char *
7609 argsep(char **sp) {
7610 char *arg, *cp, *next;
7611 bool single_quoted = false, double_quoted = false;
7612
7613 if (*sp == NULL)
7614 return NULL;
7615
7616 /* Eat and move characters until end of argument is found. */
7617 for (arg = next = cp = *sp; *cp != '\0'; ++cp) {
7618 if (!double_quoted && *cp == '\'') {
7619 /* Eat single-quote. */
7620 single_quoted = !single_quoted;
7621 } else if (!single_quoted && *cp == '"') {
7622 /* Eat double-quote. */
7623 double_quoted = !double_quoted;
7624 } else if (!single_quoted && *cp == '\\' && *(cp + 1) == '"') {
7625 /* Eat backslash; copy escaped character to arg. */
7626 *next++ = *(++cp);
7627 } else if (!single_quoted && !double_quoted && *cp == '\\' &&
7628 (*(cp + 1) == '\'' || *(cp + 1) == ' ')) {
7629 /* Eat backslash; move escaped character. */
7630 *next++ = *(++cp);
7631 } else if (!single_quoted && !double_quoted &&
7632 (*cp == ' ' || *cp == '\t')) {
7633 /* Terminate argument. */
7634 *next++ = '\0';
7635 /* Point sp to beginning of next argument. */
7636 *sp = ++cp;
7637 break;
7638 } else {
7639 /* Move regular character. */
7640 *next++ = *cp;
7641 }
7642 }
7643
7644 /* Terminate argument if end of string. */
7645 if (*cp == '\0') {
7646 *next = '\0';
7647 *sp = NULL;
7648 }
7649
7650 return arg;
7651 }
7652
7653 void
7654 spawn_insert(const char *name, const char *args, int flags)
7655 {
7656 struct spawn_prog *sp;
7657 char *arg, *cp, *ptr;
7658
7659 DNPRINTF(SWM_D_SPAWN, "spawn_insert: %s[%s]\n", name, args);
7660
7661 if (args == NULL || *args == '\0')
7662 return;
7663
7664 if ((sp = calloc(1, sizeof *sp)) == NULL)
7665 err(1, "spawn_insert: calloc");
7666 if ((sp->name = strdup(name)) == NULL)
7667 err(1, "spawn_insert: strdup");
7668
7669 /* Convert the arguments to an argument list. */
7670 if ((ptr = cp = strdup(args)) == NULL)
7671 err(1, "spawn_insert: strdup");
7672 while ((arg = argsep(&ptr)) != NULL) {
7673 /* Null argument; skip it. */
7674 if (*arg == '\0')
7675 continue;
7676
7677 sp->argc++;
7678 if ((sp->argv = realloc(sp->argv, sp->argc *
7679 sizeof *sp->argv)) == NULL)
7680 err(1, "spawn_insert: realloc");
7681 if ((sp->argv[sp->argc - 1] = strdup(arg)) == NULL)
7682 err(1, "spawn_insert: strdup");
7683 }
7684 free(cp);
7685
7686 sp->flags = flags;
7687
7688 DNPRINTF(SWM_D_SPAWN, "arg %d: [%s]\n", sp->argc, sp->argv[sp->argc-1]);
7689 TAILQ_INSERT_TAIL(&spawns, sp, entry);
7690 DNPRINTF(SWM_D_SPAWN, "spawn_insert: leave\n");
7691 }
7692
7693 void
7694 spawn_remove(struct spawn_prog *sp)
7695 {
7696 int i;
7697
7698 DNPRINTF(SWM_D_SPAWN, "spawn_remove: %s\n", sp->name);
7699
7700 TAILQ_REMOVE(&spawns, sp, entry);
7701 for (i = 0; i < sp->argc; i++)
7702 free(sp->argv[i]);
7703 free(sp->argv);
7704 free(sp->name);
7705 free(sp);
7706
7707 DNPRINTF(SWM_D_SPAWN, "spawn_remove: leave\n");
7708 }
7709
7710 void
7711 clear_spawns(void)
7712 {
7713 struct spawn_prog *sp;
7714
7715 while ((sp = TAILQ_FIRST(&spawns)) != NULL) {
7716 spawn_remove(sp);
7717 }
7718 }
7719
7720 struct spawn_prog*
7721 spawn_find(const char *name)
7722 {
7723 struct spawn_prog *sp;
7724
7725 TAILQ_FOREACH(sp, &spawns, entry)
7726 if (strcasecmp(sp->name, name) == 0)
7727 return sp;
7728
7729 return NULL;
7730 }
7731
7732 void
7733 setspawn(const char *name, const char *args, int flags)
7734 {
7735 struct spawn_prog *sp;
7736
7737 DNPRINTF(SWM_D_SPAWN, "setspawn: %s\n", name);
7738
7739 if (name == NULL)
7740 return;
7741
7742 /* Remove any old spawn under the same name. */
7743 if ((sp = spawn_find(name)) != NULL)
7744 spawn_remove(sp);
7745
7746 if (*args != '\0')
7747 spawn_insert(name, args, flags);
7748 else
7749 warnx("error: setspawn: cannot find program: %s", name);
7750
7751 DNPRINTF(SWM_D_SPAWN, "setspawn: leave\n");
7752 }
7753
7754 int
7755 setconfspawn(const char *selector, const char *value, int flags)
7756 {
7757 char *args;
7758
7759 if (selector == NULL || strlen(selector) == 0)
7760 return (1);
7761
7762 args = expand_tilde(value);
7763
7764 DNPRINTF(SWM_D_SPAWN, "setconfspawn: [%s] [%s]\n", selector, args);
7765
7766 setspawn(selector, args, flags);
7767 free(args);
7768
7769 DNPRINTF(SWM_D_SPAWN, "setconfspawn: done.\n");
7770 return (0);
7771 }
7772
7773 void
7774 validate_spawns(void)
7775 {
7776 struct binding *bp;
7777 struct spawn_prog *sp;
7778 char which[PATH_MAX];
7779 size_t i;
7780
7781 RB_FOREACH(bp, binding_tree, &bindings) {
7782 if (bp->action != FN_SPAWN_CUSTOM)
7783 continue;
7784
7785 /* find program */
7786 sp = spawn_find(bp->spawn_name);
7787 if (sp == NULL || sp->flags & SWM_SPAWN_OPTIONAL)
7788 continue;
7789
7790 /* verify we have the goods */
7791 snprintf(which, sizeof which, "which %s", sp->argv[0]);
7792 DNPRINTF(SWM_D_CONF, "validate_spawns: which %s\n",
7793 sp->argv[0]);
7794 for (i = strlen("which "); i < strlen(which); i++)
7795 if (which[i] == ' ') {
7796 which[i] = '\0';
7797 break;
7798 }
7799 if (system(which) != 0)
7800 add_startup_exception("could not find %s",
7801 &which[strlen("which ")]);
7802 }
7803 }
7804
7805 void
7806 setup_spawn(void)
7807 {
7808 setconfspawn("lock", "xlock", 0);
7809
7810 setconfspawn("term", "xterm", 0);
7811 setconfspawn("spawn_term", "xterm", 0);
7812
7813 setconfspawn("menu", "dmenu_run"
7814 " $dmenu_bottom"
7815 " -fn $bar_font"
7816 " -nb $bar_color"
7817 " -nf $bar_font_color"
7818 " -sb $bar_border"
7819 " -sf $bar_color", 0);
7820
7821 setconfspawn("search", "dmenu"
7822 " $dmenu_bottom"
7823 " -i"
7824 " -fn $bar_font"
7825 " -nb $bar_color"
7826 " -nf $bar_font_color"
7827 " -sb $bar_border"
7828 " -sf $bar_color", 0);
7829
7830 setconfspawn("name_workspace", "dmenu"
7831 " $dmenu_bottom"
7832 " -p Workspace"
7833 " -fn $bar_font"
7834 " -nb $bar_color"
7835 " -nf $bar_font_color"
7836 " -sb $bar_border"
7837 " -sf $bar_color", 0);
7838
7839 /* These are not verified for existence, even with a binding set. */
7840 setconfspawn("screenshot_all", "screenshot.sh full", SWM_SPAWN_OPTIONAL);
7841 setconfspawn("screenshot_wind", "screenshot.sh window", SWM_SPAWN_OPTIONAL);
7842 setconfspawn("initscr", "initscreen.sh", SWM_SPAWN_OPTIONAL);
7843 }
7844
7845 /* bindings */
7846 #define SWM_MODNAME_SIZE 32
7847 #define SWM_KEY_WS "\n+ \t"
7848 int
7849 parsebinding(const char *bindstr, uint16_t *mod, enum binding_type *type,
7850 uint32_t *val, uint32_t *flags)
7851 {
7852 char *str, *cp, *name;
7853 KeySym ks, lks, uks;
7854
7855 DNPRINTF(SWM_D_KEY, "parsebinding: enter [%s]\n", bindstr);
7856 if (mod == NULL || val == NULL) {
7857 DNPRINTF(SWM_D_KEY, "parsebinding: no mod or key vars\n");
7858 return (1);
7859 }
7860 if (bindstr == NULL || strlen(bindstr) == 0) {
7861 DNPRINTF(SWM_D_KEY, "parsebinding: no bindstr\n");
7862 return (1);
7863 }
7864
7865 if ((cp = str = strdup(bindstr)) == NULL)
7866 err(1, "parsebinding: strdup");
7867
7868 *val = XCB_NO_SYMBOL;
7869 *mod = 0;
7870 *flags = 0;
7871 *type = KEYBIND;
7872 while ((name = strsep(&cp, SWM_KEY_WS)) != NULL) {
7873 DNPRINTF(SWM_D_KEY, "parsebinding: entry [%s]\n", name);
7874 if (cp)
7875 cp += (long)strspn(cp, SWM_KEY_WS);
7876 if (strncasecmp(name, "MOD", SWM_MODNAME_SIZE) == 0)
7877 *mod |= mod_key;
7878 else if (strncasecmp(name, "Mod1", SWM_MODNAME_SIZE) == 0)
7879 *mod |= XCB_MOD_MASK_1;
7880 else if (strncasecmp(name, "Mod2", SWM_MODNAME_SIZE) == 0)
7881 *mod |= XCB_MOD_MASK_2;
7882 else if (strncmp(name, "Mod3", SWM_MODNAME_SIZE) == 0)
7883 *mod |= XCB_MOD_MASK_3;
7884 else if (strncmp(name, "Mod4", SWM_MODNAME_SIZE) == 0)
7885 *mod |= XCB_MOD_MASK_4;
7886 else if (strncmp(name, "Mod5", SWM_MODNAME_SIZE) == 0)
7887 *mod |= XCB_MOD_MASK_5;
7888 else if (strncasecmp(name, "SHIFT", SWM_MODNAME_SIZE) == 0)
7889 *mod |= XCB_MOD_MASK_SHIFT;
7890 else if (strncasecmp(name, "CONTROL", SWM_MODNAME_SIZE) == 0)
7891 *mod |= XCB_MOD_MASK_CONTROL;
7892 else if (strncasecmp(name, "ANYMOD", SWM_MODNAME_SIZE) == 0)
7893 *mod |= XCB_MOD_MASK_ANY;
7894 else if (strncasecmp(name, "REPLAY", SWM_MODNAME_SIZE) == 0)
7895 *flags |= BINDING_F_REPLAY;
7896 else if (sscanf(name, "Button%u", val) == 1) {
7897 DNPRINTF(SWM_D_KEY, "parsebinding: button %u\n", *val);
7898 *type = BTNBIND;
7899 if (*val > 255 || *val == 0) {
7900 DNPRINTF(SWM_D_KEY,
7901 "parsebinding: invalid button %u\n", *val);
7902 return (1);
7903 }
7904 } else {
7905 /* TODO: do this without Xlib. */
7906 ks = XStringToKeysym(name);
7907 if (ks == NoSymbol) {
7908 DNPRINTF(SWM_D_KEY,
7909 "parsebinding: invalid key %s\n", name);
7910 free(str);
7911 return (1);
7912 }
7913
7914 XConvertCase(ks, &lks, &uks);
7915 *val = (uint32_t)lks;
7916 }
7917 }
7918
7919 /* If ANYMOD was specified, ignore the rest. */
7920 if (*mod & XCB_MOD_MASK_ANY)
7921 *mod = XCB_MOD_MASK_ANY;
7922
7923 free(str);
7924 DNPRINTF(SWM_D_KEY, "parsebinding: leave ok\n");
7925 return (0);
7926 }
7927
7928 char *
7929 strdupsafe(const char *str)
7930 {
7931 if (str == NULL)
7932 return (NULL);
7933 else
7934 return (strdup(str));
7935 }
7936
7937 int
7938 binding_cmp(struct binding *bp1, struct binding *bp2)
7939 {
7940 if (bp1->type < bp2->type)
7941 return (-1);
7942 if (bp1->type > bp2->type)
7943 return (1);
7944
7945 if (bp1->value < bp2->value)
7946 return (-1);
7947 if (bp1->value > bp2->value)
7948 return (1);
7949
7950 if (bp1->mod < bp2->mod)
7951 return (-1);
7952 if (bp1->mod > bp2->mod)
7953 return (1);
7954
7955 return (0);
7956 }
7957
7958 void
7959 binding_insert(uint16_t mod, enum binding_type type, uint32_t val,
7960 enum actionid aid, uint32_t flags, const char *spawn_name)
7961 {
7962 struct binding *bp;
7963
7964 DNPRINTF(SWM_D_KEY, "binding_insert: mod: %u, type: %d, val: %u, "
7965 "action: %s(%d), spawn_name: %s\n", mod, type, val,
7966 actions[aid].name, aid, spawn_name);
7967
7968 if ((bp = malloc(sizeof *bp)) == NULL)
7969 err(1, "binding_insert: malloc");
7970
7971 bp->mod = mod;
7972 bp->type = type;
7973 bp->value = val;
7974 bp->action = aid;
7975 bp->flags = flags;
7976 bp->spawn_name = strdupsafe(spawn_name);
7977 RB_INSERT(binding_tree, &bindings, bp);
7978
7979 DNPRINTF(SWM_D_KEY, "binding_insert: leave\n");
7980 }
7981
7982 struct binding *
7983 binding_lookup(uint16_t mod, enum binding_type type, uint32_t val)
7984 {
7985 struct binding bp;
7986
7987 bp.mod = mod;
7988 bp.type = type;
7989 bp.value = val;
7990
7991 return (RB_FIND(binding_tree, &bindings, &bp));
7992 }
7993
7994 void
7995 binding_remove(struct binding *bp)
7996 {
7997 DNPRINTF(SWM_D_KEY, "binding_remove: mod: %u, type: %d, val: %u, "
7998 "action: %s(%d), spawn_name: %s\n", bp->mod, bp->type, bp->value,
7999 actions[bp->action].name, bp->action, bp->spawn_name);
8000
8001 RB_REMOVE(binding_tree, &bindings, bp);
8002 free(bp->spawn_name);
8003 free(bp);
8004
8005 DNPRINTF(SWM_D_KEY, "binding_remove: leave\n");
8006 }
8007
8008 void
8009 setbinding(uint16_t mod, enum binding_type type, uint32_t val,
8010 enum actionid aid, uint32_t flags, const char *spawn_name)
8011 {
8012 struct binding *bp;
8013
8014 DNPRINTF(SWM_D_KEY, "setbinding: enter %s [%s]\n",
8015 actions[aid].name, spawn_name);
8016
8017 /* Unbind any existing. Loop is to handle MOD_MASK_ANY. */
8018 while ((bp = binding_lookup(mod, type, val)))
8019 binding_remove(bp);
8020
8021 if (aid != FN_INVALID)
8022 binding_insert(mod, type, val, aid, flags, spawn_name);
8023
8024 DNPRINTF(SWM_D_KEY, "setbinding: leave\n");
8025 }
8026
8027 int
8028 setconfbinding(const char *selector, const char *value, int flags)
8029 {
8030 struct spawn_prog *sp;
8031 uint32_t keybtn, opts;
8032 uint16_t mod;
8033 enum actionid aid;
8034 enum binding_type type;
8035
8036 /* suppress unused warning since var is needed */
8037 (void)flags;
8038
8039 DNPRINTF(SWM_D_KEY, "setconfbinding: enter selector: [%s], "
8040 "value: [%s]\n", selector, value);
8041 if (selector == NULL || strlen(selector) == 0) {
8042 DNPRINTF(SWM_D_KEY, "setconfbinding: unbind %s\n", value);
8043 if (parsebinding(value, &mod, &type, &keybtn, &opts) == 0) {
8044 setbinding(mod, type, keybtn, FN_INVALID, opts, NULL);
8045 return (0);
8046 } else
8047 return (1);
8048 }
8049 /* search by key function name */
8050 for (aid = 0; aid < FN_INVALID; aid++) {
8051 if (strncasecmp(selector, actions[aid].name,
8052 SWM_FUNCNAME_LEN) == 0) {
8053 DNPRINTF(SWM_D_KEY, "setconfbinding: %s: match "
8054 "action\n", selector);
8055 if (parsebinding(value, &mod, &type, &keybtn,
8056 &opts) == 0) {
8057 setbinding(mod, type, keybtn, aid, opts, NULL);
8058 return (0);
8059 } else
8060 return (1);
8061 }
8062 }
8063 /* search by custom spawn name */
8064 if ((sp = spawn_find(selector)) != NULL) {
8065 DNPRINTF(SWM_D_KEY, "setconfbinding: %s: match "
8066 "spawn\n", selector);
8067 if (parsebinding(value, &mod, &type, &keybtn, &opts) == 0) {
8068 setbinding(mod, type, keybtn, FN_SPAWN_CUSTOM, opts,
8069 sp->name);
8070 return (0);
8071 } else
8072 return (1);
8073 }
8074 DNPRINTF(SWM_D_KEY, "setconfbinding: no match\n");
8075 return (1);
8076 }
8077
8078 #define MODSHIFT MODKEY | XCB_MOD_MASK_SHIFT
8079 void
8080 setup_keybindings(void)
8081 {
8082 #define BINDKEY(m, k, a) setbinding(m, KEYBIND, k, a, 0, NULL)
8083 #define BINDKEYSPAWN(m, k, s) setbinding(m, KEYBIND, k, FN_SPAWN_CUSTOM, 0, s)
8084 BINDKEY(MODKEY, XK_b, FN_BAR_TOGGLE);
8085 BINDKEY(MODSHIFT, XK_b, FN_BAR_TOGGLE_WS);
8086 BINDKEY(MODKEY, XK_b, FN_BAR_TOGGLE);
8087 BINDKEY(MODSHIFT, XK_b, FN_BAR_TOGGLE_WS);
8088 BINDKEY(MODKEY, XK_v, FN_BUTTON2);
8089 BINDKEY(MODKEY, XK_space, FN_CYCLE_LAYOUT);
8090 BINDKEY(MODSHIFT, XK_backslash, FN_FLIP_LAYOUT);
8091 BINDKEY(MODKEY, XK_t, FN_FLOAT_TOGGLE);
8092 BINDKEY(MODKEY, XK_m, FN_FOCUS_MAIN);
8093 BINDKEY(MODKEY, XK_j, FN_FOCUS_NEXT);
8094 BINDKEY(MODKEY, XK_Tab, FN_FOCUS_NEXT);
8095 BINDKEY(MODKEY, XK_k, FN_FOCUS_PREV);
8096 BINDKEY(MODSHIFT, XK_Tab, FN_FOCUS_PREV);
8097 BINDKEY(MODKEY, XK_u, FN_FOCUS_URGENT);
8098 BINDKEY(MODKEY, XK_e, FN_MAXIMIZE_TOGGLE);
8099 BINDKEY(MODSHIFT, XK_equal, FN_HEIGHT_GROW);
8100 BINDKEY(MODSHIFT, XK_minus, FN_HEIGHT_SHRINK);
8101 BINDKEY(MODKEY, XK_w, FN_ICONIFY);
8102 BINDKEY(MODKEY, XK_h, FN_MASTER_SHRINK);
8103 BINDKEY(MODKEY, XK_l, FN_MASTER_GROW);
8104 BINDKEY(MODKEY, XK_comma, FN_MASTER_ADD);
8105 BINDKEY(MODKEY, XK_period, FN_MASTER_DEL);
8106 BINDKEY(MODSHIFT, XK_bracketright, FN_MOVE_DOWN);
8107 BINDKEY(MODKEY, XK_bracketleft, FN_MOVE_LEFT);
8108 BINDKEY(MODKEY, XK_bracketright, FN_MOVE_RIGHT);
8109 BINDKEY(MODSHIFT, XK_bracketleft, FN_MOVE_UP);
8110 BINDKEY(MODSHIFT, XK_KP_End, FN_MVRG_1);
8111 BINDKEY(MODSHIFT, XK_KP_Down, FN_MVRG_2);
8112 BINDKEY(MODSHIFT, XK_KP_Next, FN_MVRG_3);
8113 BINDKEY(MODSHIFT, XK_KP_Left, FN_MVRG_4);
8114 BINDKEY(MODSHIFT, XK_KP_Begin, FN_MVRG_5);
8115 BINDKEY(MODSHIFT, XK_KP_Right, FN_MVRG_6);
8116 BINDKEY(MODSHIFT, XK_KP_Home, FN_MVRG_7);
8117 BINDKEY(MODSHIFT, XK_KP_Up, FN_MVRG_8);
8118 BINDKEY(MODSHIFT, XK_KP_Prior, FN_MVRG_9);
8119 BINDKEY(MODSHIFT, XK_1, FN_MVWS_1);
8120 BINDKEY(MODSHIFT, XK_2, FN_MVWS_2);
8121 BINDKEY(MODSHIFT, XK_3, FN_MVWS_3);
8122 BINDKEY(MODSHIFT, XK_4, FN_MVWS_4);
8123 BINDKEY(MODSHIFT, XK_5, FN_MVWS_5);
8124 BINDKEY(MODSHIFT, XK_6, FN_MVWS_6);
8125 BINDKEY(MODSHIFT, XK_7, FN_MVWS_7);
8126 BINDKEY(MODSHIFT, XK_8, FN_MVWS_8);
8127 BINDKEY(MODSHIFT, XK_9, FN_MVWS_9);
8128 BINDKEY(MODSHIFT, XK_0, FN_MVWS_10);
8129 BINDKEY(MODSHIFT, XK_F1, FN_MVWS_11);
8130 BINDKEY(MODSHIFT, XK_F2, FN_MVWS_12);
8131 BINDKEY(MODSHIFT, XK_F3, FN_MVWS_13);
8132 BINDKEY(MODSHIFT, XK_F4, FN_MVWS_14);
8133 BINDKEY(MODSHIFT, XK_F5, FN_MVWS_15);
8134 BINDKEY(MODSHIFT, XK_F6, FN_MVWS_16);
8135 BINDKEY(MODSHIFT, XK_F7, FN_MVWS_17);
8136 BINDKEY(MODSHIFT, XK_F8, FN_MVWS_18);
8137 BINDKEY(MODSHIFT, XK_F9, FN_MVWS_19);
8138 BINDKEY(MODSHIFT, XK_F10, FN_MVWS_20);
8139 BINDKEY(MODSHIFT, XK_F11, FN_MVWS_21);
8140 BINDKEY(MODSHIFT, XK_F12, FN_MVWS_22);
8141 BINDKEY(MODSHIFT, XK_slash, FN_NAME_WORKSPACE);
8142 BINDKEY(MODSHIFT, XK_q, FN_QUIT);
8143 BINDKEY(MODSHIFT, XK_r, FN_RAISE_TOGGLE);
8144 BINDKEY(MODKEY, XK_q, FN_RESTART);
8145 BINDKEY(MODKEY, XK_KP_End, FN_RG_1);
8146 BINDKEY(MODKEY, XK_KP_Down, FN_RG_2);
8147 BINDKEY(MODKEY, XK_KP_Next, FN_RG_3);
8148 BINDKEY(MODKEY, XK_KP_Left, FN_RG_4);
8149 BINDKEY(MODKEY, XK_KP_Begin, FN_RG_5);
8150 BINDKEY(MODKEY, XK_KP_Right, FN_RG_6);
8151 BINDKEY(MODKEY, XK_KP_Home, FN_RG_7);
8152 BINDKEY(MODKEY, XK_KP_Up, FN_RG_8);
8153 BINDKEY(MODKEY, XK_KP_Prior, FN_RG_9);
8154 BINDKEY(MODSHIFT, XK_Right, FN_RG_NEXT);
8155 BINDKEY(MODSHIFT, XK_Left, FN_RG_PREV);
8156 BINDKEY(MODKEY, XK_f, FN_SEARCH_WIN);
8157 BINDKEY(MODKEY, XK_slash, FN_SEARCH_WORKSPACE);
8158 BINDKEYSPAWN(MODSHIFT, XK_i, "initscr");
8159 BINDKEYSPAWN(MODSHIFT, XK_Delete, "lock");
8160 BINDKEYSPAWN(MODKEY, XK_p, "menu");
8161 BINDKEYSPAWN(MODKEY, XK_s, "screenshot_all");
8162 BINDKEYSPAWN(MODSHIFT, XK_s, "screenshot_wind");
8163 BINDKEYSPAWN(MODSHIFT, XK_Return, "term");
8164 BINDKEY(MODSHIFT, XK_comma, FN_STACK_INC);
8165 BINDKEY(MODSHIFT, XK_period, FN_STACK_DEC);
8166 BINDKEY(MODSHIFT, XK_space, FN_STACK_RESET);
8167 BINDKEY(MODKEY, XK_Return, FN_SWAP_MAIN);
8168 BINDKEY(MODSHIFT, XK_j, FN_SWAP_NEXT);
8169 BINDKEY(MODSHIFT, XK_k, FN_SWAP_PREV);
8170 BINDKEY(MODSHIFT, XK_w, FN_UNICONIFY);
8171 BINDKEY(MODSHIFT, XK_v, FN_VERSION);
8172 BINDKEY(MODKEY, XK_equal, FN_WIDTH_GROW);
8173 BINDKEY(MODKEY, XK_minus, FN_WIDTH_SHRINK);
8174 BINDKEY(MODKEY, XK_x, FN_WIND_DEL);
8175 BINDKEY(MODSHIFT, XK_x, FN_WIND_KILL);
8176 BINDKEY(MODKEY, XK_1, FN_WS_1);
8177 BINDKEY(MODKEY, XK_2, FN_WS_2);
8178 BINDKEY(MODKEY, XK_3, FN_WS_3);
8179 BINDKEY(MODKEY, XK_4, FN_WS_4);
8180 BINDKEY(MODKEY, XK_5, FN_WS_5);
8181 BINDKEY(MODKEY, XK_6, FN_WS_6);
8182 BINDKEY(MODKEY, XK_7, FN_WS_7);
8183 BINDKEY(MODKEY, XK_8, FN_WS_8);
8184 BINDKEY(MODKEY, XK_9, FN_WS_9);
8185 BINDKEY(MODKEY, XK_0, FN_WS_10);
8186 BINDKEY(MODKEY, XK_F1, FN_WS_11);
8187 BINDKEY(MODKEY, XK_F2, FN_WS_12);
8188 BINDKEY(MODKEY, XK_F3, FN_WS_13);
8189 BINDKEY(MODKEY, XK_F4, FN_WS_14);
8190 BINDKEY(MODKEY, XK_F5, FN_WS_15);
8191 BINDKEY(MODKEY, XK_F6, FN_WS_16);
8192 BINDKEY(MODKEY, XK_F7, FN_WS_17);
8193 BINDKEY(MODKEY, XK_F8, FN_WS_18);
8194 BINDKEY(MODKEY, XK_F9, FN_WS_19);
8195 BINDKEY(MODKEY, XK_F10, FN_WS_20);
8196 BINDKEY(MODKEY, XK_F11, FN_WS_21);
8197 BINDKEY(MODKEY, XK_F12, FN_WS_22);
8198 BINDKEY(MODKEY, XK_Right, FN_WS_NEXT);
8199 BINDKEY(MODKEY, XK_Left, FN_WS_PREV);
8200 BINDKEY(MODKEY, XK_Up, FN_WS_NEXT_ALL);
8201 BINDKEY(MODKEY, XK_Down, FN_WS_PREV_ALL);
8202 BINDKEY(MODSHIFT, XK_Up, FN_WS_NEXT_MOVE);
8203 BINDKEY(MODSHIFT, XK_Down, FN_WS_PREV_MOVE);
8204 BINDKEY(MODKEY, XK_a, FN_WS_PRIOR);
8205 #ifdef SWM_DEBUG
8206 BINDKEY(MODKEY, XK_d, FN_DEBUG_TOGGLE);
8207 BINDKEY(MODSHIFT, XK_d, FN_DUMPWINS);
8208 #endif
8209 #undef BINDKEY
8210 #undef BINDKEYSPAWN
8211 }
8212
8213 void
8214 setup_btnbindings(void)
8215 {
8216 setbinding(ANYMOD, BTNBIND, XCB_BUTTON_INDEX_1, FN_FOCUS,
8217 BINDING_F_REPLAY, NULL);
8218 setbinding(MODKEY, BTNBIND, XCB_BUTTON_INDEX_3, FN_RESIZE, 0, NULL);
8219 setbinding(MODSHIFT, BTNBIND, XCB_BUTTON_INDEX_3, FN_RESIZE_CENTERED, 0,
8220 NULL);
8221 setbinding(MODKEY, BTNBIND, XCB_BUTTON_INDEX_1, FN_MOVE, 0, NULL);
8222 }
8223 #undef MODSHIFT
8224
8225 void
8226 clear_bindings(void)
8227 {
8228 struct binding *bp;
8229
8230 while ((bp = RB_ROOT(&bindings)))
8231 binding_remove(bp);
8232 }
8233
8234 void
8235 clear_keybindings(void)
8236 {
8237 struct binding *bp, *bptmp;
8238
8239 RB_FOREACH_SAFE(bp, binding_tree, &bindings, bptmp) {
8240 if (bp->type != KEYBIND)
8241 continue;
8242 binding_remove(bp);
8243 }
8244 }
8245
8246 int
8247 setkeymapping(const char *selector, const char *value, int flags)
8248 {
8249 char *keymapping_file;
8250
8251 /* suppress unused warnings since vars are needed */
8252 (void)selector;
8253 (void)flags;
8254
8255 DNPRINTF(SWM_D_KEY, "setkeymapping: enter\n");
8256
8257 keymapping_file = expand_tilde(value);
8258
8259 clear_keybindings();
8260 /* load new key bindings; if it fails, revert to default bindings */
8261 if (conf_load(keymapping_file, SWM_CONF_KEYMAPPING)) {
8262 clear_keybindings();
8263 setup_keybindings();
8264 }
8265
8266 free(keymapping_file);
8267
8268 DNPRINTF(SWM_D_KEY, "setkeymapping: leave\n");
8269 return (0);
8270 }
8271
8272 void
8273 updatenumlockmask(void)
8274 {
8275 unsigned int i, j;
8276 xcb_get_modifier_mapping_reply_t *modmap_r;
8277 xcb_keycode_t *modmap, kc, *keycode;
8278
8279 numlockmask = 0;
8280
8281 modmap_r = xcb_get_modifier_mapping_reply(conn,
8282 xcb_get_modifier_mapping(conn),
8283 NULL);
8284 if (modmap_r) {
8285 modmap = xcb_get_modifier_mapping_keycodes(modmap_r);
8286 for (i = 0; i < 8; i++) {
8287 for (j = 0; j < modmap_r->keycodes_per_modifier; j++) {
8288 kc = modmap[i * modmap_r->keycodes_per_modifier
8289 + j];
8290 keycode = xcb_key_symbols_get_keycode(syms,
8291 XK_Num_Lock);
8292 if (keycode) {
8293 if (kc == *keycode)
8294 numlockmask = (1 << i);
8295 free(keycode);
8296 }
8297 }
8298 }
8299 free(modmap_r);
8300 }
8301 DNPRINTF(SWM_D_MISC, "updatenumlockmask: %d\n", numlockmask);
8302 }
8303
8304 void
8305 grabkeys(void)
8306 {
8307 struct binding *bp;
8308 int num_screens, k, j;
8309 uint16_t modifiers[4];
8310 xcb_keycode_t *code;
8311
8312 DNPRINTF(SWM_D_MISC, "grabkeys\n");
8313 updatenumlockmask();
8314
8315 modifiers[0] = 0;
8316 modifiers[1] = numlockmask;
8317 modifiers[2] = XCB_MOD_MASK_LOCK;
8318 modifiers[3] = numlockmask | XCB_MOD_MASK_LOCK;
8319
8320 num_screens = get_screen_count();
8321 for (k = 0; k < num_screens; k++) {
8322 if (TAILQ_EMPTY(&screens[k].rl))
8323 continue;
8324 xcb_ungrab_key(conn, XCB_GRAB_ANY, screens[k].root,
8325 XCB_MOD_MASK_ANY);
8326 RB_FOREACH(bp, binding_tree, &bindings) {
8327 if (bp->type != KEYBIND)
8328 continue;
8329
8330 /* If there is a catch-all, only bind that. */
8331 if ((binding_lookup(ANYMOD, KEYBIND, bp->value)) &&
8332 bp->mod != ANYMOD)
8333 continue;
8334
8335 /* Skip unused ws binds. */
8336 if ((int)bp->action > FN_WS_1 + workspace_limit - 1 &&
8337 bp->action <= FN_WS_22)
8338 continue;
8339
8340 /* Skip unused mvws binds. */
8341 if ((int)bp->action > FN_MVWS_1 + workspace_limit - 1 &&
8342 bp->action <= FN_MVWS_22)
8343 continue;
8344
8345 if ((code = xcb_key_symbols_get_keycode(syms,
8346 bp->value)) == NULL)
8347 continue;
8348
8349 if (bp->mod == XCB_MOD_MASK_ANY) {
8350 /* All modifiers are grabbed in one pass. */
8351 DNPRINTF(SWM_D_MOUSE, "grabkeys: grab, "
8352 "key: %u, modifiers: %d\n", bp->value,
8353 bp->mod);
8354 xcb_grab_key(conn, 1, screens[k].root,
8355 bp->mod, *code, XCB_GRAB_MODE_ASYNC,
8356 XCB_GRAB_MODE_SYNC);
8357 } else {
8358 /* Need to grab each modifier permutation. */
8359 for (j = 0; j < LENGTH(modifiers); j++) {
8360 DNPRINTF(SWM_D_MOUSE, "grabkeys: grab, "
8361 "key: %u, modifiers: %d\n",
8362 bp->value, bp->mod | modifiers[j]);
8363 xcb_grab_key(conn, 1,
8364 screens[k].root,
8365 bp->mod | modifiers[j],
8366 *code, XCB_GRAB_MODE_ASYNC,
8367 XCB_GRAB_MODE_SYNC);
8368 }
8369 }
8370 free(code);
8371 }
8372 }
8373 }
8374
8375 void
8376 grabbuttons(void)
8377 {
8378 struct binding *bp;
8379 int num_screens, i, k;
8380 uint16_t modifiers[4];
8381
8382 DNPRINTF(SWM_D_MOUSE, "grabbuttons\n");
8383 updatenumlockmask();
8384
8385 modifiers[0] = 0;
8386 modifiers[1] = numlockmask;
8387 modifiers[2] = XCB_MOD_MASK_LOCK;
8388 modifiers[3] = numlockmask | XCB_MOD_MASK_LOCK;
8389
8390 num_screens = get_screen_count();
8391 for (k = 0; k < num_screens; k++) {
8392 if (TAILQ_EMPTY(&screens[k].rl))
8393 continue;
8394 xcb_ungrab_button(conn, XCB_BUTTON_INDEX_ANY, screens[k].root,
8395 XCB_MOD_MASK_ANY);
8396 RB_FOREACH(bp, binding_tree, &bindings) {
8397 if (bp->type != BTNBIND)
8398 continue;
8399
8400 /* If there is a catch-all, only bind that. */
8401 if ((binding_lookup(ANYMOD, BTNBIND, bp->value)) &&
8402 bp->mod != ANYMOD)
8403 continue;
8404
8405 /* Skip unused ws binds. */
8406 if ((int)bp->action > FN_WS_1 + workspace_limit - 1 &&
8407 bp->action <= FN_WS_22)
8408 continue;
8409
8410 /* Skip unused mvws binds. */
8411 if ((int)bp->action > FN_MVWS_1 + workspace_limit - 1 &&
8412 bp->action <= FN_MVWS_22)
8413 continue;
8414
8415 if (bp->mod == XCB_MOD_MASK_ANY) {
8416 /* All modifiers are grabbed in one pass. */
8417 DNPRINTF(SWM_D_MOUSE, "grabbuttons: grab, "
8418 "button: %u, modifiers: %d\n", bp->value,
8419 bp->mod);
8420 xcb_grab_button(conn, 0, screens[k].root,
8421 BUTTONMASK, XCB_GRAB_MODE_SYNC,
8422 XCB_GRAB_MODE_ASYNC, XCB_WINDOW_NONE,
8423 XCB_CURSOR_NONE, bp->value, bp->mod);
8424 } else {
8425 /* Need to grab each modifier permutation. */
8426 for (i = 0; i < LENGTH(modifiers); ++i) {
8427 DNPRINTF(SWM_D_MOUSE, "grabbuttons: "
8428 "grab, button: %u, modifiers: %u\n",
8429 bp->value, bp->mod | modifiers[i]);
8430 xcb_grab_button(conn, 0,
8431 screens[k].root, BUTTONMASK,
8432 XCB_GRAB_MODE_SYNC,
8433 XCB_GRAB_MODE_ASYNC,
8434 XCB_WINDOW_NONE,
8435 XCB_CURSOR_NONE, bp->value,
8436 bp->mod | modifiers[i]);
8437 }
8438 }
8439 }
8440 }
8441 }
8442
8443 const char *quirkname[] = {
8444 "NONE", /* config string for "no value" */
8445 "FLOAT",
8446 "TRANSSZ",
8447 "ANYWHERE",
8448 "XTERM_FONTADJ",
8449 "FULLSCREEN",
8450 "FOCUSPREV",
8451 "NOFOCUSONMAP",
8452 "FOCUSONMAP_SINGLE",
8453 "OBEYAPPFOCUSREQ",
8454 "IGNOREPID",
8455 "IGNORESPAWNWS",
8456 "NOFOCUSCYCLE",
8457 "MINIMALBORDER",
8458 };
8459
8460 /* SWM_Q_DELIM: retain '|' for back compat for now (2009-08-11) */
8461 #define SWM_Q_DELIM "\n|+ \t"
8462 int
8463 parsequirks(const char *qstr, uint32_t *quirk, int *ws)
8464 {
8465 char *str, *cp, *name;
8466 int i;
8467
8468 if (quirk == NULL || qstr == NULL)
8469 return (1);
8470
8471 if ((cp = str = strdup(qstr)) == NULL)
8472 err(1, "parsequirks: strdup");
8473
8474 *quirk = 0;
8475 while ((name = strsep(&cp, SWM_Q_DELIM)) != NULL) {
8476 if (cp)
8477 cp += (long)strspn(cp, SWM_Q_DELIM);
8478
8479 if (sscanf(name, "WS[%d]", ws) == 1) {
8480 if (*ws > 0)
8481 *ws -= 1;
8482 continue;
8483 }
8484
8485 for (i = 0; i < LENGTH(quirkname); i++) {
8486 if (strncasecmp(name, quirkname[i],
8487 SWM_QUIRK_LEN) == 0) {
8488 DNPRINTF(SWM_D_QUIRK,
8489 "parsequirks: %s\n", name);
8490 if (i == 0) {
8491 *quirk = 0;
8492 free(str);
8493 return (0);
8494 }
8495 *quirk |= 1 << (i-1);
8496 break;
8497 }
8498 }
8499 if (i >= LENGTH(quirkname)) {
8500 DNPRINTF(SWM_D_QUIRK,
8501 "parsequirks: invalid quirk [%s]\n", name);
8502 free(str);
8503 return (1);
8504 }
8505 }
8506
8507 free(str);
8508 return (0);
8509 }
8510
8511 void
8512 quirk_insert(const char *class, const char *instance, const char *name,
8513 uint32_t quirk, int ws)
8514 {
8515 struct quirk *qp;
8516 char *str;
8517 bool failed = false;
8518
8519 DNPRINTF(SWM_D_QUIRK, "quirk_insert: class: %s, instance: %s, name: %s,"
8520 " value: %u, ws: %d\n", class, instance, name, quirk, ws);
8521
8522 if ((qp = malloc(sizeof *qp)) == NULL)
8523 err(1, "quirk_insert: malloc");
8524
8525 if ((qp->class = strdup(class)) == NULL)
8526 err(1, "quirk_insert: strdup");
8527 if ((qp->instance = strdup(instance)) == NULL)
8528 err(1, "quirk_insert: strdup");
8529 if ((qp->name = strdup(name)) == NULL)
8530 err(1, "quirk_insert: strdup");
8531
8532 if (asprintf(&str, "^%s$", class) == -1)
8533 err(1, "quirk_insert: asprintf");
8534 if (regcomp(&qp->regex_class, str, REG_EXTENDED | REG_NOSUB)) {
8535 add_startup_exception("regex failed to compile quirk 'class' "
8536 "field: %s", class);
8537 failed = true;
8538 }
8539 DNPRINTF(SWM_D_QUIRK, "quirk_insert: compiled: %s\n", str);
8540 free(str);
8541
8542 if (asprintf(&str, "^%s$", instance) == -1)
8543 err(1, "quirk_insert: asprintf");
8544 if (regcomp(&qp->regex_instance, str, REG_EXTENDED | REG_NOSUB)) {
8545 add_startup_exception("regex failed to compile quirk 'instance'"
8546 " field: %s", instance);
8547 failed = true;
8548 }
8549 DNPRINTF(SWM_D_QUIRK, "quirk_insert: compiled: %s\n", str);
8550 free(str);
8551
8552 if (asprintf(&str, "^%s$", name) == -1)
8553 err(1, "quirk_insert: asprintf");
8554 if (regcomp(&qp->regex_name, str, REG_EXTENDED | REG_NOSUB)) {
8555 add_startup_exception("regex failed to compile quirk 'name' "
8556 "field: %s", name);
8557 failed = true;
8558 }
8559 DNPRINTF(SWM_D_QUIRK, "quirk_insert: compiled: %s\n", str);
8560 free(str);
8561
8562 if (failed) {
8563 DNPRINTF(SWM_D_QUIRK, "quirk_insert: regex error; skipping.\n");
8564 quirk_free(qp);
8565 } else {
8566 qp->quirk = quirk;
8567 qp->ws = ws;
8568 TAILQ_INSERT_TAIL(&quirks, qp, entry);
8569 }
8570 DNPRINTF(SWM_D_QUIRK, "quirk_insert: leave\n");
8571 }
8572
8573 void
8574 quirk_remove(struct quirk *qp)
8575 {
8576 DNPRINTF(SWM_D_QUIRK, "quirk_remove: %s:%s [%u]\n", qp->class,
8577 qp->name, qp->quirk);
8578
8579 TAILQ_REMOVE(&quirks, qp, entry);
8580 quirk_free(qp);
8581
8582 DNPRINTF(SWM_D_QUIRK, "quirk_remove: leave\n");
8583 }
8584
8585 void
8586 quirk_free(struct quirk *qp)
8587 {
8588 regfree(&qp->regex_class);
8589 regfree(&qp->regex_instance);
8590 regfree(&qp->regex_name);
8591 free(qp->class);
8592 free(qp->instance);
8593 free(qp->name);
8594 free(qp);
8595 }
8596
8597 void
8598 clear_quirks(void)
8599 {
8600 struct quirk *qp;
8601
8602 while ((qp = TAILQ_FIRST(&quirks)) != NULL) {
8603 quirk_remove(qp);
8604 }
8605 }
8606
8607 void
8608 quirk_replace(struct quirk *qp, const char *class, const char *instance,
8609 const char *name, uint32_t quirk, int ws)
8610 {
8611 DNPRINTF(SWM_D_QUIRK, "quirk_replace: %s:%s:%s [%u], ws: %d\n", qp->class,
8612 qp->instance, qp->name, qp->quirk, qp->ws);
8613
8614 quirk_remove(qp);
8615 quirk_insert(class, instance, name, quirk, ws);
8616
8617 DNPRINTF(SWM_D_QUIRK, "quirk_replace: leave\n");
8618 }
8619
8620 void
8621 setquirk(const char *class, const char *instance, const char *name,
8622 uint32_t quirk, int ws)
8623 {
8624 struct quirk *qp;
8625
8626 DNPRINTF(SWM_D_QUIRK, "setquirk: enter %s:%s:%s [%u], ws: %d\n", class,
8627 instance, name, quirk, ws);
8628
8629 /* Remove/replace existing quirk. */
8630 TAILQ_FOREACH(qp, &quirks, entry) {
8631 if (strcmp(qp->class, class) == 0 &&
8632 strcmp(qp->instance, instance) == 0 &&
8633 strcmp(qp->name, name) == 0) {
8634 if (quirk == 0 && ws == -1)
8635 quirk_remove(qp);
8636 else
8637 quirk_replace(qp, class, instance, name, quirk,
8638 ws);
8639 DNPRINTF(SWM_D_QUIRK, "setquirk: leave\n");
8640 return;
8641 }
8642 }
8643
8644 /* Only insert if quirk is not NONE or forced ws is set. */
8645 if (quirk || ws != -1)
8646 quirk_insert(class, instance, name, quirk, ws);
8647
8648 DNPRINTF(SWM_D_QUIRK, "setquirk: leave\n");
8649 }
8650
8651 /* Eat '\' in str used to escape square brackets and colon. */
8652 void
8653 unescape_selector(char *str)
8654 {
8655 char *cp;
8656
8657 for (cp = str; *str != '\0'; ++str, ++cp) {
8658 if (*str == '\\' && (*(str + 1) == ':' || *(str + 1) == ']' ||
8659 *(str + 1) == '['))
8660 ++str;
8661
8662 *cp = *str;
8663 }
8664 *cp = '\0';
8665 }
8666
8667 int
8668 setconfquirk(const char *selector, const char *value, int flags)
8669 {
8670 char *str, *cp, *class;
8671 char *instance = NULL, *name = NULL;
8672 int retval, count = 0, ws = -1;
8673 uint32_t qrks;
8674
8675 /* suppress unused warning since var is needed */
8676 (void)flags;
8677
8678 if (selector == NULL || strlen(selector) == 0)
8679 return (0);
8680
8681 if ((str = strdup(selector)) == NULL)
8682 err(1, "setconfquirk: strdup");
8683
8684 /* Find non-escaped colon. */
8685 class = cp = str;
8686 if (*cp == ':') {
8687 *cp = '\0';
8688 ++count;
8689 }
8690
8691 for (++cp; *cp != '\0'; ++cp) {
8692 if (*cp == ':' && *(cp - 1) != '\\') {
8693 *cp = '\0';
8694 ++count;
8695 }
8696 }
8697
8698 unescape_selector(class);
8699 if (count) {
8700 instance = class + strlen(class) + 1;
8701 unescape_selector(instance);
8702 } else {
8703 instance = ".*";
8704 }
8705
8706 if (count > 1) {
8707 name = instance + strlen(instance) + 1;
8708 unescape_selector(name);
8709 } else {
8710 name = ".*";
8711 }
8712
8713 DNPRINTF(SWM_D_CONF, "setconfquirk: class: %s, instance: %s, "
8714 "name: %s\n", class, instance, name);
8715
8716 if ((retval = parsequirks(value, &qrks, &ws)) == 0)
8717 setquirk(class, instance, name, qrks, ws);
8718
8719 free(str);
8720 return (retval);
8721 }
8722
8723 void
8724 setup_quirks(void)
8725 {
8726 setquirk("MPlayer", "xv", ".*",
8727 SWM_Q_FLOAT | SWM_Q_FULLSCREEN | SWM_Q_FOCUSPREV, -1);
8728 setquirk("OpenOffice.org 3.2", "VCLSalFrame", ".*",
8729 SWM_Q_FLOAT, -1);
8730 setquirk("Firefox-bin", "firefox-bin", ".*",
8731 SWM_Q_TRANSSZ, -1);
8732 setquirk("Firefox", "Dialog", ".*",
8733 SWM_Q_FLOAT, -1);
8734 setquirk("Gimp", "gimp", ".*",
8735 SWM_Q_FLOAT | SWM_Q_ANYWHERE, -1);
8736 setquirk("XTerm", "xterm", ".*",
8737 SWM_Q_XTERM_FONTADJ, -1);
8738 setquirk("xine", "Xine Window", ".*",
8739 SWM_Q_FLOAT | SWM_Q_ANYWHERE, -1);
8740 setquirk("Xitk", "Xitk Combo", ".*",
8741 SWM_Q_FLOAT | SWM_Q_ANYWHERE, -1);
8742 setquirk("xine", "xine Panel", ".*",
8743 SWM_Q_FLOAT | SWM_Q_ANYWHERE, -1);
8744 setquirk("Xitk", "Xine Window", ".*",
8745 SWM_Q_FLOAT | SWM_Q_ANYWHERE, -1);
8746 setquirk("xine", "xine Video Fullscreen Window", ".*",
8747 SWM_Q_FULLSCREEN | SWM_Q_FLOAT, -1);
8748 setquirk("pcb", "pcb", ".*",
8749 SWM_Q_FLOAT, -1);
8750 setquirk("SDL_App", "SDL_App", ".*",
8751 SWM_Q_FLOAT | SWM_Q_FULLSCREEN, -1);
8752 }
8753
8754 /* conf file stuff */
8755 #define SWM_CONF_FILE "spectrwm.conf"
8756 #define SWM_CONF_FILE_OLD "scrotwm.conf"
8757
8758 enum {
8759 SWM_S_BAR_ACTION,
8760 SWM_S_BAR_AT_BOTTOM,
8761 SWM_S_BAR_BORDER_WIDTH,
8762 SWM_S_BAR_DELAY,
8763 SWM_S_BAR_ENABLED,
8764 SWM_S_BAR_ENABLED_WS,
8765 SWM_S_BAR_FONT,
8766 SWM_S_BAR_FORMAT,
8767 SWM_S_BAR_JUSTIFY,
8768 SWM_S_BORDER_WIDTH,
8769 SWM_S_BOUNDARY_WIDTH,
8770 SWM_S_CLOCK_ENABLED,
8771 SWM_S_CLOCK_FORMAT,
8772 SWM_S_CYCLE_EMPTY,
8773 SWM_S_CYCLE_VISIBLE,
8774 SWM_S_DIALOG_RATIO,
8775 SWM_S_DISABLE_BORDER,
8776 SWM_S_FOCUS_CLOSE,
8777 SWM_S_FOCUS_CLOSE_WRAP,
8778 SWM_S_FOCUS_DEFAULT,
8779 SWM_S_FOCUS_MODE,
8780 SWM_S_ICONIC_ENABLED,
8781 SWM_S_JAVA_WORKAROUND,
8782 SWM_S_MAXIMIZE_HIDE_BAR,
8783 SWM_S_REGION_PADDING,
8784 SWM_S_SPAWN_ORDER,
8785 SWM_S_SPAWN_TERM,
8786 SWM_S_SS_APP,
8787 SWM_S_SS_ENABLED,
8788 SWM_S_STACK_ENABLED,
8789 SWM_S_TERM_WIDTH,
8790 SWM_S_TILE_GAP,
8791 SWM_S_URGENT_COLLAPSE,
8792 SWM_S_URGENT_ENABLED,
8793 SWM_S_VERBOSE_LAYOUT,
8794 SWM_S_WARP_FOCUS,
8795 SWM_S_WARP_POINTER,
8796 SWM_S_WINDOW_CLASS_ENABLED,
8797 SWM_S_WINDOW_INSTANCE_ENABLED,
8798 SWM_S_WINDOW_NAME_ENABLED,
8799 SWM_S_WORKSPACE_CLAMP,
8800 SWM_S_WORKSPACE_LIMIT,
8801 SWM_S_WORKSPACE_NAME,
8802 };
8803
8804 int
8805 setconfvalue(const char *selector, const char *value, int flags)
8806 {
8807 struct workspace *ws;
8808 int i, ws_id, num_screens;
8809 char *b, *str, *sp, s[1024];
8810
8811 switch (flags) {
8812 case SWM_S_BAR_ACTION:
8813 free(bar_argv[0]);
8814 if ((bar_argv[0] = expand_tilde(value)) == NULL)
8815 err(1, "setconfvalue: bar_action");
8816 break;
8817 case SWM_S_BAR_AT_BOTTOM:
8818 bar_at_bottom = (atoi(value) != 0);
8819 break;
8820 case SWM_S_BAR_BORDER_WIDTH:
8821 bar_border_width = atoi(value);
8822 if (bar_border_width < 0)
8823 bar_border_width = 0;
8824 break;
8825 case SWM_S_BAR_DELAY:
8826 /* No longer needed; leave to not break old conf files. */
8827 break;
8828 case SWM_S_BAR_ENABLED:
8829 bar_enabled = (atoi(value) != 0);
8830 break;
8831 case SWM_S_BAR_ENABLED_WS:
8832 ws_id = atoi(selector) - 1;
8833 if (ws_id < 0 || ws_id >= workspace_limit)
8834 errx(1, "setconfvalue: bar_enabled_ws: invalid "
8835 "workspace %d.", ws_id + 1);
8836
8837 num_screens = get_screen_count();
8838 for (i = 0; i < num_screens; i++) {
8839 ws = (struct workspace *)&screens[i].ws;
8840 ws[ws_id].bar_enabled = (atoi(value) != 0);
8841 }
8842 break;
8843 case SWM_S_BAR_FONT:
8844 b = bar_fonts;
8845 if (asprintf(&bar_fonts, "%s,%s", value, bar_fonts) == -1)
8846 err(1, "setconfvalue: asprintf: failed to allocate "
8847 "memory for bar_fonts.");
8848 free(b);
8849
8850 /* If already in xft mode, then we are done. */
8851 if (!bar_font_legacy)
8852 break;
8853
8854 if ((sp = str = strdup(value)) == NULL)
8855 err(1, "setconfvalue: strdup");
8856
8857 /* If there are any non-XLFD entries, switch to Xft mode. */
8858 while ((b = strsep(&sp, ",")) != NULL) {
8859 if (*b == '\0')
8860 continue;
8861 if (!isxlfd(b)) {
8862 bar_font_legacy = false;
8863 break;
8864 }
8865 }
8866
8867 free(str);
8868 break;
8869 case SWM_S_BAR_FORMAT:
8870 free(bar_format);
8871 if ((bar_format = strdup(value)) == NULL)
8872 err(1, "setconfvalue: bar_format");
8873 break;
8874 case SWM_S_BAR_JUSTIFY:
8875 if (strcmp(value, "left") == 0)
8876 bar_justify = SWM_BAR_JUSTIFY_LEFT;
8877 else if (strcmp(value, "center") == 0)
8878 bar_justify = SWM_BAR_JUSTIFY_CENTER;
8879 else if (strcmp(value, "right") == 0)
8880 bar_justify = SWM_BAR_JUSTIFY_RIGHT;
8881 else
8882 errx(1, "invalid bar_justify");
8883 break;
8884 case SWM_S_BORDER_WIDTH:
8885 border_width = atoi(value);
8886 if (border_width < 0)
8887 border_width = 0;
8888 break;
8889 case SWM_S_BOUNDARY_WIDTH:
8890 boundary_width = atoi(value);
8891 if (boundary_width < 0)
8892 boundary_width = 0;
8893 break;
8894 case SWM_S_CLOCK_ENABLED:
8895 clock_enabled = (atoi(value) != 0);
8896 break;
8897 case SWM_S_CLOCK_FORMAT:
8898 #ifndef SWM_DENY_CLOCK_FORMAT
8899 free(clock_format);
8900 if ((clock_format = strdup(value)) == NULL)
8901 err(1, "setconfvalue: clock_format");
8902 #endif
8903 break;
8904 case SWM_S_CYCLE_EMPTY:
8905 cycle_empty = (atoi(value) != 0);
8906 break;
8907 case SWM_S_CYCLE_VISIBLE:
8908 cycle_visible = (atoi(value) != 0);
8909 break;
8910 case SWM_S_DIALOG_RATIO:
8911 dialog_ratio = atof(value);
8912 if (dialog_ratio > 1.0 || dialog_ratio <= .3)
8913 dialog_ratio = .6;
8914 break;
8915 case SWM_S_DISABLE_BORDER:
8916 disable_border = (atoi(value) != 0);
8917 break;
8918 case SWM_S_FOCUS_CLOSE:
8919 if (strcmp(value, "first") == 0)
8920 focus_close = SWM_STACK_BOTTOM;
8921 else if (strcmp(value, "last") == 0)
8922 focus_close = SWM_STACK_TOP;
8923 else if (strcmp(value, "next") == 0)
8924 focus_close = SWM_STACK_ABOVE;
8925 else if (strcmp(value, "previous") == 0)
8926 focus_close = SWM_STACK_BELOW;
8927 else
8928 errx(1, "focus_close");
8929 break;
8930 case SWM_S_FOCUS_CLOSE_WRAP:
8931 focus_close_wrap = (atoi(value) != 0);
8932 break;
8933 case SWM_S_FOCUS_DEFAULT:
8934 if (strcmp(value, "last") == 0)
8935 focus_default = SWM_STACK_TOP;
8936 else if (strcmp(value, "first") == 0)
8937 focus_default = SWM_STACK_BOTTOM;
8938 else
8939 errx(1, "focus_default");
8940 break;
8941 case SWM_S_FOCUS_MODE:
8942 if (strcmp(value, "default") == 0)
8943 focus_mode = SWM_FOCUS_DEFAULT;
8944 else if (strcmp(value, "follow") == 0 ||
8945 strcmp(value, "follow_cursor") == 0)
8946 focus_mode = SWM_FOCUS_FOLLOW;
8947 else if (strcmp(value, "manual") == 0)
8948 focus_mode = SWM_FOCUS_MANUAL;
8949 else
8950 errx(1, "focus_mode");
8951 break;
8952 case SWM_S_ICONIC_ENABLED:
8953 iconic_enabled = (atoi(value) != 0);
8954 break;
8955 case SWM_S_JAVA_WORKAROUND:
8956 java_workaround = (atoi(value) != 0);
8957 break;
8958 case SWM_S_MAXIMIZE_HIDE_BAR:
8959 maximize_hide_bar = atoi(value);
8960 break;
8961 case SWM_S_REGION_PADDING:
8962 region_padding = atoi(value);
8963 if (region_padding < 0)
8964 region_padding = 0;
8965 break;
8966 case SWM_S_SPAWN_ORDER:
8967 if (strcmp(value, "first") == 0)
8968 spawn_position = SWM_STACK_BOTTOM;
8969 else if (strcmp(value, "last") == 0)
8970 spawn_position = SWM_STACK_TOP;
8971 else if (strcmp(value, "next") == 0)
8972 spawn_position = SWM_STACK_ABOVE;
8973 else if (strcmp(value, "previous") == 0)
8974 spawn_position = SWM_STACK_BELOW;
8975 else
8976 errx(1, "spawn_position");
8977 break;
8978 case SWM_S_SPAWN_TERM:
8979 setconfspawn("term", value, 0);
8980 setconfspawn("spawn_term", value, 0);
8981 break;
8982 case SWM_S_SS_APP:
8983 /* No longer needed; leave to not break old conf files. */
8984 break;
8985 case SWM_S_SS_ENABLED:
8986 /* No longer needed; leave to not break old conf files. */
8987 break;
8988 case SWM_S_STACK_ENABLED:
8989 stack_enabled = (atoi(value) != 0);
8990 break;
8991 case SWM_S_TERM_WIDTH:
8992 term_width = atoi(value);
8993 if (term_width < 0)
8994 term_width = 0;
8995 break;
8996 case SWM_S_TILE_GAP:
8997 tile_gap = atoi(value);
8998 break;
8999 case SWM_S_URGENT_COLLAPSE:
9000 urgent_collapse = (atoi(value) != 0);
9001 break;
9002 case SWM_S_URGENT_ENABLED:
9003 urgent_enabled = (atoi(value) != 0);
9004 break;
9005 case SWM_S_VERBOSE_LAYOUT:
9006 verbose_layout = (atoi(value) != 0);
9007 for (i = 0; layouts[i].l_stack != NULL; i++) {
9008 if (verbose_layout)
9009 layouts[i].l_string = fancy_stacker;
9010 else
9011 layouts[i].l_string = plain_stacker;
9012 }
9013 break;
9014 case SWM_S_WARP_FOCUS:
9015 warp_focus = (atoi(value) != 0);
9016 break;
9017 case SWM_S_WARP_POINTER:
9018 warp_pointer = (atoi(value) != 0);
9019 break;
9020 case SWM_S_WINDOW_CLASS_ENABLED:
9021 window_class_enabled = (atoi(value) != 0);
9022 break;
9023 case SWM_S_WINDOW_INSTANCE_ENABLED:
9024 window_instance_enabled = (atoi(value) != 0);
9025 break;
9026 case SWM_S_WINDOW_NAME_ENABLED:
9027 window_name_enabled = (atoi(value) != 0);
9028 break;
9029 case SWM_S_WORKSPACE_CLAMP:
9030 workspace_clamp = (atoi(value) != 0);
9031 break;
9032 case SWM_S_WORKSPACE_LIMIT:
9033 workspace_limit = atoi(value);
9034 if (workspace_limit > SWM_WS_MAX)
9035 workspace_limit = SWM_WS_MAX;
9036 else if (workspace_limit < 1)
9037 workspace_limit = 1;
9038
9039 ewmh_update_desktops();
9040 break;
9041 case SWM_S_WORKSPACE_NAME:
9042 if (getenv("SWM_STARTED") != NULL)
9043 return (0);
9044
9045 bzero(s, sizeof s);
9046 if (sscanf(value, "ws[%d]:%1023c", &ws_id, s) != 2)
9047 errx(1, "invalid entry, should be 'ws[<idx>]:name'");
9048 ws_id--;
9049 if (ws_id < 0 || ws_id >= workspace_limit)
9050 errx(1, "setconfvalue: workspace_name: invalid "
9051 "workspace %d.", ws_id + 1);
9052
9053 num_screens = get_screen_count();
9054 for (i = 0; i < num_screens; ++i) {
9055 ws = (struct workspace *)&screens[i].ws;
9056
9057 if (strlen(s) > 0) {
9058 free(ws[ws_id].name);
9059 if ((ws[ws_id].name = strdup(s)) == NULL)
9060 err(1, "setconfvalue: workspace_name.");
9061
9062 ewmh_update_desktop_names();
9063 ewmh_get_desktop_names();
9064 }
9065 }
9066 break;
9067 default:
9068 return (1);
9069 }
9070 return (0);
9071 }
9072
9073 int
9074 setconfmodkey(const char *selector, const char *value, int flags)
9075 {
9076 /* suppress unused warnings since vars are needed */
9077 (void)selector;
9078 (void)flags;
9079
9080 if (strncasecmp(value, "Mod1", strlen("Mod1")) == 0)
9081 update_modkey(XCB_MOD_MASK_1);
9082 else if (strncasecmp(value, "Mod2", strlen("Mod2")) == 0)
9083 update_modkey(XCB_MOD_MASK_2);
9084 else if (strncasecmp(value, "Mod3", strlen("Mod3")) == 0)
9085 update_modkey(XCB_MOD_MASK_3);
9086 else if (strncasecmp(value, "Mod4", strlen("Mod4")) == 0)
9087 update_modkey(XCB_MOD_MASK_4);
9088 else if (strncasecmp(value, "Mod5", strlen("Mod5")) == 0)
9089 update_modkey(XCB_MOD_MASK_5);
9090 else
9091 return (1);
9092 return (0);
9093 }
9094
9095 int
9096 setconfcolor(const char *selector, const char *value, int flags)
9097 {
9098 int first, last, i = 0, num_screens;
9099
9100 num_screens = get_screen_count();
9101
9102 /* conf screen indices begin at 1; treat vals <= 0 as 'all screens.' */
9103 if (selector == NULL || strlen(selector) == 0 ||
9104 (last = atoi(selector) - 1) < 0) {
9105 first = 0;
9106 last = num_screens - 1;
9107 } else {
9108 first = last;
9109 }
9110
9111 if (last >= num_screens) {
9112 add_startup_exception("invalid screen index: %d out of bounds "
9113 "(maximum %d)", last + 1, num_screens);
9114 return (1);
9115 }
9116
9117 for (i = first; i <= last; ++i) {
9118 setscreencolor(value, i, flags);
9119
9120 /*
9121 * When setting focus/unfocus colors, we need to also
9122 * set maximize colors to match if they haven't been customized.
9123 */
9124 if (flags == SWM_S_COLOR_FOCUS &&
9125 !screens[i].c[SWM_S_COLOR_FOCUS_MAXIMIZED].manual)
9126 setscreencolor(value, i, SWM_S_COLOR_FOCUS_MAXIMIZED);
9127 else if (flags == SWM_S_COLOR_UNFOCUS &&
9128 !screens[i].c[SWM_S_COLOR_UNFOCUS_MAXIMIZED].manual)
9129 setscreencolor(value, i, SWM_S_COLOR_UNFOCUS_MAXIMIZED);
9130
9131 screens[i].c[flags].manual = 1;
9132 }
9133
9134 return (0);
9135 }
9136
9137 int
9138 setconfregion(const char *selector, const char *value, int flags)
9139 {
9140 /* suppress unused warnings since vars are needed */
9141 (void)selector;
9142 (void)flags;
9143
9144 custom_region(value);
9145 return (0);
9146 }
9147
9148 int
9149 setautorun(const char *selector, const char *value, int flags)
9150 {
9151 int ws_id;
9152 char s[1024];
9153 char *ap, *sp, *str;
9154 union arg a;
9155 int argc = 0;
9156 pid_t pid;
9157 struct pid_e *p;
9158
9159 /* suppress unused warnings since vars are needed */
9160 (void)selector;
9161 (void)flags;
9162
9163 if (getenv("SWM_STARTED"))
9164 return (0);
9165
9166 bzero(s, sizeof s);
9167 if (sscanf(value, "ws[%d]:%1023c", &ws_id, s) != 2)
9168 errx(1, "invalid autorun entry, should be 'ws[<idx>]:command'");
9169 ws_id--;
9170 if (ws_id < 0 || ws_id >= workspace_limit)
9171 errx(1, "autorun: invalid workspace %d", ws_id + 1);
9172
9173 sp = str = expand_tilde((char *)&s);
9174
9175 /*
9176 * This is a little intricate
9177 *
9178 * If the pid already exists we simply reuse it because it means it was
9179 * used before AND not claimed by manage_window. We get away with
9180 * altering it in the parent after INSERT because this can not be a race
9181 */
9182 a.argv = NULL;
9183 while ((ap = strsep(&sp, " \t")) != NULL) {
9184 if (*ap == '\0')
9185 continue;
9186 DNPRINTF(SWM_D_SPAWN, "setautorun: arg [%s]\n", ap);
9187 argc++;
9188 if ((a.argv = realloc(a.argv, argc * sizeof(char *))) == NULL)
9189 err(1, "setautorun: realloc");
9190 a.argv[argc - 1] = ap;
9191 }
9192
9193 if ((a.argv = realloc(a.argv, (argc + 1) * sizeof(char *))) == NULL)
9194 err(1, "setautorun: realloc");
9195 a.argv[argc] = NULL;
9196
9197 if ((pid = fork()) == 0) {
9198 spawn(ws_id, &a, true);
9199 /* NOTREACHED */
9200 _exit(1);
9201 }
9202 free(a.argv);
9203 free(str);
9204
9205 /* parent */
9206 p = find_pid(pid);
9207 if (p == NULL) {
9208 p = calloc(1, sizeof *p);
9209 if (p == NULL)
9210 return (1);
9211 TAILQ_INSERT_TAIL(&pidlist, p, entry);
9212 }
9213
9214 p->pid = pid;
9215 p->ws = ws_id;
9216
9217 return (0);
9218 }
9219
9220 int
9221 setlayout(const char *selector, const char *value, int flags)
9222 {
9223 struct workspace *ws;
9224 int ws_id, i, x, mg, ma, si, ar;
9225 int st = SWM_V_STACK, num_screens;
9226 char s[1024];
9227 bool f = false;
9228
9229 /* suppress unused warnings since vars are needed */
9230 (void)selector;
9231 (void)flags;
9232
9233 if (getenv("SWM_STARTED"))
9234 return (0);
9235
9236 bzero(s, sizeof s);
9237 if (sscanf(value, "ws[%d]:%d:%d:%d:%d:%1023c",
9238 &ws_id, &mg, &ma, &si, &ar, s) != 6)
9239 errx(1, "invalid layout entry, should be 'ws[<idx>]:"
9240 "<master_grow>:<master_add>:<stack_inc>:<always_raise>:"
9241 "<type>'");
9242 ws_id--;
9243 if (ws_id < 0 || ws_id >= workspace_limit)
9244 errx(1, "layout: invalid workspace %d", ws_id + 1);
9245
9246 if (strcasecmp(s, "vertical") == 0)
9247 st = SWM_V_STACK;
9248 else if (strcasecmp(s, "vertical_flip") == 0) {
9249 st = SWM_V_STACK;
9250 f = true;
9251 } else if (strcasecmp(s, "horizontal") == 0)
9252 st = SWM_H_STACK;
9253 else if (strcasecmp(s, "horizontal_flip") == 0) {
9254 st = SWM_H_STACK;
9255 f = true;
9256 } else if (strcasecmp(s, "fullscreen") == 0)
9257 st = SWM_MAX_STACK;
9258 else
9259 errx(1, "invalid layout entry, should be 'ws[<idx>]:"
9260 "<master_grow>:<master_add>:<stack_inc>:<always_raise>:"
9261 "<type>'");
9262
9263 num_screens = get_screen_count();
9264 for (i = 0; i < num_screens; i++) {
9265 ws = (struct workspace *)&screens[i].ws;
9266 ws[ws_id].cur_layout = &layouts[st];
9267
9268 ws[ws_id].always_raise = (ar != 0);
9269 if (st == SWM_MAX_STACK)
9270 continue;
9271
9272 /* master grow */
9273 for (x = 0; x < abs(mg); x++) {
9274 ws[ws_id].cur_layout->l_config(&ws[ws_id],
9275 mg >= 0 ? SWM_ARG_ID_MASTERGROW :
9276 SWM_ARG_ID_MASTERSHRINK);
9277 }
9278 /* master add */
9279 for (x = 0; x < abs(ma); x++) {
9280 ws[ws_id].cur_layout->l_config(&ws[ws_id],
9281 ma >= 0 ? SWM_ARG_ID_MASTERADD :
9282 SWM_ARG_ID_MASTERDEL);
9283 }
9284 /* stack inc */
9285 for (x = 0; x < abs(si); x++) {
9286 ws[ws_id].cur_layout->l_config(&ws[ws_id],
9287 si >= 0 ? SWM_ARG_ID_STACKINC :
9288 SWM_ARG_ID_STACKDEC);
9289 }
9290 /* Apply flip */
9291 if (f) {
9292 ws[ws_id].cur_layout->l_config(&ws[ws_id],
9293 SWM_ARG_ID_FLIPLAYOUT);
9294 }
9295 }
9296
9297 return (0);
9298 }
9299
9300 /* config options */
9301 struct config_option {
9302 char *optname;
9303 int (*func)(const char*, const char*, int);
9304 int funcflags;
9305 };
9306 struct config_option configopt[] = {
9307 { "autorun", setautorun, 0 },
9308 { "bar_action", setconfvalue, SWM_S_BAR_ACTION },
9309 { "bar_at_bottom", setconfvalue, SWM_S_BAR_AT_BOTTOM },
9310 { "bar_border", setconfcolor, SWM_S_COLOR_BAR_BORDER },
9311 { "bar_border_unfocus", setconfcolor, SWM_S_COLOR_BAR_BORDER_UNFOCUS },
9312 { "bar_border_width", setconfvalue, SWM_S_BAR_BORDER_WIDTH },
9313 { "bar_color", setconfcolor, SWM_S_COLOR_BAR },
9314 { "bar_delay", setconfvalue, SWM_S_BAR_DELAY },
9315 { "bar_enabled", setconfvalue, SWM_S_BAR_ENABLED },
9316 { "bar_enabled_ws", setconfvalue, SWM_S_BAR_ENABLED_WS },
9317 { "bar_font", setconfvalue, SWM_S_BAR_FONT },
9318 { "bar_font_color", setconfcolor, SWM_S_COLOR_BAR_FONT },
9319 { "bar_format", setconfvalue, SWM_S_BAR_FORMAT },
9320 { "bar_justify", setconfvalue, SWM_S_BAR_JUSTIFY },
9321 { "bind", setconfbinding, 0 },
9322 { "border_width", setconfvalue, SWM_S_BORDER_WIDTH },
9323 { "boundary_width", setconfvalue, SWM_S_BOUNDARY_WIDTH },
9324 { "clock_enabled", setconfvalue, SWM_S_CLOCK_ENABLED },
9325 { "clock_format", setconfvalue, SWM_S_CLOCK_FORMAT },
9326 { "color_focus", setconfcolor, SWM_S_COLOR_FOCUS },
9327 { "color_focus_maximized", setconfcolor, SWM_S_COLOR_FOCUS_MAXIMIZED },
9328 { "color_unfocus", setconfcolor, SWM_S_COLOR_UNFOCUS },
9329 { "color_unfocus_maximized", setconfcolor, SWM_S_COLOR_UNFOCUS_MAXIMIZED },
9330 { "cycle_empty", setconfvalue, SWM_S_CYCLE_EMPTY },
9331 { "cycle_visible", setconfvalue, SWM_S_CYCLE_VISIBLE },
9332 { "dialog_ratio", setconfvalue, SWM_S_DIALOG_RATIO },
9333 { "disable_border", setconfvalue, SWM_S_DISABLE_BORDER },
9334 { "focus_close", setconfvalue, SWM_S_FOCUS_CLOSE },
9335 { "focus_close_wrap", setconfvalue, SWM_S_FOCUS_CLOSE_WRAP },
9336 { "focus_default", setconfvalue, SWM_S_FOCUS_DEFAULT },
9337 { "focus_mode", setconfvalue, SWM_S_FOCUS_MODE },
9338 { "iconic_enabled", setconfvalue, SWM_S_ICONIC_ENABLED },
9339 { "java_workaround", setconfvalue, SWM_S_JAVA_WORKAROUND },
9340 { "keyboard_mapping", setkeymapping, 0 },
9341 { "layout", setlayout, 0 },
9342 { "maximize_hide_bar", setconfvalue, SWM_S_MAXIMIZE_HIDE_BAR },
9343 { "modkey", setconfmodkey, 0 },
9344 { "program", setconfspawn, 0 },
9345 { "quirk", setconfquirk, 0 },
9346 { "region", setconfregion, 0 },
9347 { "region_padding", setconfvalue, SWM_S_REGION_PADDING },
9348 { "screenshot_app", setconfvalue, SWM_S_SS_APP },
9349 { "screenshot_enabled", setconfvalue, SWM_S_SS_ENABLED },
9350 { "spawn_position", setconfvalue, SWM_S_SPAWN_ORDER },
9351 { "spawn_term", setconfvalue, SWM_S_SPAWN_TERM },
9352 { "stack_enabled", setconfvalue, SWM_S_STACK_ENABLED },
9353 { "term_width", setconfvalue, SWM_S_TERM_WIDTH },
9354 { "tile_gap", setconfvalue, SWM_S_TILE_GAP },
9355 { "title_class_enabled", setconfvalue, SWM_S_WINDOW_CLASS_ENABLED }, /* For backwards compat. */
9356 { "title_name_enabled", setconfvalue, SWM_S_WINDOW_INSTANCE_ENABLED }, /* For backwards compat. */
9357 { "urgent_collapse", setconfvalue, SWM_S_URGENT_COLLAPSE },
9358 { "urgent_enabled", setconfvalue, SWM_S_URGENT_ENABLED },
9359 { "verbose_layout", setconfvalue, SWM_S_VERBOSE_LAYOUT },
9360 { "warp_focus", setconfvalue, SWM_S_WARP_FOCUS },
9361 { "warp_pointer", setconfvalue, SWM_S_WARP_POINTER },
9362 { "window_class_enabled", setconfvalue, SWM_S_WINDOW_CLASS_ENABLED },
9363 { "window_instance_enabled", setconfvalue, SWM_S_WINDOW_INSTANCE_ENABLED },
9364 { "window_name_enabled", setconfvalue, SWM_S_WINDOW_NAME_ENABLED },
9365 { "workspace_clamp", setconfvalue, SWM_S_WORKSPACE_CLAMP },
9366 { "workspace_limit", setconfvalue, SWM_S_WORKSPACE_LIMIT },
9367 { "name", setconfvalue, SWM_S_WORKSPACE_NAME },
9368 };
9369
9370 void
9371 _add_startup_exception(const char *fmt, va_list ap)
9372 {
9373 if (vasprintf(&startup_exception, fmt, ap) == -1)
9374 warn("%s: asprintf", __func__);
9375 }
9376
9377 void
9378 add_startup_exception(const char *fmt, ...)
9379 {
9380 va_list ap;
9381
9382 nr_exceptions++;
9383
9384 if (startup_exception)
9385 return;
9386
9387 /* force bar to be enabled due to exception */
9388 bar_enabled = true;
9389
9390 va_start(ap, fmt);
9391 _add_startup_exception(fmt, ap);
9392 va_end(ap);
9393 }
9394
9395 int
9396 conf_load(const char *filename, int keymapping)
9397 {
9398 FILE *config;
9399 char *line = NULL, *cp, *ce, *optsub, *optval = NULL;
9400 size_t linelen, lineno = 0;
9401 int wordlen, i, optidx, count;
9402 struct config_option *opt = NULL;
9403
9404 DNPRINTF(SWM_D_CONF, "conf_load: begin\n");
9405
9406 if (filename == NULL) {
9407 warnx("conf_load: no filename");
9408 return (1);
9409 }
9410
9411 DNPRINTF(SWM_D_CONF, "conf_load: open %s\n", filename);
9412
9413 if ((config = fopen(filename, "r")) == NULL) {
9414 warn("conf_load: fopen: %s", filename);
9415 return (1);
9416 }
9417
9418 while (!feof(config)) {
9419 if (line)
9420 free(line);
9421
9422 if ((line = fparseln(config, &linelen, &lineno, NULL,
9423 FPARSELN_UNESCCOMM | FPARSELN_UNESCCONT)) == NULL) {
9424 if (ferror(config))
9425 err(1, "%s", filename);
9426 else
9427 continue;
9428 }
9429 cp = line;
9430 cp += strspn(cp, " \t\n"); /* eat whitespace */
9431 if (cp[0] == '\0') {
9432 /* empty line */
9433 continue;
9434 }
9435 /* get config option */
9436 wordlen = strcspn(cp, "=[ \t\n");
9437 if (wordlen == 0) {
9438 add_startup_exception("%s: line %zd: no option found",
9439 filename, lineno);
9440 continue;
9441 }
9442 optidx = -1;
9443 for (i = 0; i < LENGTH(configopt); i++) {
9444 opt = &configopt[i];
9445 if (strncasecmp(cp, opt->optname, wordlen) == 0 &&
9446 (int)strlen(opt->optname) == wordlen) {
9447 optidx = i;
9448 break;
9449 }
9450 }
9451 if (optidx == -1) {
9452 add_startup_exception("%s: line %zd: unknown option "
9453 "%.*s", filename, lineno, wordlen, cp);
9454 continue;
9455 }
9456 if (keymapping && opt && strcmp(opt->optname, "bind")) {
9457 add_startup_exception("%s: line %zd: invalid option "
9458 "%.*s", filename, lineno, wordlen, cp);
9459 continue;
9460 }
9461 cp += wordlen;
9462 cp += strspn(cp, " \t\n"); /* eat whitespace */
9463
9464 /* get [selector] if any */
9465 optsub = NULL;
9466 count = 0;
9467 if (*cp == '[') {
9468 ++count;
9469 /* Get length of selector. */
9470 for (ce = ++cp; *ce != '\0'; ++ce) {
9471 /* Find matching (not escaped) bracket. */
9472 if (*ce == ']' && *(ce - 1) != '\\') {
9473 --count;
9474 break;
9475 }
9476 }
9477
9478 if (count > 0) {
9479 add_startup_exception("%s: line %zd: syntax "
9480 "error: unterminated selector", filename,
9481 lineno);
9482 continue;
9483 }
9484
9485 /* ce points at ']'; terminate optsub. */
9486 *ce = '\0';
9487 optsub = cp;
9488 cp = ce + 1;
9489 }
9490 cp += strspn(cp, "= \t\n"); /* eat trailing */
9491 /* get RHS value */
9492 optval = cp;
9493 if (strlen(optval) == 0) {
9494 add_startup_exception("%s: line %zd: must supply value "
9495 "to %s", filename, lineno,
9496 configopt[optidx].optname);
9497 continue;
9498 }
9499 /* call function to deal with it all */
9500 if (configopt[optidx].func(optsub, optval,
9501 configopt[optidx].funcflags) != 0) {
9502 add_startup_exception("%s: line %zd: invalid data for "
9503 "%s", filename, lineno, configopt[optidx].optname);
9504 continue;
9505 }
9506 }
9507
9508 if (line)
9509 free(line);
9510 fclose(config);
9511
9512 DNPRINTF(SWM_D_CONF, "conf_load: end\n");
9513
9514 return (0);
9515 }
9516
9517 void
9518 set_child_transient(struct ws_win *win, xcb_window_t *trans)
9519 {
9520 struct ws_win *parent, *w;
9521 struct swm_region *r;
9522 struct workspace *ws;
9523 xcb_icccm_wm_hints_t wmh;
9524
9525 parent = find_window(win->transient);
9526 if (parent)
9527 parent->focus_child = win;
9528 else {
9529 DNPRINTF(SWM_D_MISC, "set_child_transient: parent doesn't exist"
9530 " for %#x trans %#x\n", win->id, win->transient);
9531
9532 r = root_to_region(win->s->root, SWM_CK_ALL);
9533 ws = r->ws;
9534 /* parent doen't exist in our window list */
9535 TAILQ_FOREACH(w, &ws->winlist, entry) {
9536 if (xcb_icccm_get_wm_hints_reply(conn,
9537 xcb_icccm_get_wm_hints(conn, w->id),
9538 &wmh, NULL) != 1) {
9539 warnx("can't get hints for %#x", w->id);
9540 continue;
9541 }
9542
9543 if (win->hints.window_group != wmh.window_group)
9544 continue;
9545
9546 w->focus_child = win;
9547 win->transient = w->id;
9548 *trans = w->id;
9549 DNPRINTF(SWM_D_MISC, "set_child_transient: adjusting "
9550 "transient to %#x\n", win->transient);
9551 break;
9552 }
9553 }
9554 }
9555
9556 pid_t
9557 window_get_pid(xcb_window_t win)
9558 {
9559 pid_t ret = 0;
9560 const char *errstr;
9561 xcb_atom_t apid;
9562 xcb_get_property_cookie_t pc;
9563 xcb_get_property_reply_t *pr;
9564
9565 apid = get_atom_from_string("_NET_WM_PID");
9566 if (apid == XCB_ATOM_NONE)
9567 goto tryharder;
9568
9569 pc = xcb_get_property(conn, 0, win, apid, XCB_ATOM_CARDINAL, 0, 1);
9570 pr = xcb_get_property_reply(conn, pc, NULL);
9571 if (pr == NULL)
9572 goto tryharder;
9573 if (pr->type != XCB_ATOM_CARDINAL) {
9574 free(pr);
9575 goto tryharder;
9576 }
9577
9578 if (pr->type == apid && pr->format == 32)
9579 ret = *((pid_t *)xcb_get_property_value(pr));
9580 free(pr);
9581
9582 return (ret);
9583
9584 tryharder:
9585 apid = get_atom_from_string("_SWM_PID");
9586 pc = xcb_get_property(conn, 0, win, apid, XCB_ATOM_STRING,
9587 0, SWM_PROPLEN);
9588 pr = xcb_get_property_reply(conn, pc, NULL);
9589 if (pr == NULL)
9590 return (0);
9591 if (pr->type != apid) {
9592 free(pr);
9593 return (0);
9594 }
9595
9596 ret = (pid_t)strtonum(xcb_get_property_value(pr), 0, INT_MAX, &errstr);
9597 free(pr);
9598
9599 return (ret);
9600 }
9601
9602 int
9603 get_swm_ws(xcb_window_t id)
9604 {
9605 int ws_idx = -1;
9606 char *prop = NULL;
9607 size_t proplen;
9608 const char *errstr;
9609 xcb_get_property_reply_t *gpr;
9610
9611 gpr = xcb_get_property_reply(conn,
9612 xcb_get_property(conn, 0, id, a_swm_ws,
9613 XCB_ATOM_STRING, 0, SWM_PROPLEN),
9614 NULL);
9615 if (gpr == NULL)
9616 return (-1);
9617 if (gpr->type) {
9618 proplen = xcb_get_property_value_length(gpr);
9619 if (proplen > 0) {
9620 prop = malloc(proplen + 1);
9621 if (prop) {
9622 memcpy(prop,
9623 xcb_get_property_value(gpr),
9624 proplen);
9625 prop[proplen] = '\0';
9626 }
9627 }
9628 }
9629 free(gpr);
9630
9631 if (prop) {
9632 DNPRINTF(SWM_D_PROP, "get_swm_ws: _SWM_WS: %s\n", prop);
9633 ws_idx = (int)strtonum(prop, 0, workspace_limit - 1, &errstr);
9634 if (errstr) {
9635 DNPRINTF(SWM_D_PROP, "get_swm_ws: win #%s: %s",
9636 errstr, prop);
9637 }
9638 free(prop);
9639 }
9640
9641 return ws_idx;
9642 }
9643
9644 int
9645 get_ws_idx(struct ws_win *win)
9646 {
9647 xcb_get_property_reply_t *gpr;
9648 int ws_idx = -1;
9649
9650 if (win == NULL)
9651 return -1;
9652
9653 gpr = xcb_get_property_reply(conn,
9654 xcb_get_property(conn, 0, win->id, ewmh[_NET_WM_DESKTOP].atom,
9655 XCB_ATOM_CARDINAL, 0, 1),
9656 NULL);
9657 if (gpr) {
9658 if (gpr->type == XCB_ATOM_CARDINAL && gpr->format == 32)
9659 ws_idx = *((int *)xcb_get_property_value(gpr));
9660 free(gpr);
9661 }
9662
9663 if (ws_idx == -1 && !(win->quirks & SWM_Q_IGNORESPAWNWS))
9664 ws_idx = get_swm_ws(win->id);
9665
9666 if (ws_idx > workspace_limit - 1 || ws_idx < -1)
9667 ws_idx = -1;
9668
9669 DNPRINTF(SWM_D_PROP, "get_ws_idx: win %#x, ws_idx: %d\n", win->id,
9670 ws_idx);
9671
9672 return ws_idx;
9673 }
9674
9675 void
9676 reparent_window(struct ws_win *win)
9677 {
9678 xcb_void_cookie_t c;
9679 xcb_generic_error_t *error;
9680 uint32_t wa[2];
9681
9682 win->frame = xcb_generate_id(conn);
9683
9684 DNPRINTF(SWM_D_MISC, "reparent_window: win %#x, frame: %#x\n",
9685 win->id, win->frame);
9686
9687 wa[0] =
9688 XCB_EVENT_MASK_ENTER_WINDOW |
9689 XCB_EVENT_MASK_STRUCTURE_NOTIFY |
9690 XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT |
9691 XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY |
9692 XCB_EVENT_MASK_EXPOSURE;
9693 #ifdef SWM_DEBUG
9694 wa[0] |= XCB_EVENT_MASK_LEAVE_WINDOW | XCB_EVENT_MASK_FOCUS_CHANGE;
9695 #endif
9696
9697 xcb_create_window(conn, XCB_COPY_FROM_PARENT, win->frame, win->s->root,
9698 X(win), Y(win), WIDTH(win), HEIGHT(win), 0,
9699 XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_COPY_FROM_PARENT,
9700 XCB_CW_EVENT_MASK, wa);
9701
9702 win->state = SWM_WIN_STATE_REPARENTING;
9703 c = xcb_reparent_window_checked(conn, win->id, win->frame, 0, 0);
9704 if ((error = xcb_request_check(conn, c))) {
9705 DNPRINTF(SWM_D_MISC, "reparent_window: error:\n");
9706 event_error(error);
9707 free(error);
9708
9709 /* Abort. */
9710 xcb_destroy_window(conn, win->frame);
9711 win->frame = XCB_WINDOW_NONE;
9712 } else {
9713 xcb_change_save_set(conn, XCB_SET_MODE_INSERT, win->id);
9714 }
9715 DNPRINTF(SWM_D_MISC, "reparent_window: done.\n");
9716 }
9717
9718 void
9719 unparent_window(struct ws_win *win)
9720 {
9721 xcb_change_save_set(conn, XCB_SET_MODE_DELETE, win->id);
9722 xcb_reparent_window(conn, win->id, win->s->root, X(win), Y(win));
9723 xcb_destroy_window(conn, win->frame);
9724 win->frame = XCB_WINDOW_NONE;
9725 win->state = SWM_WIN_STATE_UNPARENTING;
9726 }
9727
9728 struct ws_win *
9729 manage_window(xcb_window_t id, int spawn_pos, bool mapping)
9730 {
9731 struct ws_win *win = NULL, *ww;
9732 struct swm_region *r;
9733 struct pid_e *p;
9734 struct quirk *qp;
9735 xcb_get_geometry_reply_t *gr;
9736 xcb_get_window_attributes_reply_t *war = NULL;
9737 xcb_window_t trans = XCB_WINDOW_NONE;
9738 uint32_t i, wa[1], new_flags;
9739 int ws_idx, force_ws = -1;
9740 char *class, *instance, *name;
9741
9742 if (find_bar(id)) {
9743 DNPRINTF(SWM_D_MISC, "manage_window: win %#x is region bar; "
9744 "skipping.\n", id);
9745 goto out;
9746 }
9747
9748 if (find_region(id)) {
9749 DNPRINTF(SWM_D_MISC, "manage_window: win %#x is region window; "
9750 "skipping.\n", id);
9751 goto out;
9752 }
9753
9754 if ((win = find_window(id)) != NULL) {
9755 DNPRINTF(SWM_D_MISC, "manage_window: win %#x already "
9756 "managed; skipping.)\n", id);
9757 return (win); /* Already managed. */
9758 }
9759
9760 /* See if window is on the unmanaged list. */
9761 if ((win = find_unmanaged_window(id)) != NULL) {
9762 DNPRINTF(SWM_D_MISC, "manage_window: win %#x found on "
9763 "unmanaged list.\n", id);
9764 TAILQ_REMOVE(&win->ws->unmanagedlist, win, entry);
9765
9766 if (TRANS(win))
9767 set_child_transient(win, &trans);
9768
9769 goto remanage;
9770 } else {
9771 DNPRINTF(SWM_D_MISC, "manage_window: win %#x is new.\n", id);
9772 }
9773
9774 war = xcb_get_window_attributes_reply(conn,
9775 xcb_get_window_attributes(conn, id), NULL);
9776 if (war == NULL) {
9777 DNPRINTF(SWM_D_EVENT, "manage_window: window lost.\n");
9778 goto out;
9779 }
9780
9781 if (war->override_redirect) {
9782 DNPRINTF(SWM_D_EVENT, "manage_window: override_redirect; "
9783 "skipping.\n");
9784 goto out;
9785 }
9786
9787 if (!mapping && war->map_state == XCB_MAP_STATE_UNMAPPED &&
9788 get_win_state(id) == XCB_ICCCM_WM_STATE_WITHDRAWN) {
9789 DNPRINTF(SWM_D_EVENT, "manage_window: window withdrawn; "
9790 "skipping.\n");
9791 goto out;
9792 }
9793
9794 /* Try to get initial window geometry. */
9795 gr = xcb_get_geometry_reply(conn, xcb_get_geometry(conn, id), NULL);
9796 if (gr == NULL) {
9797 DNPRINTF(SWM_D_MISC, "manage_window: get geometry failed.\n");
9798 goto out;
9799 }
9800
9801 /* Create and initialize ws_win object. */
9802 if ((win = calloc(1, sizeof(struct ws_win))) == NULL)
9803 err(1, "manage_window: calloc: failed to allocate memory for "
9804 "new window");
9805
9806 win->id = id;
9807
9808 /* Figureout which region the window belongs to. */
9809 r = root_to_region(gr->root, SWM_CK_ALL);
9810
9811 /* Ignore window border if there is one. */
9812 WIDTH(win) = gr->width;
9813 HEIGHT(win) = gr->height;
9814 X(win) = gr->x + gr->border_width;
9815 Y(win) = gr->y + gr->border_width;
9816 win->bordered = false;
9817 win->mapped = (war->map_state != XCB_MAP_STATE_UNMAPPED);
9818 win->s = r->s; /* this never changes */
9819
9820 free(gr);
9821
9822 /* Select which X events to monitor and set border pixel color. */
9823 wa[0] = XCB_EVENT_MASK_ENTER_WINDOW | XCB_EVENT_MASK_PROPERTY_CHANGE |
9824 XCB_EVENT_MASK_STRUCTURE_NOTIFY;
9825
9826 xcb_change_window_attributes(conn, win->id, XCB_CW_EVENT_MASK, wa);
9827
9828 /* Get WM_SIZE_HINTS. */
9829 xcb_icccm_get_wm_normal_hints_reply(conn,
9830 xcb_icccm_get_wm_normal_hints(conn, win->id),
9831 &win->sh, NULL);
9832
9833 /* Get WM_HINTS. */
9834 xcb_icccm_get_wm_hints_reply(conn,
9835 xcb_icccm_get_wm_hints(conn, win->id),
9836 &win->hints, NULL);
9837
9838 /* Get WM_TRANSIENT_FOR; see if window is a transient. */
9839 xcb_icccm_get_wm_transient_for_reply(conn,
9840 xcb_icccm_get_wm_transient_for(conn, win->id),
9841 &trans, NULL);
9842 if (trans) {
9843 win->transient = trans;
9844 set_child_transient(win, &win->transient);
9845 }
9846
9847 /* Get WM_PROTOCOLS. */
9848 get_wm_protocols(win);
9849
9850 #ifdef SWM_DEBUG
9851 /* Must be after getting WM_HINTS and WM_PROTOCOLS. */
9852 DNPRINTF(SWM_D_FOCUS, "manage_window: input_model: %s\n",
9853 get_win_input_model(win));
9854 #endif
9855
9856 /* Set initial quirks based on EWMH. */
9857 ewmh_autoquirk(win);
9858
9859 /* Determine initial quirks. */
9860 xcb_icccm_get_wm_class_reply(conn,
9861 xcb_icccm_get_wm_class(conn, win->id),
9862 &win->ch, NULL);
9863
9864 class = win->ch.class_name ? win->ch.class_name : "";
9865 instance = win->ch.instance_name ? win->ch.instance_name : "";
9866 name = get_win_name(win->id);
9867
9868 DNPRINTF(SWM_D_CLASS, "manage_window: class: %s, instance: %s, "
9869 "name: %s\n", class, instance, name);
9870
9871 /* java is retarded so treat it special */
9872 if (strstr(instance, "sun-awt")) {
9873 DNPRINTF(SWM_D_CLASS, "manage_window: java window detected.\n");
9874 win->java = true;
9875 }
9876
9877 TAILQ_FOREACH(qp, &quirks, entry) {
9878 if (regexec(&qp->regex_class, class, 0, NULL, 0) == 0 &&
9879 regexec(&qp->regex_instance, instance, 0, NULL, 0) == 0 &&
9880 regexec(&qp->regex_name, name, 0, NULL, 0) == 0) {
9881 DNPRINTF(SWM_D_CLASS, "manage_window: matched "
9882 "quirk: %s:%s:%s mask: %#x, ws: %d\n", qp->class,
9883 qp->instance, qp->name, qp->quirk, qp->ws);
9884 win->quirks = qp->quirk;
9885 if (qp->ws >= 0 && qp->ws < workspace_limit)
9886 force_ws = qp->ws;
9887 }
9888 }
9889
9890 free(name);
9891
9892 /* Reset font sizes (the bruteforce way; no default keybinding). */
9893 if (win->quirks & SWM_Q_XTERM_FONTADJ) {
9894 for (i = 0; i < SWM_MAX_FONT_STEPS; i++)
9895 fake_keypress(win, XK_KP_Subtract, XCB_MOD_MASK_SHIFT);
9896 for (i = 0; i < SWM_MAX_FONT_STEPS; i++)
9897 fake_keypress(win, XK_KP_Add, XCB_MOD_MASK_SHIFT);
9898 }
9899
9900 /* Figure out which workspace the window belongs to. */
9901 if (!(win->quirks & SWM_Q_IGNOREPID) &&
9902 (p = find_pid(window_get_pid(win->id))) != NULL) {
9903 win->ws = &r->s->ws[p->ws];
9904 TAILQ_REMOVE(&pidlist, p, entry);
9905 free(p);
9906 p = NULL;
9907 } else if ((ws_idx = get_ws_idx(win)) != -1 &&
9908 !TRANS(win)) {
9909 /* _SWM_WS is set; use that. */
9910 win->ws = &r->s->ws[ws_idx];
9911 } else if (trans && (ww = find_window(trans)) != NULL) {
9912 /* Launch transients in the same ws as parent. */
9913 win->ws = ww->ws;
9914 } else {
9915 win->ws = r->ws;
9916 }
9917
9918 if (force_ws != -1)
9919 win->ws = &r->s->ws[force_ws];
9920
9921 /* Set the _NET_WM_DESKTOP atom. */
9922 DNPRINTF(SWM_D_PROP, "manage_window: set _NET_WM_DESKTOP: %d\n",
9923 win->ws->idx);
9924 xcb_change_property(conn, XCB_PROP_MODE_REPLACE, win->id,
9925 ewmh[_NET_WM_DESKTOP].atom, XCB_ATOM_CARDINAL, 32, 1, &win->ws->idx);
9926
9927 /* Remove any _SWM_WS now that we set _NET_WM_DESKTOP. */
9928 xcb_delete_property(conn, win->id, a_swm_ws);
9929
9930 /* WS must already be set for this to work. */
9931 store_float_geom(win);
9932
9933 /* Make sure window is positioned inside its region, if its active. */
9934 if (win->ws->r) {
9935 region_containment(win, r, SWM_CW_ALLSIDES |
9936 SWM_CW_HARDBOUNDARY);
9937 update_window(win);
9938 }
9939
9940 remanage:
9941 /* Figure out where to insert the window in the workspace list. */
9942 if (trans && (ww = find_window(trans)))
9943 TAILQ_INSERT_AFTER(&win->ws->winlist, ww, win, entry);
9944 else if (win->ws->focus && spawn_pos == SWM_STACK_ABOVE)
9945 TAILQ_INSERT_AFTER(&win->ws->winlist, win->ws->focus, win,
9946 entry);
9947 else if (win->ws->focus && spawn_pos == SWM_STACK_BELOW)
9948 TAILQ_INSERT_BEFORE(win->ws->focus, win, entry);
9949 else switch (spawn_pos) {
9950 default:
9951 case SWM_STACK_TOP:
9952 case SWM_STACK_ABOVE:
9953 TAILQ_INSERT_TAIL(&win->ws->winlist, win, entry);
9954 break;
9955 case SWM_STACK_BOTTOM:
9956 case SWM_STACK_BELOW:
9957 TAILQ_INSERT_HEAD(&win->ws->winlist, win, entry);
9958 }
9959
9960 ewmh_update_client_list();
9961
9962 TAILQ_INSERT_HEAD(&win->ws->stack, win, stack_entry);
9963 lower_window(win);
9964
9965 /* Get/apply initial _NET_WM_STATE */
9966 ewmh_get_wm_state(win);
9967
9968 /* Apply quirks. */
9969 new_flags = win->ewmh_flags;
9970
9971 if (win->quirks & SWM_Q_FLOAT)
9972 new_flags |= EWMH_F_ABOVE;
9973
9974 if (win->quirks & SWM_Q_ANYWHERE)
9975 new_flags |= SWM_F_MANUAL;
9976
9977 ewmh_apply_flags(win, new_flags);
9978 ewmh_update_wm_state(win);
9979
9980 /* Set initial _NET_WM_ALLOWED_ACTIONS */
9981 ewmh_update_actions(win);
9982
9983 reparent_window(win);
9984
9985 DNPRINTF(SWM_D_MISC, "manage_window: done. win %#x, (x,y) w x h: "
9986 "(%d,%d) %d x %d, ws: %d, iconic: %s, transient: %#x\n", win->id,
9987 X(win), Y(win), WIDTH(win), HEIGHT(win), win->ws->idx,
9988 YESNO(ICONIC(win)), win->transient);
9989 out:
9990 free(war);
9991 return (win);
9992 }
9993
9994 void
9995 free_window(struct ws_win *win)
9996 {
9997 DNPRINTF(SWM_D_MISC, "free_window: win %#x\n", WINID(win));
9998
9999 if (win == NULL)
10000 return;
10001
10002 xcb_icccm_get_wm_class_reply_wipe(&win->ch);
10003
10004 /* paint memory */
10005 memset(win, 0xff, sizeof *win); /* XXX kill later */
10006
10007 free(win);
10008 DNPRINTF(SWM_D_MISC, "free_window: done.\n");
10009 }
10010
10011 void
10012 unmanage_window(struct ws_win *win)
10013 {
10014 DNPRINTF(SWM_D_MISC, "unmanage_window: win %#x\n", WINID(win));
10015
10016 if (win == NULL)
10017 return;
10018
10019 kill_refs(win);
10020 unparent_window(win);
10021
10022 TAILQ_REMOVE(&win->ws->stack, win, stack_entry);
10023 TAILQ_REMOVE(&win->ws->winlist, win, entry);
10024 TAILQ_INSERT_TAIL(&win->ws->unmanagedlist, win, entry);
10025
10026 ewmh_update_client_list();
10027 }
10028
10029 void
10030 expose(xcb_expose_event_t *e)
10031 {
10032 struct ws_win *w;
10033 struct swm_bar *b;
10034 #ifdef SWM_DEBUG
10035 struct workspace *ws;
10036 #endif
10037
10038 DNPRINTF(SWM_D_EVENT, "expose: win %#x, count: %d\n", e->window,
10039 e->count);
10040
10041 if (e->count > 0)
10042 return;
10043
10044 if ((b = find_bar(e->window))) {
10045 bar_draw(b);
10046 xcb_flush(conn);
10047 } else if ((w = find_window(e->window)) && w->frame == e->window) {
10048 draw_frame(w);
10049 #ifdef SWM_DEBUG
10050 ws = w->ws;
10051 TAILQ_FOREACH(w, &ws->winlist, entry)
10052 debug_refresh(w);
10053 #endif
10054 xcb_flush(conn);
10055 }
10056
10057 DNPRINTF(SWM_D_EVENT, "expose: done\n");
10058 }
10059
10060 void
10061 focusin(xcb_focus_in_event_t *e)
10062 {
10063 struct ws_win *win;
10064
10065 DNPRINTF(SWM_D_EVENT, "focusin: win %#x, mode: %s(%u), "
10066 "detail: %s(%u)\n", e->event, get_notify_mode_label(e->mode),
10067 e->mode, get_notify_detail_label(e->detail), e->detail);
10068 if ((win = find_window(e->event)) && win != win->ws->focus &&
10069 win != win->ws->focus_pending &&
10070 e->mode == XCB_NOTIFY_MODE_NORMAL) {
10071 win->ws->focus_prev = win->ws->focus;
10072 win->ws->focus = win;
10073 win->ws->focus_pending = NULL;
10074
10075 if (win->ws->focus_prev)
10076 draw_frame(win->ws->focus_prev);
10077 draw_frame(win);
10078 raise_window(win);
10079 }
10080 }
10081
10082 #ifdef SWM_DEBUG
10083 void
10084 focusout(xcb_focus_out_event_t *e)
10085 {
10086 DNPRINTF(SWM_D_EVENT, "focusout: win %#x, mode: %s(%u), "
10087 "detail: %s(%u)\n", e->event, get_notify_mode_label(e->mode),
10088 e->mode, get_notify_detail_label(e->detail), e->detail);
10089 }
10090 #endif
10091
10092 void
10093 keypress(xcb_key_press_event_t *e)
10094 {
10095 struct action *ap;
10096 struct binding *bp;
10097 xcb_keysym_t keysym;
10098 bool replay = true;
10099
10100 last_event_time = e->time;
10101
10102 keysym = xcb_key_press_lookup_keysym(syms, e, 0);
10103
10104 DNPRINTF(SWM_D_EVENT, "keypress: keysym: %u, win (x,y): %#x (%d,%d), "
10105 "detail: %u, time: %u, root (x,y): %#x (%d,%d), child: %#x, "
10106 "state: %u, cleaned: %u, same_screen: %s\n", keysym, e->event,
10107 e->event_x, e->event_y, e->detail, e->time, e->root, e->root_x,
10108 e->root_y, e->child, e->state, CLEANMASK(e->state),
10109 YESNO(e->same_screen));
10110
10111 bp = binding_lookup(CLEANMASK(e->state), KEYBIND, keysym);
10112 if (bp == NULL) {
10113 /* Look for catch-all. */
10114 if ((bp = binding_lookup(ANYMOD, KEYBIND, keysym)) == NULL)
10115 goto out;
10116 }
10117
10118 replay = bp->flags & BINDING_F_REPLAY;
10119
10120 if ((ap = &actions[bp->action]) == NULL)
10121 goto out;
10122
10123 if (bp->action == FN_SPAWN_CUSTOM)
10124 spawn_custom(root_to_region(e->root, SWM_CK_ALL), &ap->args,
10125 bp->spawn_name);
10126 else if (ap->func)
10127 ap->func(bp, root_to_region(e->root, SWM_CK_ALL), &ap->args);
10128
10129 replay = replay && !(ap->flags & FN_F_NOREPLAY);
10130
10131 out:
10132 if (replay) {
10133 DNPRINTF(SWM_D_EVENT, "keypress: replaying.\n");
10134 /* Pass keypress to event window and unfreeze keyboard queue. */
10135 xcb_allow_events(conn, XCB_ALLOW_REPLAY_KEYBOARD, e->time);
10136 } else {
10137 /* Release freeze. */
10138 xcb_allow_events(conn, XCB_ALLOW_SYNC_KEYBOARD, e->time);
10139 }
10140 xcb_flush(conn);
10141
10142 DNPRINTF(SWM_D_EVENT, "keypress: done.\n");
10143 }
10144
10145 void
10146 keyrelease(xcb_key_release_event_t *e)
10147 {
10148 struct action *ap;
10149 struct binding *bp;
10150 xcb_keysym_t keysym;
10151
10152 last_event_time = e->time;
10153
10154 keysym = xcb_key_release_lookup_keysym(syms, e, 0);
10155
10156 DNPRINTF(SWM_D_EVENT, "keyrelease: keysym: %u, win (x,y): %#x (%d,%d), "
10157 "detail: %u, time: %u, root (x,y): %#x (%d,%d), child: %#x, "
10158 "state: %u, same_screen: %s\n", keysym, e->event, e->event_x,
10159 e->event_y, e->detail, e->time, e->root, e->root_x, e->root_y,
10160 e->child, e->state, YESNO(e->same_screen));
10161
10162 bp = binding_lookup(CLEANMASK(e->state), KEYBIND, keysym);
10163 if (bp == NULL)
10164 /* Look for catch-all. */
10165 bp = binding_lookup(ANYMOD, KEYBIND, keysym);
10166
10167 if (bp && (ap = &actions[bp->action]) && !(ap->flags & FN_F_NOREPLAY) &&
10168 bp->flags & BINDING_F_REPLAY) {
10169 xcb_allow_events(conn, XCB_ALLOW_REPLAY_KEYBOARD, e->time);
10170 DNPRINTF(SWM_D_EVENT, "keyrelease: replaying.\n");
10171 } else {
10172 xcb_allow_events(conn, XCB_ALLOW_SYNC_KEYBOARD, e->time);
10173 DNPRINTF(SWM_D_EVENT, "keyrelease: unfreezing.\n");
10174 }
10175
10176 xcb_flush(conn);
10177
10178 DNPRINTF(SWM_D_EVENT, "keyrelease: done.\n");
10179 }
10180
10181 void
10182 buttonpress(xcb_button_press_event_t *e)
10183 {
10184 struct ws_win *win = NULL, *newf;
10185 struct swm_region *r, *old_r;
10186 struct action *ap;
10187 struct binding *bp;
10188 bool replay = true;
10189
10190 last_event_time = e->time;
10191
10192 DNPRINTF(SWM_D_EVENT, "buttonpress: win (x,y): %#x (%d,%d), "
10193 "detail: %u, time: %u, root (x,y): %#x (%d,%d), child: %#x, "
10194 "state: %u, same_screen: %s\n", e->event, e->event_x, e->event_y,
10195 e->detail, e->time, e->root, e->root_x, e->root_y, e->child,
10196 e->state, YESNO(e->same_screen));
10197
10198 if (e->event == e->root) {
10199 if (e->child) {
10200 win = find_window(e->child);
10201 } else {
10202 /* Focus on empty region */
10203 /* If no windows on region if its empty. */
10204 r = root_to_region(e->root, SWM_CK_POINTER);
10205 if (r && TAILQ_EMPTY(&r->ws->winlist)) {
10206 old_r = root_to_region(e->root, SWM_CK_FOCUS);
10207 if (old_r && old_r != r)
10208 unfocus_win(old_r->ws->focus);
10209
10210 DNPRINTF(SWM_D_FOCUS, "buttonpress: "
10211 "set_input_focus: %#x, revert-to: parent, "
10212 "time: %#x\n", e->root, e->time);
10213 xcb_set_input_focus(conn,
10214 XCB_INPUT_FOCUS_POINTER_ROOT,
10215 e->root, e->time);
10216
10217 /* Clear bar since empty. */
10218 bar_draw(r->bar);
10219
10220 /* No need to replay event. */
10221 replay = false;
10222 }
10223 }
10224 } else {
10225 win = find_window(e->event);
10226 }
10227
10228 if (win) {
10229 newf = get_focus_magic(win);
10230 if (win->ws->focus == newf && newf != win) {
10231 if (win->focus_child == win)
10232 win->focus_child = NULL;
10233 newf = win;
10234 }
10235 focus_win(newf);
10236 }
10237
10238 /* Handle any bound action. */
10239 bp = binding_lookup(CLEANMASK(e->state), BTNBIND, e->detail);
10240 if (bp == NULL) {
10241 /* Look for catch-all. */
10242 if ((bp = binding_lookup(ANYMOD, BTNBIND, e->detail)) == NULL)
10243 goto out;
10244
10245 }
10246
10247 replay = bp->flags & BINDING_F_REPLAY;
10248
10249 if ((ap = &actions[bp->action]) == NULL)
10250 goto out;
10251
10252 if (bp->action == FN_SPAWN_CUSTOM)
10253 spawn_custom(root_to_region(e->root, SWM_CK_ALL), &ap->args,
10254 bp->spawn_name);
10255 else if (ap->func)
10256 ap->func(bp, root_to_region(e->root, SWM_CK_ALL), &ap->args);
10257
10258 replay = replay && !(ap->flags & FN_F_NOREPLAY);
10259
10260 out:
10261 if (replay) {
10262 DNPRINTF(SWM_D_EVENT, "buttonpress: replaying.\n");
10263 /* Replay event to event window */
10264 xcb_allow_events(conn, XCB_ALLOW_REPLAY_POINTER, e->time);
10265 } else {
10266 /* Unfreeze grab events. */
10267 xcb_allow_events(conn, XCB_ALLOW_SYNC_POINTER, e->time);
10268 }
10269
10270 focus_flush();
10271 }
10272
10273 void
10274 buttonrelease(xcb_button_release_event_t *e)
10275 {
10276 struct action *ap;
10277 struct binding *bp;
10278
10279 last_event_time = e->time;
10280
10281 DNPRINTF(SWM_D_EVENT, "buttonrelease: win (x,y): %#x (%d,%d), "
10282 "detail: %u, time: %u, root (x,y): %#x (%d,%d), child: %#x, "
10283 "state: %u, same_screen: %s\n", e->event, e->event_x, e->event_y,
10284 e->detail, e->time, e->root, e->root_x, e->root_y, e->child,
10285 e->state, YESNO(e->same_screen));
10286
10287 bp = binding_lookup(CLEANMASK(e->state), BTNBIND, e->detail);
10288 if (bp == NULL)
10289 /* Look for catch-all. */
10290 bp = binding_lookup(ANYMOD, BTNBIND, e->detail);
10291
10292 if (bp && (ap = &actions[bp->action]) && !(ap->flags & FN_F_NOREPLAY) &&
10293 bp->flags & BINDING_F_REPLAY) {
10294 DNPRINTF(SWM_D_EVENT, "buttonrelease: replaying.\n");
10295 xcb_allow_events(conn, XCB_ALLOW_REPLAY_POINTER, e->time);
10296 } else {
10297 xcb_allow_events(conn, XCB_ALLOW_SYNC_POINTER, e->time);
10298 }
10299
10300 xcb_flush(conn);
10301 }
10302
10303 #ifdef SWM_DEBUG
10304 char *
10305 get_win_input_model(struct ws_win *win)
10306 {
10307 char *inputmodel;
10308 /*
10309 * Input Model Input Field WM_TAKE_FOCUS
10310 * No Input False Absent
10311 * Passive True Absent
10312 * Locally Active True Present
10313 * Globally Active False Present
10314 */
10315
10316 if (ACCEPTS_FOCUS(win))
10317 inputmodel = (win->take_focus) ? "Locally Active" : "Passive";
10318 else
10319 inputmodel = (win->take_focus) ? "Globally Active" : "No Input";
10320
10321 return inputmodel;
10322 }
10323
10324 void
10325 print_win_geom(xcb_window_t w)
10326 {
10327 xcb_get_geometry_reply_t *wa;
10328
10329 wa = xcb_get_geometry_reply(conn, xcb_get_geometry(conn, w), NULL);
10330 if (wa == NULL) {
10331 DNPRINTF(SWM_D_MISC, "print_win_geom: window not found: %#x\n",
10332 w);
10333 return;
10334 }
10335
10336 DNPRINTF(SWM_D_MISC, "print_win_geom: win %#x, root: %#x, "
10337 "depth: %u, (x,y) w x h: (%d,%d) %d x %d, border: %d\n",
10338 w, wa->root, wa->depth, wa->x, wa->y, wa->width, wa->height,
10339 wa->border_width);
10340
10341 free(wa);
10342 }
10343 #endif
10344
10345 #ifdef SWM_DEBUG
10346 char *
10347 get_stack_mode_name(uint8_t mode)
10348 {
10349 char *name;
10350
10351 switch(mode) {
10352 case XCB_STACK_MODE_ABOVE:
10353 name = "Above";
10354 break;
10355 case XCB_STACK_MODE_BELOW:
10356 name = "Below";
10357 break;
10358 case XCB_STACK_MODE_TOP_IF:
10359 name = "TopIf";
10360 break;
10361 case XCB_STACK_MODE_BOTTOM_IF:
10362 name = "BottomIf";
10363 break;
10364 case XCB_STACK_MODE_OPPOSITE:
10365 name = "Opposite";
10366 break;
10367 default:
10368 name = "Unknown";
10369 }
10370
10371 return name;
10372 }
10373 #endif
10374
10375 void
10376 configurerequest(xcb_configure_request_event_t *e)
10377 {
10378 struct ws_win *win;
10379 struct swm_region *r = NULL;
10380 int i = 0;
10381 uint32_t wc[7] = {0};
10382 uint16_t mask = 0;
10383 bool new = false;
10384
10385 if ((win = find_window(e->window)) == NULL)
10386 if ((win = find_unmanaged_window(e->window)) == NULL)
10387 new = true;
10388
10389 #ifdef SWM_DEBUG
10390 if (swm_debug & SWM_D_EVENT) {
10391 print_win_geom(e->window);
10392
10393 DNPRINTF(SWM_D_EVENT, "configurerequest: win %#x, "
10394 "parent: %#x, new: %s, value_mask: %u { ", e->window,
10395 e->parent, YESNO(new), e->value_mask);
10396 if (e->value_mask & XCB_CONFIG_WINDOW_X)
10397 DPRINTF("X: %d ", e->x);
10398 if (e->value_mask & XCB_CONFIG_WINDOW_Y)
10399 DPRINTF("Y: %d ", e->y);
10400 if (e->value_mask & XCB_CONFIG_WINDOW_WIDTH)
10401 DPRINTF("W: %u ", e->width);
10402 if (e->value_mask & XCB_CONFIG_WINDOW_HEIGHT)
10403 DPRINTF("H: %u ", e->height);
10404 if (e->value_mask & XCB_CONFIG_WINDOW_BORDER_WIDTH)
10405 DPRINTF("Border: %u ", e->border_width);
10406 if (e->value_mask & XCB_CONFIG_WINDOW_SIBLING)
10407 DPRINTF("Sibling: %#x ", e->sibling);
10408 if (e->value_mask & XCB_CONFIG_WINDOW_STACK_MODE)
10409 DPRINTF("StackMode: %s(%u) ",
10410 get_stack_mode_name(e->stack_mode), e->stack_mode);
10411 DPRINTF("}\n");
10412 }
10413 #endif
10414
10415 if (new) {
10416 if (e->value_mask & XCB_CONFIG_WINDOW_X) {
10417 mask |= XCB_CONFIG_WINDOW_X;
10418 wc[i++] = e->x;
10419 }
10420 if (e->value_mask & XCB_CONFIG_WINDOW_Y) {
10421 mask |= XCB_CONFIG_WINDOW_Y;
10422 wc[i++] = e->y;
10423 }
10424 if (e->value_mask & XCB_CONFIG_WINDOW_WIDTH) {
10425 mask |= XCB_CONFIG_WINDOW_WIDTH;
10426 wc[i++] = e->width;
10427 }
10428 if (e->value_mask & XCB_CONFIG_WINDOW_HEIGHT) {
10429 mask |= XCB_CONFIG_WINDOW_HEIGHT;
10430 wc[i++] = e->height;
10431 }
10432 if (e->value_mask & XCB_CONFIG_WINDOW_BORDER_WIDTH) {
10433 mask |= XCB_CONFIG_WINDOW_BORDER_WIDTH;
10434 wc[i++] = e->border_width;
10435 }
10436 if (e->value_mask & XCB_CONFIG_WINDOW_SIBLING) {
10437 mask |= XCB_CONFIG_WINDOW_SIBLING;
10438 wc[i++] = e->sibling;
10439 }
10440 if (e->value_mask & XCB_CONFIG_WINDOW_STACK_MODE) {
10441 mask |= XCB_CONFIG_WINDOW_STACK_MODE;
10442 wc[i++] = e->stack_mode;
10443 }
10444
10445 if (mask != 0) {
10446 xcb_configure_window(conn, e->window, mask, wc);
10447 xcb_flush(conn);
10448 }
10449 } else if ((!MANUAL(win) || win->quirks & SWM_Q_ANYWHERE) &&
10450 !FULLSCREEN(win) && !MAXIMIZED(win)) {
10451 if (win->ws->r)
10452 r = win->ws->r;
10453 else if (win->ws->old_r)
10454 r = win->ws->old_r;
10455
10456 /* windows are centered unless ANYWHERE quirk is set. */
10457 if (win->quirks & SWM_Q_ANYWHERE) {
10458 if (e->value_mask & XCB_CONFIG_WINDOW_X) {
10459 win->g_float.x = e->x;
10460 if (r)
10461 win->g_float.x -= X(r);
10462 }
10463
10464 if (e->value_mask & XCB_CONFIG_WINDOW_Y) {
10465 win->g_float.y = e->y;
10466 if (r)
10467 win->g_float.y -= Y(r);
10468 }
10469 }
10470
10471 if (e->value_mask & XCB_CONFIG_WINDOW_WIDTH)
10472 win->g_float.w = e->width;
10473
10474 if (e->value_mask & XCB_CONFIG_WINDOW_HEIGHT)
10475 win->g_float.h = e->height;
10476
10477 win->g_floatvalid = true;
10478
10479 if (!MAXIMIZED(win) && !FULLSCREEN(win) &&
10480 (TRANS(win) || (ABOVE(win) &&
10481 win->ws->cur_layout != &layouts[SWM_MAX_STACK]))) {
10482 WIDTH(win) = win->g_float.w;
10483 HEIGHT(win) = win->g_float.h;
10484
10485 if (r != NULL) {
10486 update_floater(win);
10487 focus_flush();
10488 } else {
10489 config_win(win, e);
10490 xcb_flush(conn);
10491 }
10492 } else {
10493 config_win(win, e);
10494 xcb_flush(conn);
10495 }
10496 } else {
10497 config_win(win, e);
10498 xcb_flush(conn);
10499 }
10500
10501 DNPRINTF(SWM_D_EVENT, "configurerequest: done.\n");
10502 }
10503
10504 void
10505 configurenotify(xcb_configure_notify_event_t *e)
10506 {
10507 struct ws_win *win;
10508
10509 DNPRINTF(SWM_D_EVENT, "configurenotify: win %#x, event win: %#x, "
10510 "(x,y) WxH: (%d,%d) %ux%u, border: %u, above_sibling: %#x, "
10511 "override_redirect: %s\n", e->window, e->event, e->x, e->y,
10512 e->width, e->height, e->border_width, e->above_sibling,
10513 YESNO(e->override_redirect));
10514
10515 win = find_window(e->window);
10516 if (win) {
10517 adjust_font(win);
10518 if (font_adjusted && win->ws->r) {
10519 stack(win->ws->r);
10520 xcb_flush(conn);
10521 }
10522 }
10523 }
10524
10525 void
10526 destroynotify(xcb_destroy_notify_event_t *e)
10527 {
10528 struct ws_win *win;
10529 struct workspace *ws;
10530
10531 DNPRINTF(SWM_D_EVENT, "destroynotify: win %#x\n", e->window);
10532
10533 if ((win = find_window(e->window)) == NULL) {
10534 if ((win = find_unmanaged_window(e->window)) == NULL)
10535 goto out;
10536 /* Window is on unmanaged list. */
10537 TAILQ_REMOVE(&win->ws->unmanagedlist, win, entry);
10538 free_window(win);
10539 goto out;
10540 }
10541
10542 ws = win->ws;
10543
10544 if (win->frame == e->window) {
10545 DNPRINTF(SWM_D_EVENT, "destroynotify: frame for win %#x\n",
10546 win->id);
10547 win->frame = XCB_WINDOW_NONE;
10548 goto out;
10549 }
10550
10551 if (focus_mode != SWM_FOCUS_FOLLOW) {
10552 /* If we were focused, make sure we focus on something else. */
10553 if (win == ws->focus) {
10554 ws->focus_pending = get_focus_prev(win);
10555 if (ws->focus_pending == win)
10556 ws->focus_pending = NULL;
10557 }
10558 }
10559
10560 unmanage_window(win);
10561 TAILQ_REMOVE(&win->ws->unmanagedlist, win, entry);
10562 free_window(win);
10563 stack(ws->r);
10564
10565 if (focus_mode != SWM_FOCUS_FOLLOW && WS_FOCUSED(ws)) {
10566 if (ws->focus_pending) {
10567 focus_win(ws->focus_pending);
10568 ws->focus_pending = NULL;
10569 } else if (ws->focus == NULL) {
10570 DNPRINTF(SWM_D_FOCUS, "destroynotify: set_input_focus: "
10571 "%#x, revert-to: parent, time: 0\n", ws->r->id);
10572 xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT,
10573 ws->r->id, XCB_CURRENT_TIME);
10574 }
10575
10576 focus_flush();
10577 }
10578
10579 out:
10580 DNPRINTF(SWM_D_EVENT, "destroynotify: done.\n");
10581 }
10582
10583 #ifdef SWM_DEBUG
10584 char *
10585 get_notify_detail_label(uint8_t detail)
10586 {
10587 char *label;
10588
10589 switch (detail) {
10590 case XCB_NOTIFY_DETAIL_ANCESTOR:
10591 label = "Ancestor";
10592 break;
10593 case XCB_NOTIFY_DETAIL_VIRTUAL:
10594 label = "Virtual";
10595 break;
10596 case XCB_NOTIFY_DETAIL_INFERIOR:
10597 label = "Inferior";
10598 break;
10599 case XCB_NOTIFY_DETAIL_NONLINEAR:
10600 label = "Nonlinear";
10601 break;
10602 case XCB_NOTIFY_DETAIL_NONLINEAR_VIRTUAL:
10603 label = "NonlinearVirtual";
10604 break;
10605 case XCB_NOTIFY_DETAIL_POINTER:
10606 label = "Pointer";
10607 break;
10608 case XCB_NOTIFY_DETAIL_POINTER_ROOT:
10609 label = "PointerRoot";
10610 break;
10611 case XCB_NOTIFY_DETAIL_NONE:
10612 label = "None";
10613 break;
10614 default:
10615 label = "Unknown";
10616 }
10617
10618 return label;
10619 }
10620
10621 char *
10622 get_notify_mode_label(uint8_t mode)
10623 {
10624 char *label;
10625
10626 switch (mode) {
10627 case XCB_NOTIFY_MODE_NORMAL:
10628 label = "Normal";
10629 break;
10630 case XCB_NOTIFY_MODE_GRAB:
10631 label = "Grab";
10632 break;
10633 case XCB_NOTIFY_MODE_UNGRAB:
10634 label = "Ungrab";
10635 break;
10636 case XCB_NOTIFY_MODE_WHILE_GRABBED:
10637 label = "WhileGrabbed";
10638 break;
10639 default:
10640 label = "Unknown";
10641 }
10642
10643 return label;
10644 }
10645
10646 char *
10647 get_state_mask_label(uint16_t state)
10648 {
10649 char *label;
10650
10651 switch (state) {
10652 case XCB_KEY_BUT_MASK_SHIFT:
10653 label = "ShiftMask";
10654 break;
10655 case XCB_KEY_BUT_MASK_LOCK:
10656 label = "LockMask";
10657 break;
10658 case XCB_KEY_BUT_MASK_CONTROL:
10659 label = "ControlMask";
10660 break;
10661 case XCB_KEY_BUT_MASK_MOD_1:
10662 label = "Mod1Mask";
10663 break;
10664 case XCB_KEY_BUT_MASK_MOD_2:
10665 label = "Mod2Mask";
10666 break;
10667 case XCB_KEY_BUT_MASK_MOD_3:
10668 label = "Mod3Mask";
10669 break;
10670 case XCB_KEY_BUT_MASK_MOD_4:
10671 label = "Mod4Mask";
10672 break;
10673 case XCB_KEY_BUT_MASK_MOD_5:
10674 label = "Mod5Mask";
10675 break;
10676 case XCB_KEY_BUT_MASK_BUTTON_1:
10677 label = "Button1Mask";
10678 break;
10679 case XCB_KEY_BUT_MASK_BUTTON_2:
10680 label = "Button2Mask";
10681 break;
10682 case XCB_KEY_BUT_MASK_BUTTON_3:
10683 label = "Button3Mask";
10684 break;
10685 case XCB_KEY_BUT_MASK_BUTTON_4:
10686 label = "Button4Mask";
10687 break;
10688 case XCB_KEY_BUT_MASK_BUTTON_5:
10689 label = "Button5Mask";
10690 break;
10691 case 0:
10692 label = "None";
10693 break;
10694 default:
10695 label = "Unknown";
10696 }
10697
10698 return label;
10699 }
10700 #endif
10701
10702 void
10703 enternotify(xcb_enter_notify_event_t *e)
10704 {
10705 struct ws_win *win;
10706 struct swm_region *r;
10707
10708 last_event_time = e->time;
10709
10710 DNPRINTF(SWM_D_FOCUS, "enternotify: time: %u, win (x,y): %#x "
10711 "(%d,%d), mode: %s(%d), detail: %s(%d), root (x,y): %#x (%d,%d), "
10712 "child: %#x, same_screen_focus: %s, state: %s(%d)\n",
10713 e->time, e->event, e->event_x, e->event_y,
10714 get_notify_mode_label(e->mode), e->mode,
10715 get_notify_detail_label(e->detail), e->detail,
10716 e->root, e->root_x, e->root_y, e->child,
10717 YESNO(e->same_screen_focus), get_state_mask_label(e->state),
10718 e->state);
10719
10720 if (e->event == e->root && e->child == XCB_WINDOW_NONE &&
10721 e->mode == XCB_NOTIFY_MODE_GRAB &&
10722 e->detail == XCB_NOTIFY_DETAIL_INFERIOR) {
10723 DNPRINTF(SWM_D_EVENT, "enternotify: grab inferior; ignoring.\n");
10724 return;
10725 }
10726
10727 if (focus_mode == SWM_FOCUS_MANUAL &&
10728 e->mode == XCB_NOTIFY_MODE_NORMAL) {
10729 DNPRINTF(SWM_D_EVENT, "enternotify: manual focus; ignoring.\n");
10730 return;
10731 }
10732
10733 if (focus_mode != SWM_FOCUS_FOLLOW &&
10734 e->mode == XCB_NOTIFY_MODE_UNGRAB &&
10735 e->detail != XCB_NOTIFY_DETAIL_ANCESTOR) {
10736 DNPRINTF(SWM_D_EVENT, "enternotify: ungrab; ignoring.\n");
10737 return;
10738 }
10739
10740 if ((win = find_window(e->event)) == NULL) {
10741 if (e->event == e->root) {
10742 /* If no windows on pointer region, then focus root. */
10743 r = root_to_region(e->root, SWM_CK_POINTER);
10744 if (r == NULL) {
10745 DNPRINTF(SWM_D_EVENT, "enternotify: "
10746 "NULL region; ignoring.\n");
10747 return;
10748 }
10749
10750 focus_region(r);
10751 } else {
10752 DNPRINTF(SWM_D_EVENT, "enternotify: window is NULL; "
10753 "ignoring\n");
10754 return;
10755 }
10756 } else {
10757 if (e->mode == XCB_NOTIFY_MODE_NORMAL &&
10758 e->detail == XCB_NOTIFY_DETAIL_INFERIOR) {
10759 DNPRINTF(SWM_D_EVENT, "enternotify: entering from "
10760 "inferior; ignoring\n");
10761 return;
10762 }
10763
10764 focus_win(get_focus_magic(win));
10765 }
10766
10767 DNPRINTF(SWM_D_EVENT, "enternotify: done\n");
10768
10769 xcb_flush(conn);
10770 }
10771
10772 #ifdef SWM_DEBUG
10773 void
10774 leavenotify(xcb_leave_notify_event_t *e)
10775 {
10776 last_event_time = e->time;
10777
10778 DNPRINTF(SWM_D_FOCUS, "leavenotify: time: %u, win (x,y): %#x "
10779 "(%d,%d), mode: %s(%d), detail: %s(%d), root (x,y): %#x (%d,%d), "
10780 "child: %#x, same_screen_focus: %s, state: %s(%d)\n",
10781 e->time, e->event, e->event_x, e->event_y,
10782 get_notify_mode_label(e->mode), e->mode,
10783 get_notify_detail_label(e->detail), e->detail,
10784 e->root, e->root_x, e->root_y, e->child,
10785 YESNO(e->same_screen_focus), get_state_mask_label(e->state),
10786 e->state);
10787 }
10788 #endif
10789
10790 void
10791 mapnotify(xcb_map_notify_event_t *e)
10792 {
10793 struct ws_win *win, *parent = NULL;
10794 struct workspace *ws;
10795
10796 DNPRINTF(SWM_D_EVENT, "mapnotify: win %#x\n", e->window);
10797
10798 if ((win = manage_window(e->window, spawn_position, false)) == NULL)
10799 return;
10800 ws = win->ws;
10801
10802 if (win->state == SWM_WIN_STATE_REPARENTING)
10803 return;
10804
10805 if (ws->r == NULL) {
10806 unmap_window(win);
10807 goto out;
10808 }
10809
10810 /* Need to know if win was mapped due to ws switch. */
10811 if (ws->state == SWM_WS_STATE_MAPPED) {
10812 if (ws->focus_pending && TRANS(ws->focus_pending))
10813 parent = find_window(win->transient);
10814
10815 /* If window's parent is maximized, don't clear it. */
10816 if ((parent == NULL) || !MAXIMIZED(parent))
10817 if (clear_maximized(ws) > 0)
10818 stack(ws->r);
10819 }
10820
10821 win->mapped = true;
10822 set_win_state(win, XCB_ICCCM_WM_STATE_NORMAL);
10823
10824 if (focus_mode != SWM_FOCUS_FOLLOW && WS_FOCUSED(win->ws)) {
10825 if (ws->focus_pending == win) {
10826 focus_win(win);
10827 ws->focus_pending = NULL;
10828 center_pointer(ws->r);
10829 focus_flush();
10830 }
10831 }
10832
10833 out:
10834 DNPRINTF(SWM_D_EVENT, "mapnotify: done\n");
10835
10836 xcb_flush(conn);
10837 }
10838
10839 void
10840 mappingnotify(xcb_mapping_notify_event_t *e)
10841 {
10842 if (e->request != XCB_MAPPING_POINTER) {
10843 xcb_refresh_keyboard_mapping(syms, e);
10844 grabkeys();
10845 }
10846
10847 grabbuttons();
10848 }
10849
10850 void
10851 maprequest(xcb_map_request_event_t *e)
10852 {
10853 struct ws_win *win, *w = NULL;
10854
10855 DNPRINTF(SWM_D_EVENT, "maprequest: win %#x\n",
10856 e->window);
10857
10858 win = manage_window(e->window, spawn_position, true);
10859 if (win == NULL)
10860 goto out;
10861
10862 /* The new window should get focus; prepare. */
10863 if (focus_mode != SWM_FOCUS_FOLLOW &&
10864 !(win->quirks & SWM_Q_NOFOCUSONMAP) && ACCEPTS_FOCUS(win)) {
10865 if (win->quirks & SWM_Q_FOCUSONMAP_SINGLE) {
10866 /* See if other wins of same type are already mapped. */
10867 TAILQ_FOREACH(w, &win->ws->winlist, entry) {
10868 if (w == win || !w->mapped)
10869 continue;
10870
10871 if (w->ch.class_name &&
10872 win->ch.class_name &&
10873 strcmp(w->ch.class_name,
10874 win->ch.class_name) == 0 &&
10875 w->ch.instance_name &&
10876 win->ch.instance_name &&
10877 strcmp(w->ch.instance_name,
10878 win->ch.instance_name) == 0)
10879 break;
10880 }
10881 }
10882
10883 if (w == NULL)
10884 win->ws->focus_pending = get_focus_magic(win);
10885 }
10886
10887 /* All windows need to be mapped if they are in the current workspace.*/
10888 stack(win->ws->r);
10889
10890 /* Ignore EnterNotify to handle the mapnotify without interference. */
10891 if (focus_mode == SWM_FOCUS_DEFAULT)
10892 event_drain(XCB_ENTER_NOTIFY);
10893 out:
10894 DNPRINTF(SWM_D_EVENT, "maprequest: done.\n");
10895 }
10896
10897 void
10898 motionnotify(xcb_motion_notify_event_t *e)
10899 {
10900 struct swm_region *r = NULL;
10901 int i, num_screens;
10902
10903 last_event_time = e->time;
10904
10905 DNPRINTF(SWM_D_FOCUS, "motionnotify: time: %u, win (x,y): %#x "
10906 "(%d,%d), detail: %s(%d), root (x,y): %#x (%d,%d), "
10907 "child: %#x, same_screen_focus: %s, state: %d\n",
10908 e->time, e->event, e->event_x, e->event_y,
10909 get_notify_detail_label(e->detail), e->detail,
10910 e->root, e->root_x, e->root_y, e->child,
10911 YESNO(e->same_screen), e->state);
10912
10913 if (focus_mode == SWM_FOCUS_MANUAL)
10914 return;
10915
10916 num_screens = get_screen_count();
10917 for (i = 0; i < num_screens; i++)
10918 if (screens[i].root == e->root)
10919 break;
10920
10921 TAILQ_FOREACH(r, &screens[i].rl, entry)
10922 if (X(r) <= e->root_x && e->root_x < MAX_X(r) &&
10923 Y(r) <= e->root_y && e->root_y < MAX_Y(r))
10924 break;
10925
10926 focus_region(r);
10927 }
10928
10929 #ifdef SWM_DEBUG
10930 char *
10931 get_atom_name(xcb_atom_t atom)
10932 {
10933 char *name = NULL;
10934 #ifdef SWM_DEBUG_ATOM_NAMES
10935 /*
10936 * This should be disabled during most debugging since
10937 * xcb_get_* causes an xcb_flush.
10938 */
10939 size_t len;
10940 xcb_get_atom_name_reply_t *r;
10941
10942 r = xcb_get_atom_name_reply(conn,
10943 xcb_get_atom_name(conn, atom),
10944 NULL);
10945 if (r) {
10946 len = xcb_get_atom_name_name_length(r);
10947 if (len > 0) {
10948 name = malloc(len + 1);
10949 if (name) {
10950 memcpy(name, xcb_get_atom_name_name(r), len);
10951 name[len] = '\0';
10952 }
10953 }
10954 free(r);
10955 }
10956 #else
10957 (void)atom;
10958 #endif
10959 return (name);
10960 }
10961 #endif
10962
10963 void
10964 propertynotify(xcb_property_notify_event_t *e)
10965 {
10966 struct ws_win *win;
10967 struct workspace *ws;
10968 #ifdef SWM_DEBUG
10969 char *name;
10970
10971 name = get_atom_name(e->atom);
10972 DNPRINTF(SWM_D_EVENT, "propertynotify: win %#x, atom: %s(%u), "
10973 "time: %#x, state: %u\n", e->window, name, e->atom, e->time,
10974 e->state);
10975 free(name);
10976 #endif
10977 last_event_time = e->time;
10978
10979 win = find_window(e->window);
10980 if (win == NULL)
10981 return;
10982
10983 ws = win->ws;
10984
10985 if (e->atom == a_state) {
10986 /* State just changed, make sure it gets focused if mapped. */
10987 if (e->state == XCB_PROPERTY_NEW_VALUE) {
10988 if (focus_mode != SWM_FOCUS_FOLLOW && WS_FOCUSED(ws)) {
10989 if (win->mapped &&
10990 win->state == SWM_WIN_STATE_REPARENTED &&
10991 ws->focus_pending == win) {
10992 focus_win(ws->focus_pending);
10993 ws->focus_pending = NULL;
10994 }
10995 }
10996 }
10997 } else if (e->atom == XCB_ATOM_WM_CLASS ||
10998 e->atom == XCB_ATOM_WM_NAME) {
10999 if (ws->r)
11000 bar_draw(ws->r->bar);
11001 } else if (e->atom == a_prot) {
11002 get_wm_protocols(win);
11003 } else if (e->atom == XCB_ATOM_WM_NORMAL_HINTS) {
11004 xcb_icccm_get_wm_normal_hints_reply(conn,
11005 xcb_icccm_get_wm_normal_hints(conn, win->id),
11006 &win->sh, NULL);
11007 }
11008
11009 xcb_flush(conn);
11010 }
11011
11012 void
11013 reparentnotify(xcb_reparent_notify_event_t *e)
11014 {
11015 struct ws_win *win;
11016
11017 DNPRINTF(SWM_D_EVENT, "reparentnotify: event: %#x, win %#x, "
11018 "parent: %#x, (x,y): (%u,%u), override_redirect: %u\n",
11019 e->event, e->window, e->parent, e->x, e->y, e->override_redirect);
11020
11021 win = find_window(e->window);
11022 if (win) {
11023 if (win->state == SWM_WIN_STATE_REPARENTING) {
11024 win->state = SWM_WIN_STATE_REPARENTED;
11025
11026 if (win->ws->r)
11027 map_window(win);
11028 else
11029 unmap_window(win);
11030
11031 update_window(win);
11032 update_win_stacking(win);
11033 } else if (win->state == SWM_WIN_STATE_UNPARENTING) {
11034 win->state = SWM_WIN_STATE_UNPARENTED;
11035 }
11036 }
11037 }
11038
11039 void
11040 unmapnotify(xcb_unmap_notify_event_t *e)
11041 {
11042 struct ws_win *win;
11043 struct workspace *ws;
11044
11045 DNPRINTF(SWM_D_EVENT, "unmapnotify: win %#x\n", e->window);
11046
11047 /* If we aren't managing the window, then ignore. */
11048 win = find_window(e->window);
11049 if (win == NULL || win->id != e->window) {
11050 DNPRINTF(SWM_D_EVENT, "unmapnotify: ignore unmanaged.\n");
11051 return;
11052 }
11053
11054 /* Do nothing if already withdrawn. */
11055 if (!win->mapped && !ICONIC(win)) {
11056 DNPRINTF(SWM_D_EVENT, "unmapnotify: ignore withdrawn.\n");
11057 return;
11058 }
11059
11060 ws = win->ws;
11061 win->mapped = false;
11062
11063 /* Ignore if reparenting-related. */
11064 if (win->state != SWM_WIN_STATE_REPARENTED) {
11065 DNPRINTF(SWM_D_EVENT, "unmapnotify: ignore not reparented.\n");
11066 return;
11067 }
11068
11069 /* If win was focused, make sure to focus on something else. */
11070 if (win == ws->focus) {
11071 if (focus_mode != SWM_FOCUS_FOLLOW) {
11072 ws->focus_pending = get_focus_prev(win);
11073 DNPRINTF(SWM_D_EVENT, "unmapnotify: "
11074 "focus_pending: %#x\n",
11075 WINID(ws->focus_pending));
11076 }
11077
11078 unfocus_win(win);
11079 }
11080
11081 if (ICONIC(win)) {
11082 /* Iconify. */
11083 DNPRINTF(SWM_D_EVENT, "unmapnotify: iconify.\n");
11084 set_win_state(win, XCB_ICCCM_WM_STATE_ICONIC);
11085 } else {
11086 /* Withdraw. */
11087 DNPRINTF(SWM_D_EVENT, "unmapnotify: withdraw.\n");
11088 set_win_state(win, XCB_ICCCM_WM_STATE_WITHDRAWN);
11089 unmanage_window(win);
11090 }
11091
11092 stack(ws->r);
11093
11094 /* Update focus if ws is active. */
11095 if (WS_FOCUSED(ws)) {
11096 if (focus_mode == SWM_FOCUS_FOLLOW) {
11097 focus_win(get_pointer_win(ws->r->s->root));
11098 } else if (ws->focus_pending) {
11099 focus_win(ws->focus_pending);
11100 ws->focus_pending = NULL;
11101 } else if (ws->focus == NULL) {
11102 DNPRINTF(SWM_D_FOCUS, "unmapnotify: set_input_focus: "
11103 "%#x, revert-to: parent, time: 0\n",
11104 ws->r->id);
11105 xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT,
11106 ws->r->id, XCB_CURRENT_TIME);
11107 }
11108 }
11109
11110 center_pointer(ws->r);
11111 focus_flush();
11112 DNPRINTF(SWM_D_EVENT, "unmapnotify: done.\n");
11113 }
11114
11115 #ifdef SWM_DEBUG
11116 char *
11117 get_source_type_label(uint32_t type)
11118 {
11119 char *label;
11120
11121 switch (type) {
11122 case EWMH_SOURCE_TYPE_NONE:
11123 label = "None";
11124 break;
11125 case EWMH_SOURCE_TYPE_NORMAL:
11126 label = "Normal";
11127 break;
11128 case EWMH_SOURCE_TYPE_OTHER:
11129 label = "Other";
11130 break;
11131 default:
11132 label = "Invalid";
11133 }
11134
11135 return label;
11136 }
11137 #endif
11138
11139 void
11140 clientmessage(xcb_client_message_event_t *e)
11141 {
11142 struct ws_win *win;
11143 struct swm_region *r = NULL;
11144 union arg a;
11145 uint32_t vals[4];
11146 int num_screens, i;
11147 xcb_map_request_event_t mre;
11148 #ifdef SWM_DEBUG
11149 char *name;
11150
11151 name = get_atom_name(e->type);
11152 DNPRINTF(SWM_D_EVENT, "clientmessage: win %#x, atom: %s(%u)\n",
11153 e->window, name, e->type);
11154 free(name);
11155 #endif
11156
11157 if (e->type == ewmh[_NET_CURRENT_DESKTOP].atom) {
11158 num_screens = get_screen_count();
11159 for (i = 0; i < num_screens; i++)
11160 if (screens[i].root == e->window) {
11161 r = screens[i].r_focus;
11162 break;
11163 }
11164
11165 if (r && e->data.data32[0] < (uint32_t)workspace_limit) {
11166 a.id = e->data.data32[0];
11167 switchws(NULL, r, &a);
11168 focus_flush();
11169 }
11170
11171 return;
11172 } else if (e->type == ewmh[_NET_REQUEST_FRAME_EXTENTS].atom) {
11173 DNPRINTF(SWM_D_EVENT,
11174 "clientmessage: set _NET_FRAME_EXTENTS on window.\n");
11175 vals[0] = vals[1] = vals[2] = vals[3] = border_width;
11176 xcb_change_property(conn, XCB_PROP_MODE_REPLACE, e->window,
11177 a_net_frame_extents, XCB_ATOM_CARDINAL, 32, 4, vals);
11178 xcb_flush(conn);
11179 return;
11180 }
11181
11182 win = find_window(e->window);
11183 if (win == NULL) {
11184 if (e->type == ewmh[_NET_ACTIVE_WINDOW].atom) {
11185 /* Manage the window with maprequest. */
11186 DNPRINTF(SWM_D_EVENT, "clientmessage: request focus on "
11187 "unmanaged window.\n");
11188 mre.window = e->window;
11189 maprequest(&mre);
11190 }
11191 return;
11192 }
11193
11194 if (e->type == ewmh[_NET_ACTIVE_WINDOW].atom) {
11195 DNPRINTF(SWM_D_EVENT, "clientmessage: _NET_ACTIVE_WINDOW, "
11196 "source_type: %s(%d)\n",
11197 get_source_type_label(e->data.data32[0]),
11198 e->data.data32[0]);
11199
11200 /*
11201 * Allow focus changes that are a result of direct user
11202 * action and from applications that use the old EWMH spec.
11203 */
11204 if (e->data.data32[0] != EWMH_SOURCE_TYPE_NORMAL ||
11205 win->quirks & SWM_Q_OBEYAPPFOCUSREQ) {
11206 if (WS_FOCUSED(win->ws))
11207 focus_win(win);
11208 else
11209 win->ws->focus_pending = win;
11210 }
11211 } else if (e->type == ewmh[_NET_CLOSE_WINDOW].atom) {
11212 DNPRINTF(SWM_D_EVENT, "clientmessage: _NET_CLOSE_WINDOW\n");
11213 if (win->can_delete)
11214 client_msg(win, a_delete, 0);
11215 else
11216 xcb_kill_client(conn, win->id);
11217 } else if (e->type == ewmh[_NET_MOVERESIZE_WINDOW].atom) {
11218 DNPRINTF(SWM_D_EVENT,
11219 "clientmessage: _NET_MOVERESIZE_WINDOW\n");
11220 if (ABOVE(win)) {
11221 if (e->data.data32[0] & (1<<8)) /* x */
11222 X(win) = e->data.data32[1];
11223 if (e->data.data32[0] & (1<<9)) /* y */
11224 Y(win) = e->data.data32[2];
11225 if (e->data.data32[0] & (1<<10)) /* width */
11226 WIDTH(win) = e->data.data32[3];
11227 if (e->data.data32[0] & (1<<11)) /* height */
11228 HEIGHT(win) = e->data.data32[4];
11229
11230 update_window(win);
11231 } else {
11232 /* Notify no change was made. */
11233 config_win(win, NULL);
11234 /* TODO: Change stack sizes */
11235 }
11236 } else if (e->type == ewmh[_NET_RESTACK_WINDOW].atom) {
11237 DNPRINTF(SWM_D_EVENT, "clientmessage: _NET_RESTACK_WINDOW\n");
11238 vals[0] = e->data.data32[1]; /* Sibling window. */
11239 vals[1] = e->data.data32[2]; /* Stack mode detail. */
11240
11241 if (win->frame != XCB_WINDOW_NONE)
11242 xcb_configure_window(conn, win->frame,
11243 XCB_CONFIG_WINDOW_SIBLING |
11244 XCB_CONFIG_WINDOW_STACK_MODE, vals);
11245 } else if (e->type == ewmh[_NET_WM_STATE].atom) {
11246 DNPRINTF(SWM_D_EVENT, "clientmessage: _NET_WM_STATE\n");
11247 ewmh_change_wm_state(win, e->data.data32[1], e->data.data32[0]);
11248 if (e->data.data32[2])
11249 ewmh_change_wm_state(win, e->data.data32[2],
11250 e->data.data32[0]);
11251
11252 ewmh_update_wm_state(win);
11253 stack(win->ws->r);
11254 } else if (e->type == ewmh[_NET_WM_DESKTOP].atom) {
11255 DNPRINTF(SWM_D_EVENT, "clientmessage: _NET_WM_DESKTOP\n");
11256 r = win->ws->r;
11257
11258 win_to_ws(win, e->data.data32[0], true);
11259
11260 /* Stack source and destination ws, if mapped. */
11261 if (r != win->ws->r) {
11262 if (r)
11263 stack(r);
11264
11265 if (win->ws->r) {
11266 if (FLOATING(win))
11267 load_float_geom(win);
11268
11269 stack(win->ws->r);
11270 }
11271 }
11272 }
11273
11274 focus_flush();
11275 }
11276
11277 void
11278 check_conn(void)
11279 {
11280 int errcode = xcb_connection_has_error(conn);
11281 #ifdef XCB_CONN_ERROR
11282 char *s;
11283 switch (errcode) {
11284 case XCB_CONN_ERROR:
11285 s = "Socket error, pipe error or other stream error.";
11286 break;
11287 case XCB_CONN_CLOSED_EXT_NOTSUPPORTED:
11288 s = "Extension not supported.";
11289 break;
11290 case XCB_CONN_CLOSED_MEM_INSUFFICIENT:
11291 s = "Insufficient memory.";
11292 break;
11293 case XCB_CONN_CLOSED_REQ_LEN_EXCEED:
11294 s = "Request length was exceeded.";
11295 break;
11296 case XCB_CONN_CLOSED_PARSE_ERR:
11297 s = "Error parsing display string.";
11298 break;
11299 default:
11300 s = "Unknown error.";
11301 }
11302 if (errcode)
11303 errx(errcode, "X CONNECTION ERROR: %s", s);
11304 #else
11305 if (errcode)
11306 errx(errcode, "X CONNECTION ERROR");
11307 #endif
11308 }
11309
11310 int
11311 enable_wm(void)
11312 {
11313 int num_screens, i;
11314 const uint32_t val = XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT |
11315 XCB_EVENT_MASK_ENTER_WINDOW |
11316 XCB_EVENT_MASK_OWNER_GRAB_BUTTON |
11317 XCB_EVENT_MASK_STRUCTURE_NOTIFY |
11318 XCB_EVENT_MASK_ENTER_WINDOW |
11319 XCB_EVENT_MASK_LEAVE_WINDOW |
11320 XCB_EVENT_MASK_BUTTON_PRESS |
11321 XCB_EVENT_MASK_BUTTON_RELEASE |
11322 XCB_EVENT_MASK_KEY_PRESS |
11323 XCB_EVENT_MASK_KEY_RELEASE |
11324 XCB_EVENT_MASK_PROPERTY_CHANGE;
11325 xcb_screen_t *sc;
11326 xcb_void_cookie_t wac;
11327 xcb_generic_error_t *error;
11328
11329 /* this causes an error if some other window manager is running */
11330 num_screens = get_screen_count();
11331 for (i = 0; i < num_screens; i++) {
11332 if ((sc = get_screen(i)) == NULL)
11333 errx(1, "ERROR: can't get screen %d.", i);
11334 DNPRINTF(SWM_D_INIT, "enable_wm: screen %d, root: %#x\n",
11335 i, sc->root);
11336 wac = xcb_change_window_attributes_checked(conn, sc->root,
11337 XCB_CW_EVENT_MASK, &val);
11338 if ((error = xcb_request_check(conn, wac))) {
11339 DNPRINTF(SWM_D_INIT, "enable_wm: error_code: %u\n",
11340 error->error_code);
11341 free(error);
11342 return 1;
11343 }
11344 }
11345
11346 return 0;
11347 }
11348
11349 void
11350 new_region(struct swm_screen *s, int x, int y, int w, int h)
11351 {
11352 struct swm_region *r = NULL, *n;
11353 struct workspace *ws = NULL;
11354 int i;
11355 uint32_t wa[1];
11356
11357 DNPRINTF(SWM_D_MISC, "new region: screen[%d]:%dx%d+%d+%d\n",
11358 s->idx, w, h, x, y);
11359
11360 /* remove any conflicting regions */
11361 n = TAILQ_FIRST(&s->rl);
11362 while (n) {
11363 r = n;
11364 n = TAILQ_NEXT(r, entry);
11365 if (X(r) < (x + w) && (X(r) + WIDTH(r)) > x &&
11366 Y(r) < (y + h) && (Y(r) + HEIGHT(r)) > y) {
11367 if (r->ws->r != NULL)
11368 r->ws->old_r = r->ws->r;
11369 r->ws->r = NULL;
11370 bar_cleanup(r);
11371 xcb_destroy_window(conn, r->id);
11372 r->id = XCB_WINDOW_NONE;
11373 TAILQ_REMOVE(&s->rl, r, entry);
11374 TAILQ_INSERT_TAIL(&s->orl, r, entry);
11375 }
11376 }
11377
11378 /* search old regions for one to reuse */
11379
11380 /* size + location match */
11381 TAILQ_FOREACH(r, &s->orl, entry)
11382 if (X(r) == x && Y(r) == y &&
11383 HEIGHT(r) == h && WIDTH(r) == w)
11384 break;
11385
11386 /* size match */
11387 TAILQ_FOREACH(r, &s->orl, entry)
11388 if (HEIGHT(r) == h && WIDTH(r) == w)
11389 break;
11390
11391 if (r != NULL) {
11392 TAILQ_REMOVE(&s->orl, r, entry);
11393 /* try to use old region's workspace */
11394 if (r->ws->r == NULL)
11395 ws = r->ws;
11396 } else
11397 if ((r = calloc(1, sizeof(struct swm_region))) == NULL)
11398 err(1, "new_region: calloc: failed to allocate memory "
11399 "for screen");
11400
11401 /* if we don't have a workspace already, find one */
11402 if (ws == NULL) {
11403 for (i = 0; i < workspace_limit; i++)
11404 if (s->ws[i].r == NULL) {
11405 ws = &s->ws[i];
11406 break;
11407 }
11408 }
11409
11410 if (ws == NULL)
11411 errx(1, "new_region: no free workspaces");
11412
11413 if (ws->state == SWM_WS_STATE_HIDDEN)
11414 ws->state = SWM_WS_STATE_MAPPING;
11415
11416 X(r) = x;
11417 Y(r) = y;
11418 WIDTH(r) = w;
11419 HEIGHT(r) = h;
11420 r->bar = NULL;
11421 r->s = s;
11422 r->ws = ws;
11423 r->ws_prior = NULL;
11424 ws->r = r;
11425 outputs++;
11426 TAILQ_INSERT_TAIL(&s->rl, r, entry);
11427
11428 /* Invisible region window to detect pointer events on empty regions. */
11429 r->id = xcb_generate_id(conn);
11430 wa[0] = XCB_EVENT_MASK_POINTER_MOTION |
11431 XCB_EVENT_MASK_POINTER_MOTION_HINT;
11432
11433 xcb_create_window(conn, XCB_COPY_FROM_PARENT, r->id, r->s->root,
11434 X(r), Y(r), WIDTH(r), HEIGHT(r), 0, XCB_WINDOW_CLASS_INPUT_ONLY,
11435 XCB_COPY_FROM_PARENT, XCB_CW_EVENT_MASK, wa);
11436
11437 /* Make sure region input is at the bottom. */
11438 wa[0] = XCB_STACK_MODE_BELOW;
11439 xcb_configure_window(conn, r->id, XCB_CONFIG_WINDOW_STACK_MODE, wa);
11440
11441 xcb_map_window(conn, r->id);
11442 }
11443
11444 void
11445 scan_randr(int idx)
11446 {
11447 #ifdef SWM_XRR_HAS_CRTC
11448 int c;
11449 int ncrtc = 0;
11450 #endif /* SWM_XRR_HAS_CRTC */
11451 struct swm_region *r;
11452 struct ws_win *win;
11453 int num_screens;
11454 xcb_randr_get_screen_resources_current_cookie_t src;
11455 xcb_randr_get_screen_resources_current_reply_t *srr;
11456 xcb_randr_get_crtc_info_cookie_t cic;
11457 xcb_randr_get_crtc_info_reply_t *cir = NULL;
11458 xcb_randr_crtc_t *crtc;
11459 xcb_screen_t *screen;
11460
11461 DNPRINTF(SWM_D_MISC, "scan_randr: screen: %d\n", idx);
11462
11463 if ((screen = get_screen(idx)) == NULL)
11464 errx(1, "ERROR: can't get screen %d.", idx);
11465
11466 num_screens = get_screen_count();
11467 if (idx >= num_screens)
11468 errx(1, "scan_randr: invalid screen");
11469
11470 /* remove any old regions */
11471 while ((r = TAILQ_FIRST(&screens[idx].rl)) != NULL) {
11472 r->ws->old_r = r->ws->r = NULL;
11473 bar_cleanup(r);
11474 xcb_destroy_window(conn, r->id);
11475 r->id = XCB_WINDOW_NONE;
11476 TAILQ_REMOVE(&screens[idx].rl, r, entry);
11477 TAILQ_INSERT_TAIL(&screens[idx].orl, r, entry);
11478 }
11479 outputs = 0;
11480
11481 /* map virtual screens onto physical screens */
11482 #ifdef SWM_XRR_HAS_CRTC
11483 if (randr_support) {
11484 src = xcb_randr_get_screen_resources_current(conn,
11485 screens[idx].root);
11486 srr = xcb_randr_get_screen_resources_current_reply(conn, src,
11487 NULL);
11488 if (srr == NULL) {
11489 new_region(&screens[idx], 0, 0,
11490 screen->width_in_pixels,
11491 screen->height_in_pixels);
11492 goto out;
11493 } else
11494 ncrtc = srr->num_crtcs;
11495
11496 crtc = xcb_randr_get_screen_resources_current_crtcs(srr);
11497 for (c = 0; c < ncrtc; c++) {
11498 cic = xcb_randr_get_crtc_info(conn, crtc[c],
11499 XCB_CURRENT_TIME);
11500 cir = xcb_randr_get_crtc_info_reply(conn, cic, NULL);
11501 if (cir == NULL)
11502 continue;
11503 if (cir->num_outputs == 0) {
11504 free(cir);
11505 continue;
11506 }
11507
11508 if (cir->mode == 0)
11509 new_region(&screens[idx], 0, 0,
11510 screen->width_in_pixels,
11511 screen->height_in_pixels);
11512 else
11513 new_region(&screens[idx],
11514 cir->x, cir->y, cir->width, cir->height);
11515 free(cir);
11516 }
11517 free(srr);
11518 }
11519 #endif /* SWM_XRR_HAS_CRTC */
11520
11521 /* If detection failed, create a single region that spans the screen. */
11522 if (TAILQ_EMPTY(&screens[idx].rl))
11523 new_region(&screens[idx], 0, 0, screen->width_in_pixels,
11524 screen->height_in_pixels);
11525
11526 out:
11527 /* Cleanup unused previously visible workspaces. */
11528 TAILQ_FOREACH(r, &screens[idx].orl, entry) {
11529 TAILQ_FOREACH(win, &r->ws->winlist, entry)
11530 unmap_window(win);
11531 r->ws->state = SWM_WS_STATE_HIDDEN;
11532
11533 /* The screen shouldn't focus on an unused region. */
11534 if (screens[idx].r_focus == r)
11535 screens[idx].r_focus = NULL;
11536 }
11537
11538 DNPRINTF(SWM_D_MISC, "scan_randr: done.\n");
11539 }
11540
11541 void
11542 screenchange(xcb_randr_screen_change_notify_event_t *e)
11543 {
11544 struct swm_region *r;
11545 int i, num_screens;
11546
11547 DNPRINTF(SWM_D_EVENT, "screenchange: root: %#x\n", e->root);
11548
11549 num_screens = get_screen_count();
11550 /* silly event doesn't include the screen index */
11551 for (i = 0; i < num_screens; i++)
11552 if (screens[i].root == e->root)
11553 break;
11554 if (i >= num_screens)
11555 errx(1, "screenchange: screen not found");
11556
11557 /* brute force for now, just re-enumerate the regions */
11558 scan_randr(i);
11559
11560 #ifdef SWM_DEBUG
11561 print_win_geom(e->root);
11562 #endif
11563 /* add bars to all regions */
11564 TAILQ_FOREACH(r, &screens[i].rl, entry)
11565 bar_setup(r);
11566
11567 /* Stack all regions. */
11568 TAILQ_FOREACH(r, &screens[i].rl, entry)
11569 stack(r);
11570
11571 /* Make sure a region has focus. */
11572 if (screens[i].r_focus == NULL) {
11573 r = TAILQ_FIRST(&screens[i].rl);
11574 if (r != NULL)
11575 focus_region(r);
11576 }
11577
11578 focus_flush();
11579
11580 /* Update workspace state and bar on all regions. */
11581 TAILQ_FOREACH(r, &screens[i].rl, entry) {
11582 r->ws->state = SWM_WS_STATE_MAPPED;
11583 bar_draw(r->bar);
11584 }
11585 }
11586
11587 void
11588 grab_windows(void)
11589 {
11590 struct swm_region *r = NULL;
11591 xcb_query_tree_cookie_t qtc;
11592 xcb_query_tree_reply_t *qtr;
11593 xcb_get_property_cookie_t pc;
11594 xcb_get_property_reply_t *pr;
11595 xcb_window_t *wins = NULL, trans, *cwins = NULL;
11596 int i, j, k, n, no, num_screens;
11597
11598 DNPRINTF(SWM_D_INIT, "grab_windows: begin\n");
11599 num_screens = get_screen_count();
11600 for (i = 0; i < num_screens; i++) {
11601 qtc = xcb_query_tree(conn, screens[i].root);
11602 qtr = xcb_query_tree_reply(conn, qtc, NULL);
11603 if (qtr == NULL)
11604 continue;
11605 wins = xcb_query_tree_children(qtr);
11606 no = xcb_query_tree_children_length(qtr);
11607
11608 /* Try to sort windows according to _NET_CLIENT_LIST. */
11609 pr = xcb_get_property_reply(conn, xcb_get_property(conn, 0,
11610 screens[i].root, ewmh[_NET_CLIENT_LIST].atom,
11611 XCB_ATOM_WINDOW, 0, UINT32_MAX), NULL);
11612 if (pr != NULL) {
11613 cwins = xcb_get_property_value(pr);
11614 n = xcb_get_property_value_length(pr) /
11615 sizeof(xcb_atom_t);
11616
11617 for (j = 0; j < n; ++j) {
11618 for (k = j; k < no; ++k) {
11619 if (wins[k] == cwins[j]) {
11620 /* Swap wins j and k. */
11621 wins[k] = wins[j];
11622 wins[j] = cwins[j];
11623 }
11624 }
11625 }
11626
11627 free(pr);
11628 }
11629
11630 /* Manage top-level windows first, then transients. */
11631 /* TODO: allow transients to be managed before leader. */
11632 DNPRINTF(SWM_D_INIT, "grab_windows: grab top-level windows.\n");
11633 for (j = 0; j < no; j++) {
11634 TAILQ_FOREACH(r, &screens[i].rl, entry) {
11635 if (r->id == wins[j]) {
11636 DNPRINTF(SWM_D_INIT, "grab_windows: "
11637 "skip %#x; region input window.\n",
11638 wins[j]);
11639 break;
11640 } else if (r->bar->id == wins[j]) {
11641 DNPRINTF(SWM_D_INIT, "grab_windows: "
11642 "skip %#x; region bar.\n",
11643 wins[j]);
11644 break;
11645 }
11646 }
11647
11648 if (r)
11649 continue;
11650
11651 pc = xcb_icccm_get_wm_transient_for(conn, wins[j]);
11652 if (xcb_icccm_get_wm_transient_for_reply(conn, pc,
11653 &trans, NULL)) {
11654 DNPRINTF(SWM_D_INIT, "grab_windows: skip %#x; "
11655 "is transient for %#x.\n", wins[j], trans);
11656 continue;
11657 }
11658
11659 manage_window(wins[j], SWM_STACK_TOP, false);
11660 }
11661
11662 DNPRINTF(SWM_D_INIT, "grab_windows: grab transient windows.\n");
11663 for (j = 0; j < no; j++) {
11664 pc = xcb_icccm_get_wm_transient_for(conn, wins[j]);
11665 if (xcb_icccm_get_wm_transient_for_reply(conn, pc,
11666 &trans, NULL))
11667 manage_window(wins[j], SWM_STACK_TOP, false);
11668 }
11669 free(qtr);
11670 }
11671 DNPRINTF(SWM_D_INIT, "grab_windows: done.\n");
11672 }
11673
11674 void
11675 setup_screens(void)
11676 {
11677 int i, j, k, num_screens;
11678 struct workspace *ws;
11679 uint32_t gcv[1], wa[1];
11680 const xcb_query_extension_reply_t *qep;
11681 xcb_screen_t *screen;
11682 xcb_randr_query_version_cookie_t c;
11683 xcb_randr_query_version_reply_t *r;
11684
11685 num_screens = get_screen_count();
11686 if ((screens = calloc(num_screens, sizeof(struct swm_screen))) == NULL)
11687 err(1, "setup_screens: calloc: failed to allocate memory for "
11688 "screens");
11689
11690 /* Initial RandR setup. */
11691 randr_support = false;
11692 qep = xcb_get_extension_data(conn, &xcb_randr_id);
11693 if (qep->present) {
11694 c = xcb_randr_query_version(conn, 1, 1);
11695 r = xcb_randr_query_version_reply(conn, c, NULL);
11696 if (r) {
11697 if (r->major_version >= 1) {
11698 randr_support = true;
11699 randr_eventbase = qep->first_event;
11700 }
11701 free(r);
11702 }
11703 }
11704
11705 wa[0] = cursors[XC_LEFT_PTR].cid;
11706
11707 /* map physical screens */
11708 for (i = 0; i < num_screens; i++) {
11709 DNPRINTF(SWM_D_WS, "setup_screens: init screen: %d\n", i);
11710 screens[i].idx = i;
11711 screens[i].r_focus = NULL;
11712
11713 TAILQ_INIT(&screens[i].rl);
11714 TAILQ_INIT(&screens[i].orl);
11715 if ((screen = get_screen(i)) == NULL)
11716 errx(1, "ERROR: can't get screen %d.", i);
11717 screens[i].root = screen->root;
11718
11719 /* set default colors */
11720 setscreencolor("red", i, SWM_S_COLOR_FOCUS);
11721 setscreencolor("rgb:88/88/88", i, SWM_S_COLOR_UNFOCUS);
11722 setscreencolor("rgb:00/80/80", i, SWM_S_COLOR_BAR_BORDER);
11723 setscreencolor("rgb:00/40/40", i,
11724 SWM_S_COLOR_BAR_BORDER_UNFOCUS);
11725 setscreencolor("black", i, SWM_S_COLOR_BAR);
11726 setscreencolor("rgb:a0/a0/a0", i, SWM_S_COLOR_BAR_FONT);
11727 setscreencolor("red", i, SWM_S_COLOR_FOCUS_MAXIMIZED);
11728 setscreencolor("rgb:88/88/88", i,
11729 SWM_S_COLOR_UNFOCUS_MAXIMIZED);
11730
11731 /* create graphics context on screen */
11732 screens[i].bar_gc = xcb_generate_id(conn);
11733 gcv[0] = 0;
11734 xcb_create_gc(conn, screens[i].bar_gc, screens[i].root,
11735 XCB_GC_GRAPHICS_EXPOSURES, gcv);
11736
11737 /* set default cursor */
11738 xcb_change_window_attributes(conn, screens[i].root,
11739 XCB_CW_CURSOR, wa);
11740
11741 /* init all workspaces */
11742 /* XXX these should be dynamically allocated too */
11743 for (j = 0; j < SWM_WS_MAX; j++) {
11744 ws = &screens[i].ws[j];
11745 ws->idx = j;
11746 ws->name = NULL;
11747 ws->bar_enabled = true;
11748 ws->focus = NULL;
11749 ws->focus_prev = NULL;
11750 ws->focus_pending = NULL;
11751 ws->raised = NULL;
11752 ws->r = NULL;
11753 ws->old_r = NULL;
11754 ws->state = SWM_WS_STATE_HIDDEN;
11755 TAILQ_INIT(&ws->stack);
11756 TAILQ_INIT(&ws->winlist);
11757 TAILQ_INIT(&ws->unmanagedlist);
11758
11759 for (k = 0; layouts[k].l_stack != NULL; k++)
11760 if (layouts[k].l_config != NULL)
11761 layouts[k].l_config(ws,
11762 SWM_ARG_ID_STACKINIT);
11763 ws->cur_layout = &layouts[0];
11764 ws->cur_layout->l_string(ws);
11765 }
11766
11767 scan_randr(i);
11768
11769 if (randr_support)
11770 xcb_randr_select_input(conn, screens[i].root,
11771 XCB_RANDR_NOTIFY_MASK_SCREEN_CHANGE);
11772 }
11773 }
11774
11775 void
11776 setup_globals(void)
11777 {
11778 if ((bar_fonts = strdup(SWM_BAR_FONTS)) == NULL)
11779 err(1, "setup_globals: strdup: failed to allocate memory.");
11780
11781 if ((clock_format = strdup("%a %b %d %R %Z %Y")) == NULL)
11782 err(1, "setup_globals: strdup: failed to allocate memory.");
11783
11784 if ((syms = xcb_key_symbols_alloc(conn)) == NULL)
11785 errx(1, "unable to allocate key symbols");
11786
11787 a_state = get_atom_from_string("WM_STATE");
11788 a_prot = get_atom_from_string("WM_PROTOCOLS");
11789 a_delete = get_atom_from_string("WM_DELETE_WINDOW");
11790 a_net_frame_extents = get_atom_from_string("_NET_FRAME_EXTENTS");
11791 a_net_supported = get_atom_from_string("_NET_SUPPORTED");
11792 a_net_wm_check = get_atom_from_string("_NET_SUPPORTING_WM_CHECK");
11793 a_takefocus = get_atom_from_string("WM_TAKE_FOCUS");
11794 a_utf8_string = get_atom_from_string("UTF8_STRING");
11795 a_swm_ws = get_atom_from_string("_SWM_WS");
11796 }
11797
11798 void
11799 shutdown_cleanup(void)
11800 {
11801 struct swm_region *r;
11802 struct ws_win *w;
11803 struct workspace *ws;
11804 int i, num_screens;
11805
11806 /* disable alarm because the following code may not be interrupted */
11807 alarm(0);
11808 if (signal(SIGALRM, SIG_IGN) == SIG_ERR)
11809 err(1, "can't disable alarm");
11810
11811 bar_extra_stop();
11812 unmap_all();
11813
11814 cursors_cleanup();
11815
11816 clear_quirks();
11817 clear_spawns();
11818 clear_bindings();
11819
11820 teardown_ewmh();
11821
11822 num_screens = get_screen_count();
11823 for (i = 0; i < num_screens; ++i) {
11824 int j;
11825
11826 DNPRINTF(SWM_D_FOCUS, "shutdown_cleanup: set_input_focus: "
11827 "%#x, revert-to: root, time: 0\n", screens[i].root);
11828 xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT,
11829 screens[i].root, XCB_CURRENT_TIME);
11830
11831 if (screens[i].bar_gc != XCB_NONE)
11832 xcb_free_gc(conn, screens[i].bar_gc);
11833 if (!bar_font_legacy) {
11834 XftColorFree(display, DefaultVisual(display, i),
11835 DefaultColormap(display, i), &bar_font_color);
11836 XftColorFree(display, DefaultVisual(display, i),
11837 DefaultColormap(display, i), &search_font_color);
11838 }
11839
11840 for (j = 0; j < SWM_S_COLOR_MAX; ++j) {
11841 free(screens[i].c[j].name);
11842 }
11843
11844 /* Free window memory. */
11845 for (j = 0; j < SWM_WS_MAX; ++j) {
11846 ws = &screens[i].ws[j];
11847 free(ws->name);
11848
11849 while ((w = TAILQ_FIRST(&ws->winlist)) != NULL) {
11850 TAILQ_REMOVE(&ws->winlist, w, entry);
11851 free_window(w);
11852 }
11853
11854 while ((w = TAILQ_FIRST(&ws->unmanagedlist)) != NULL) {
11855 TAILQ_REMOVE(&ws->unmanagedlist, w, entry);
11856 free_window(w);
11857 }
11858 }
11859
11860 /* Free region memory. */
11861 while ((r = TAILQ_FIRST(&screens[i].rl)) != NULL) {
11862 TAILQ_REMOVE(&screens[i].rl, r, entry);
11863 free(r->bar);
11864 free(r);
11865 }
11866
11867 while ((r = TAILQ_FIRST(&screens[i].orl)) != NULL) {
11868 TAILQ_REMOVE(&screens[i].rl, r, entry);
11869 free(r->bar);
11870 free(r);
11871 }
11872 }
11873 free(screens);
11874
11875 free(bar_format);
11876 free(bar_fonts);
11877 free(clock_format);
11878 free(startup_exception);
11879
11880 if (bar_fs)
11881 XFreeFontSet(display, bar_fs);
11882 if (bar_font)
11883 XftFontClose(display, bar_font);
11884
11885 xcb_key_symbols_free(syms);
11886 xcb_flush(conn);
11887 xcb_aux_sync(conn);
11888 xcb_disconnect(conn);
11889 }
11890
11891 void
11892 event_error(xcb_generic_error_t *e)
11893 {
11894 (void)e;
11895
11896 DNPRINTF(SWM_D_EVENT, "event_error: %s(%u) from %s(%u), sequence: %u, "
11897 "resource_id: %u, minor_code: %u\n",
11898 xcb_event_get_error_label(e->error_code), e->error_code,
11899 xcb_event_get_request_label(e->major_code), e->major_code,
11900 e->sequence, e->resource_id, e->minor_code);
11901 }
11902
11903 void
11904 event_handle(xcb_generic_event_t *evt)
11905 {
11906 uint8_t type = XCB_EVENT_RESPONSE_TYPE(evt);
11907
11908 DNPRINTF(SWM_D_EVENT, "XCB Event: %s(%d), seq %u\n",
11909 xcb_event_get_label(XCB_EVENT_RESPONSE_TYPE(evt)),
11910 XCB_EVENT_RESPONSE_TYPE(evt), evt->sequence);
11911
11912 switch (type) {
11913 #define EVENT(type, callback) case type: callback((void *)evt); return
11914 EVENT(0, event_error);
11915 EVENT(XCB_BUTTON_PRESS, buttonpress);
11916 EVENT(XCB_BUTTON_RELEASE, buttonrelease);
11917 /*EVENT(XCB_CIRCULATE_NOTIFY, );*/
11918 /*EVENT(XCB_CIRCULATE_REQUEST, );*/
11919 EVENT(XCB_CLIENT_MESSAGE, clientmessage);
11920 /*EVENT(XCB_COLORMAP_NOTIFY, );*/
11921 EVENT(XCB_CONFIGURE_NOTIFY, configurenotify);
11922 EVENT(XCB_CONFIGURE_REQUEST, configurerequest);
11923 /*EVENT(XCB_CREATE_NOTIFY, );*/
11924 EVENT(XCB_DESTROY_NOTIFY, destroynotify);
11925 EVENT(XCB_ENTER_NOTIFY, enternotify);
11926 EVENT(XCB_EXPOSE, expose);
11927 EVENT(XCB_FOCUS_IN, focusin);
11928 #ifdef SWM_DEBUG
11929 EVENT(XCB_FOCUS_OUT, focusout);
11930 #endif
11931 /*EVENT(XCB_GRAPHICS_EXPOSURE, );*/
11932 /*EVENT(XCB_GRAVITY_NOTIFY, );*/
11933 EVENT(XCB_KEY_PRESS, keypress);
11934 EVENT(XCB_KEY_RELEASE, keyrelease);
11935 /*EVENT(XCB_KEYMAP_NOTIFY, );*/
11936 #ifdef SWM_DEBUG
11937 EVENT(XCB_LEAVE_NOTIFY, leavenotify);
11938 #endif
11939 EVENT(XCB_MAP_NOTIFY, mapnotify);
11940 EVENT(XCB_MAP_REQUEST, maprequest);
11941 EVENT(XCB_MAPPING_NOTIFY, mappingnotify);
11942 EVENT(XCB_MOTION_NOTIFY, motionnotify);
11943 /*EVENT(XCB_NO_EXPOSURE, );*/
11944 EVENT(XCB_PROPERTY_NOTIFY, propertynotify);
11945 EVENT(XCB_REPARENT_NOTIFY, reparentnotify);
11946 /*EVENT(XCB_RESIZE_REQUEST, );*/
11947 /*EVENT(XCB_SELECTION_CLEAR, );*/
11948 /*EVENT(XCB_SELECTION_NOTIFY, );*/
11949 /*EVENT(XCB_SELECTION_REQUEST, );*/
11950 EVENT(XCB_UNMAP_NOTIFY, unmapnotify);
11951 /*EVENT(XCB_VISIBILITY_NOTIFY, );*/
11952 #undef EVENT
11953 }
11954 if (type - randr_eventbase == XCB_RANDR_SCREEN_CHANGE_NOTIFY)
11955 screenchange((void *)evt);
11956 }
11957
11958 int
11959 main(int argc, char *argv[])
11960 {
11961 struct pollfd pfd[2];
11962 struct sigaction sact;
11963 struct stat sb;
11964 struct passwd *pwd;
11965 struct swm_region *r;
11966 xcb_generic_event_t *evt;
11967 int xfd, i, num_screens, num_readable;
11968 char conf[PATH_MAX], *cfile = NULL;
11969 bool stdin_ready = false, startup = true;
11970
11971 /* suppress unused warning since var is needed */
11972 (void)argc;
11973
11974 #ifdef SWM_DEBUG
11975 time_started = time(NULL);
11976 #endif
11977
11978 start_argv = argv;
11979 warnx("Welcome to spectrwm V%s Build: %s", SPECTRWM_VERSION, buildstr);
11980 if (setlocale(LC_CTYPE, "") == NULL || setlocale(LC_TIME, "") == NULL)
11981 warnx("no locale support");
11982
11983 /* handle some signals */
11984 bzero(&sact, sizeof(sact));
11985 sigemptyset(&sact.sa_mask);
11986 sact.sa_flags = 0;
11987 sact.sa_handler = sighdlr;
11988 sigaction(SIGINT, &sact, NULL);
11989 sigaction(SIGQUIT, &sact, NULL);
11990 sigaction(SIGTERM, &sact, NULL);
11991 sigaction(SIGHUP, &sact, NULL);
11992
11993 sact.sa_handler = sighdlr;
11994 sact.sa_flags = SA_NOCLDSTOP;
11995 sigaction(SIGCHLD, &sact, NULL);
11996
11997 if ((display = XOpenDisplay(0)) == NULL)
11998 errx(1, "can not open display");
11999
12000 conn = XGetXCBConnection(display);
12001 if (xcb_connection_has_error(conn))
12002 errx(1, "can not get XCB connection");
12003
12004 XSetEventQueueOwner(display, XCBOwnsEventQueue);
12005
12006 xcb_prefetch_extension_data(conn, &xcb_randr_id);
12007 xfd = xcb_get_file_descriptor(conn);
12008
12009 /* look for local and global conf file */
12010 pwd = getpwuid(getuid());
12011 if (pwd == NULL)
12012 errx(1, "invalid user: %d", getuid());
12013
12014 xcb_grab_server(conn);
12015 xcb_aux_sync(conn);
12016
12017 /* flush all events */
12018 while ((evt = get_next_event(false))) {
12019 if (XCB_EVENT_RESPONSE_TYPE(evt) == 0)
12020 event_handle(evt);
12021 free(evt);
12022 }
12023
12024 if (enable_wm())
12025 errx(1, "another window manager is currently running");
12026
12027 /* Load Xcursors and/or cursorfont glyph cursors. */
12028 cursors_load();
12029
12030 xcb_aux_sync(conn);
12031
12032 setup_globals();
12033 setup_screens();
12034 setup_ewmh();
12035 setup_keybindings();
12036 setup_btnbindings();
12037 setup_quirks();
12038 setup_spawn();
12039
12040 /* load config */
12041 for (i = 0; ; i++) {
12042 conf[0] = '\0';
12043 switch (i) {
12044 case 0:
12045 /* ~ */
12046 snprintf(conf, sizeof conf, "%s/.%s",
12047 pwd->pw_dir, SWM_CONF_FILE);
12048 break;
12049 case 1:
12050 /* global */
12051 snprintf(conf, sizeof conf, "/etc/%s",
12052 SWM_CONF_FILE);
12053 break;
12054 case 2:
12055 /* ~ compat */
12056 snprintf(conf, sizeof conf, "%s/.%s",
12057 pwd->pw_dir, SWM_CONF_FILE_OLD);
12058 break;
12059 case 3:
12060 /* global compat */
12061 snprintf(conf, sizeof conf, "/etc/%s",
12062 SWM_CONF_FILE_OLD);
12063 break;
12064 default:
12065 goto noconfig;
12066 }
12067
12068 if (strlen(conf) && stat(conf, &sb) != -1)
12069 if (S_ISREG(sb.st_mode)) {
12070 cfile = conf;
12071 break;
12072 }
12073 }
12074 noconfig:
12075
12076 /* load conf (if any) */
12077 if (cfile)
12078 conf_load(cfile, SWM_CONF_DEFAULT);
12079
12080 validate_spawns();
12081
12082 if (getenv("SWM_STARTED") == NULL)
12083 setenv("SWM_STARTED", "YES", 1);
12084
12085 /* setup all bars */
12086 num_screens = get_screen_count();
12087 for (i = 0; i < num_screens; i++)
12088 TAILQ_FOREACH(r, &screens[i].rl, entry)
12089 bar_setup(r);
12090
12091 /* Manage existing windows. */
12092 grab_windows();
12093
12094 grabkeys();
12095 grabbuttons();
12096
12097 /* Stack all regions to trigger mapping. */
12098 for (i = 0; i < num_screens; i++)
12099 TAILQ_FOREACH(r, &screens[i].rl, entry)
12100 stack(r);
12101
12102 xcb_ungrab_server(conn);
12103 xcb_flush(conn);
12104
12105 /* Update state and bar of each newly mapped workspace. */
12106 for (i = 0; i < num_screens; i++)
12107 TAILQ_FOREACH(r, &screens[i].rl, entry) {
12108 r->ws->state = SWM_WS_STATE_MAPPED;
12109 bar_draw(r->bar);
12110 }
12111
12112 memset(&pfd, 0, sizeof(pfd));
12113 pfd[0].fd = xfd;
12114 pfd[0].events = POLLIN;
12115 pfd[1].fd = STDIN_FILENO;
12116 pfd[1].events = POLLIN;
12117
12118 while (running) {
12119 while ((evt = get_next_event(false))) {
12120 if (!running)
12121 goto done;
12122 event_handle(evt);
12123 free(evt);
12124 }
12125
12126 /* If just (re)started, set default focus if needed. */
12127 if (startup) {
12128 startup = false;
12129
12130 if (focus_mode != SWM_FOCUS_FOLLOW) {
12131 r = TAILQ_FIRST(&screens[0].rl);
12132 if (r) {
12133 focus_region(r);
12134 focus_flush();
12135 }
12136 continue;
12137 }
12138 }
12139
12140 if (search_resp)
12141 search_do_resp();
12142
12143 num_readable = poll(pfd, bar_extra ? 2 : 1, 1000);
12144 if (num_readable == -1) {
12145 DNPRINTF(SWM_D_MISC, "poll failed: %s",
12146 strerror(errno));
12147 } else if (num_readable > 0 && bar_extra &&
12148 pfd[1].revents & POLLIN) {
12149 stdin_ready = true;
12150 }
12151
12152 if (restart_wm)
12153 restart(NULL, NULL, NULL);
12154
12155 if (!running)
12156 goto done;
12157
12158 if (stdin_ready) {
12159 stdin_ready = false;
12160 bar_extra_update();
12161 }
12162
12163 /* Need to ensure the bar(s) are always updated. */
12164 for (i = 0; i < num_screens; i++)
12165 TAILQ_FOREACH(r, &screens[i].rl, entry)
12166 bar_draw(r->bar);
12167
12168 xcb_flush(conn);
12169 }
12170 done:
12171 shutdown_cleanup();
12172
12173 return (0);
12174 }