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