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