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