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