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