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