]> code.delx.au - spectrwm/blob - scrotwm.c
47e73191acec7a419541b448eb09938de107e323
[spectrwm] / scrotwm.c
1 /*
2 * Copyright (c) 2009-2010-2011 Marco Peereboom <marco@peereboom.us>
3 * Copyright (c) 2009-2010-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 *
9 * Permission to use, copy, modify, and distribute this software for any
10 * purpose with or without fee is hereby granted, provided that the above
11 * copyright notice and this permission notice appear in all copies.
12 *
13 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
14 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
15 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
16 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
17 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
18 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
19 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20 */
21 /*
22 * Much code and ideas taken from dwm under the following license:
23 * MIT/X Consortium License
24 *
25 * 2006-2008 Anselm R Garbe <garbeam at gmail dot com>
26 * 2006-2007 Sander van Dijk <a dot h dot vandijk at gmail dot com>
27 * 2006-2007 Jukka Salmi <jukka at salmi dot ch>
28 * 2007 Premysl Hruby <dfenze at gmail dot com>
29 * 2007 Szabolcs Nagy <nszabolcs at gmail dot com>
30 * 2007 Christof Musik <christof at sendfax dot de>
31 * 2007-2008 Enno Gottox Boland <gottox at s01 dot de>
32 * 2007-2008 Peter Hartlich <sgkkr at hartlich dot com>
33 * 2008 Martin Hurton <martin dot hurton at gmail dot com>
34 *
35 * Permission is hereby granted, free of charge, to any person obtaining a
36 * copy of this software and associated documentation files (the "Software"),
37 * to deal in the Software without restriction, including without limitation
38 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
39 * and/or sell copies of the Software, and to permit persons to whom the
40 * Software is furnished to do so, subject to the following conditions:
41 *
42 * The above copyright notice and this permission notice shall be included in
43 * all copies or substantial portions of the Software.
44 *
45 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
46 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
47 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
48 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
49 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
50 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
51 * DEALINGS IN THE SOFTWARE.
52 */
53
54 static const char *cvstag =
55 "$scrotwm$";
56
57 #define SWM_VERSION "0.9.34"
58
59 #include <stdio.h>
60 #include <stdlib.h>
61 #include <err.h>
62 #include <errno.h>
63 #include <fcntl.h>
64 #include <locale.h>
65 #include <unistd.h>
66 #include <time.h>
67 #include <signal.h>
68 #include <string.h>
69 #include <util.h>
70 #include <pwd.h>
71 #include <paths.h>
72 #include <ctype.h>
73
74 #include <sys/types.h>
75 #include <sys/time.h>
76 #include <sys/stat.h>
77 #include <sys/wait.h>
78 #include <sys/queue.h>
79 #include <sys/param.h>
80 #include <sys/select.h>
81
82 #include <X11/cursorfont.h>
83 #include <X11/keysym.h>
84 #include <X11/Xatom.h>
85 #include <X11/Xlib.h>
86 #include <X11/Xproto.h>
87 #include <X11/Xutil.h>
88 #include <X11/extensions/Xrandr.h>
89 #include <X11/extensions/XTest.h>
90
91 #ifdef __OSX__
92 #include <osx.h>
93 #endif
94
95 #if RANDR_MAJOR < 1
96 # error XRandR versions less than 1.0 are not supported
97 #endif
98
99 #if RANDR_MAJOR >= 1
100 #if RANDR_MINOR >= 2
101 #define SWM_XRR_HAS_CRTC
102 #endif
103 #endif
104
105 /*#define SWM_DEBUG*/
106 #ifdef SWM_DEBUG
107 #define DPRINTF(x...) do { if (swm_debug) fprintf(stderr, x); } while (0)
108 #define DNPRINTF(n,x...) do { if (swm_debug & n) fprintf(stderr, x); } while (0)
109 #define SWM_D_MISC 0x0001
110 #define SWM_D_EVENT 0x0002
111 #define SWM_D_WS 0x0004
112 #define SWM_D_FOCUS 0x0008
113 #define SWM_D_MOVE 0x0010
114 #define SWM_D_STACK 0x0020
115 #define SWM_D_MOUSE 0x0040
116 #define SWM_D_PROP 0x0080
117 #define SWM_D_CLASS 0x0100
118 #define SWM_D_KEY 0x0200
119 #define SWM_D_QUIRK 0x0400
120 #define SWM_D_SPAWN 0x0800
121 #define SWM_D_EVENTQ 0x1000
122 #define SWM_D_CONF 0x2000
123
124 u_int32_t swm_debug = 0
125 | SWM_D_MISC
126 | SWM_D_EVENT
127 | SWM_D_WS
128 | SWM_D_FOCUS
129 | SWM_D_MOVE
130 | SWM_D_STACK
131 | SWM_D_MOUSE
132 | SWM_D_PROP
133 | SWM_D_CLASS
134 | SWM_D_KEY
135 | SWM_D_QUIRK
136 | SWM_D_SPAWN
137 | SWM_D_EVENTQ
138 | SWM_D_CONF
139 ;
140 #else
141 #define DPRINTF(x...)
142 #define DNPRINTF(n,x...)
143 #endif
144
145 #define LENGTH(x) (sizeof x / sizeof x[0])
146 #define MODKEY Mod1Mask
147 #define CLEANMASK(mask) (mask & ~(numlockmask | LockMask))
148 #define BUTTONMASK (ButtonPressMask|ButtonReleaseMask)
149 #define MOUSEMASK (BUTTONMASK|PointerMotionMask)
150 #define SWM_PROPLEN (16)
151 #define SWM_FUNCNAME_LEN (32)
152 #define SWM_KEYS_LEN (255)
153 #define SWM_QUIRK_LEN (64)
154 #define X(r) (r)->g.x
155 #define Y(r) (r)->g.y
156 #define WIDTH(r) (r)->g.w
157 #define HEIGHT(r) (r)->g.h
158 #define SWM_MAX_FONT_STEPS (3)
159 #define WINID(w) (w ? w->id : 0)
160
161 #define SWM_FOCUS_DEFAULT (0)
162 #define SWM_FOCUS_SYNERGY (1)
163 #define SWM_FOCUS_FOLLOW (2)
164
165 #define SWM_CONF_DEFAULT (0)
166 #define SWM_CONF_KEYMAPPING (1)
167
168 #ifndef SWM_LIB
169 #define SWM_LIB "/usr/local/lib/libswmhack.so"
170 #endif
171
172 char **start_argv;
173 Atom astate;
174 Atom aprot;
175 Atom adelete;
176 Atom takefocus;
177 Atom a_wmname;
178 Atom a_utf8_string;
179 Atom a_string;
180 Atom a_swm_iconic;
181 volatile sig_atomic_t running = 1;
182 volatile sig_atomic_t restart_wm = 0;
183 int outputs = 0;
184 int last_focus_event = FocusOut;
185 int (*xerrorxlib)(Display *, XErrorEvent *);
186 int other_wm;
187 int ss_enabled = 0;
188 int xrandr_support;
189 int xrandr_eventbase;
190 unsigned int numlockmask = 0;
191 Display *display;
192
193 int cycle_empty = 0;
194 int cycle_visible = 0;
195 int term_width = 0;
196 int font_adjusted = 0;
197 unsigned int mod_key = MODKEY;
198
199 /* dmenu search */
200 struct swm_region *search_r;
201 int select_list_pipe[2];
202 int select_resp_pipe[2];
203 pid_t searchpid;
204 volatile sig_atomic_t search_resp;
205
206 /* dialog windows */
207 double dialog_ratio = .6;
208 /* status bar */
209 #define SWM_BAR_MAX (256)
210 char *bar_argv[] = { NULL, NULL };
211 int bar_pipe[2];
212 char bar_ext[SWM_BAR_MAX];
213 char bar_vertext[SWM_BAR_MAX];
214 int bar_version = 0;
215 sig_atomic_t bar_alarm = 0;
216 int bar_delay = 30;
217 int bar_enabled = 1;
218 int bar_border_width = 1;
219 int bar_at_bottom = 0;
220 int bar_extra = 1;
221 int bar_extra_running = 0;
222 int bar_verbose = 1;
223 int bar_height = 0;
224 int stack_enabled = 1;
225 int clock_enabled = 1;
226 int urgent_enabled = 0;
227 char *clock_format = NULL;
228 int title_name_enabled = 0;
229 int title_class_enabled = 0;
230 int window_name_enabled = 0;
231 int focus_mode = SWM_FOCUS_DEFAULT;
232 int disable_border = 0;
233 int border_width = 1;
234 int verbose_layout = 0;
235 pid_t bar_pid;
236 GC bar_gc;
237 XGCValues bar_gcv;
238 int bar_fidx = 0;
239 XFontStruct *bar_fs;
240 char *bar_fonts[] = { NULL, NULL, NULL, NULL };/* XXX Make fully dynamic */
241 char *spawn_term[] = { NULL, NULL }; /* XXX Make fully dynamic */
242 struct passwd *pwd;
243
244 #define SWM_MENU_FN (2)
245 #define SWM_MENU_NB (4)
246 #define SWM_MENU_NF (6)
247 #define SWM_MENU_SB (8)
248 #define SWM_MENU_SF (10)
249
250 /* layout manager data */
251 struct swm_geometry {
252 int x;
253 int y;
254 int w;
255 int h;
256 };
257
258 struct swm_screen;
259 struct workspace;
260
261 /* virtual "screens" */
262 struct swm_region {
263 TAILQ_ENTRY(swm_region) entry;
264 struct swm_geometry g;
265 struct workspace *ws; /* current workspace on this region */
266 struct workspace *ws_prior; /* prior workspace on this region */
267 struct swm_screen *s; /* screen idx */
268 Window bar_window;
269 };
270 TAILQ_HEAD(swm_region_list, swm_region);
271
272 struct ws_win {
273 TAILQ_ENTRY(ws_win) entry;
274 Window id;
275 Window transient;
276 struct ws_win *child_trans; /* transient child window */
277 struct swm_geometry g; /* current geometry */
278 struct swm_geometry g_float; /* geometry when floating */
279 struct swm_geometry rg_float; /* region geom when floating */
280 int g_floatvalid; /* flag: geometry in g_float is valid */
281 int floatmaxed; /* flag: floater was maxed in max_stack */
282 int floating;
283 int manual;
284 int iconic;
285 unsigned int ewmh_flags;
286 int font_size_boundary[SWM_MAX_FONT_STEPS];
287 int font_steps;
288 int last_inc;
289 int can_delete;
290 int take_focus;
291 int java;
292 unsigned long quirks;
293 struct workspace *ws; /* always valid */
294 struct swm_screen *s; /* always valid, never changes */
295 XWindowAttributes wa;
296 XSizeHints sh;
297 XClassHint ch;
298 XWMHints *hints;
299 };
300 TAILQ_HEAD(ws_win_list, ws_win);
301
302 /* pid goo */
303 struct pid_e {
304 TAILQ_ENTRY(pid_e) entry;
305 long pid;
306 int ws;
307 };
308 TAILQ_HEAD(pid_list, pid_e);
309 struct pid_list pidlist = TAILQ_HEAD_INITIALIZER(pidlist);
310
311 /* layout handlers */
312 void stack(void);
313 void vertical_config(struct workspace *, int);
314 void vertical_stack(struct workspace *, struct swm_geometry *);
315 void horizontal_config(struct workspace *, int);
316 void horizontal_stack(struct workspace *, struct swm_geometry *);
317 void max_stack(struct workspace *, struct swm_geometry *);
318 void plain_stacker(struct workspace *);
319 void fancy_stacker(struct workspace *);
320
321 struct ws_win *find_window(Window);
322
323 void grabbuttons(struct ws_win *, int);
324 void new_region(struct swm_screen *, int, int, int, int);
325 void unmanage_window(struct ws_win *);
326 long getstate(Window);
327
328 int conf_load(char *, int);
329
330 struct layout {
331 void (*l_stack)(struct workspace *, struct swm_geometry *);
332 void (*l_config)(struct workspace *, int);
333 u_int32_t flags;
334 #define SWM_L_FOCUSPREV (1<<0)
335 #define SWM_L_MAPONFOCUS (1<<1)
336 void (*l_string)(struct workspace *);
337 } layouts[] = {
338 /* stack, configure */
339 { vertical_stack, vertical_config, 0, plain_stacker },
340 { horizontal_stack, horizontal_config, 0, plain_stacker },
341 { max_stack, NULL,
342 SWM_L_MAPONFOCUS | SWM_L_FOCUSPREV, plain_stacker },
343 { NULL, NULL, 0, NULL },
344 };
345
346 /* position of max_stack mode in the layouts array, index into layouts! */
347 #define SWM_V_STACK (0)
348 #define SWM_H_STACK (1)
349 #define SWM_MAX_STACK (2)
350
351 #define SWM_H_SLICE (32)
352 #define SWM_V_SLICE (32)
353
354 /* define work spaces */
355 struct workspace {
356 int idx; /* workspace index */
357 int always_raise; /* raise windows on focus */
358 struct layout *cur_layout; /* current layout handlers */
359 struct ws_win *focus; /* may be NULL */
360 struct ws_win *focus_prev; /* may be NULL */
361 struct swm_region *r; /* may be NULL */
362 struct swm_region *old_r; /* may be NULL */
363 struct ws_win_list winlist; /* list of windows in ws */
364 struct ws_win_list unmanagedlist; /* list of dead windows in ws */
365 char stacker[10]; /* display stacker and layout */
366
367 /* stacker state */
368 struct {
369 int horizontal_msize;
370 int horizontal_mwin;
371 int horizontal_stacks;
372 int vertical_msize;
373 int vertical_mwin;
374 int vertical_stacks;
375 } l_state;
376 };
377
378 enum { SWM_S_COLOR_BAR, SWM_S_COLOR_BAR_BORDER, SWM_S_COLOR_BAR_FONT,
379 SWM_S_COLOR_FOCUS, SWM_S_COLOR_UNFOCUS, SWM_S_COLOR_MAX };
380
381 /* physical screen mapping */
382 #define SWM_WS_MAX (10)
383 struct swm_screen {
384 int idx; /* screen index */
385 struct swm_region_list rl; /* list of regions on this screen */
386 struct swm_region_list orl; /* list of old regions */
387 Window root;
388 struct workspace ws[SWM_WS_MAX];
389
390 /* colors */
391 struct {
392 unsigned long color;
393 char *name;
394 } c[SWM_S_COLOR_MAX];
395 };
396 struct swm_screen *screens;
397 int num_screens;
398
399 /* args to functions */
400 union arg {
401 int id;
402 #define SWM_ARG_ID_FOCUSNEXT (0)
403 #define SWM_ARG_ID_FOCUSPREV (1)
404 #define SWM_ARG_ID_FOCUSMAIN (2)
405 #define SWM_ARG_ID_FOCUSCUR (4)
406 #define SWM_ARG_ID_SWAPNEXT (10)
407 #define SWM_ARG_ID_SWAPPREV (11)
408 #define SWM_ARG_ID_SWAPMAIN (12)
409 #define SWM_ARG_ID_MOVELAST (13)
410 #define SWM_ARG_ID_MASTERSHRINK (20)
411 #define SWM_ARG_ID_MASTERGROW (21)
412 #define SWM_ARG_ID_MASTERADD (22)
413 #define SWM_ARG_ID_MASTERDEL (23)
414 #define SWM_ARG_ID_STACKRESET (30)
415 #define SWM_ARG_ID_STACKINIT (31)
416 #define SWM_ARG_ID_CYCLEWS_UP (40)
417 #define SWM_ARG_ID_CYCLEWS_DOWN (41)
418 #define SWM_ARG_ID_CYCLESC_UP (42)
419 #define SWM_ARG_ID_CYCLESC_DOWN (43)
420 #define SWM_ARG_ID_STACKINC (50)
421 #define SWM_ARG_ID_STACKDEC (51)
422 #define SWM_ARG_ID_SS_ALL (60)
423 #define SWM_ARG_ID_SS_WINDOW (61)
424 #define SWM_ARG_ID_DONTCENTER (70)
425 #define SWM_ARG_ID_CENTER (71)
426 #define SWM_ARG_ID_KILLWINDOW (80)
427 #define SWM_ARG_ID_DELETEWINDOW (81)
428 #define SWM_ARG_ID_WIDTHGROW (90)
429 #define SWM_ARG_ID_WIDTHSHRINK (91)
430 #define SWM_ARG_ID_HEIGHTGROW (92)
431 #define SWM_ARG_ID_HEIGHTSHRINK (93)
432 #define SWM_ARG_ID_MOVEUP (100)
433 #define SWM_ARG_ID_MOVEDOWN (101)
434 #define SWM_ARG_ID_MOVELEFT (102)
435 #define SWM_ARG_ID_MOVERIGHT (103)
436 char **argv;
437 };
438
439 void focus(struct swm_region *, union arg *);
440 void focus_magic(struct ws_win *);
441
442 /* quirks */
443 struct quirk {
444 char *class;
445 char *name;
446 unsigned long quirk;
447 #define SWM_Q_FLOAT (1<<0) /* float this window */
448 #define SWM_Q_TRANSSZ (1<<1) /* transiend window size too small */
449 #define SWM_Q_ANYWHERE (1<<2) /* don't position this window */
450 #define SWM_Q_XTERM_FONTADJ (1<<3) /* adjust xterm fonts when resizing */
451 #define SWM_Q_FULLSCREEN (1<<4) /* remove border */
452 #define SWM_Q_FOCUSPREV (1<<5) /* focus on caller */
453 };
454 int quirks_size = 0, quirks_length = 0;
455 struct quirk *quirks = NULL;
456
457 /*
458 * Supported EWMH hints should be added to
459 * both the enum and the ewmh array
460 */
461 enum { _NET_ACTIVE_WINDOW, _NET_MOVERESIZE_WINDOW, _NET_CLOSE_WINDOW,
462 _NET_WM_WINDOW_TYPE, _NET_WM_WINDOW_TYPE_DOCK,
463 _NET_WM_WINDOW_TYPE_TOOLBAR, _NET_WM_WINDOW_TYPE_UTILITY,
464 _NET_WM_WINDOW_TYPE_SPLASH, _NET_WM_WINDOW_TYPE_DIALOG,
465 _NET_WM_WINDOW_TYPE_NORMAL, _NET_WM_STATE,
466 _NET_WM_STATE_MAXIMIZED_HORZ, _NET_WM_STATE_MAXIMIZED_VERT,
467 _NET_WM_STATE_SKIP_TASKBAR, _NET_WM_STATE_SKIP_PAGER,
468 _NET_WM_STATE_HIDDEN, _NET_WM_STATE_ABOVE, _SWM_WM_STATE_MANUAL,
469 _NET_WM_STATE_FULLSCREEN, _NET_WM_ALLOWED_ACTIONS, _NET_WM_ACTION_MOVE,
470 _NET_WM_ACTION_RESIZE, _NET_WM_ACTION_FULLSCREEN, _NET_WM_ACTION_CLOSE,
471 SWM_EWMH_HINT_MAX };
472
473 struct ewmh_hint {
474 char *name;
475 Atom atom;
476 } ewmh[SWM_EWMH_HINT_MAX] = {
477 /* must be in same order as in the enum */
478 {"_NET_ACTIVE_WINDOW", None},
479 {"_NET_MOVERESIZE_WINDOW", None},
480 {"_NET_CLOSE_WINDOW", None},
481 {"_NET_WM_WINDOW_TYPE", None},
482 {"_NET_WM_WINDOW_TYPE_DOCK", None},
483 {"_NET_WM_WINDOW_TYPE_TOOLBAR", None},
484 {"_NET_WM_WINDOW_TYPE_UTILITY", None},
485 {"_NET_WM_WINDOW_TYPE_SPLASH", None},
486 {"_NET_WM_WINDOW_TYPE_DIALOG", None},
487 {"_NET_WM_WINDOW_TYPE_NORMAL", None},
488 {"_NET_WM_STATE", None},
489 {"_NET_WM_STATE_MAXIMIZED_HORZ", None},
490 {"_NET_WM_STATE_MAXIMIZED_VERT", None},
491 {"_NET_WM_STATE_SKIP_TASKBAR", None},
492 {"_NET_WM_STATE_SKIP_PAGER", None},
493 {"_NET_WM_STATE_HIDDEN", None},
494 {"_NET_WM_STATE_ABOVE", None},
495 {"_SWM_WM_STATE_MANUAL", None},
496 {"_NET_WM_STATE_FULLSCREEN", None},
497 {"_NET_WM_ALLOWED_ACTIONS", None},
498 {"_NET_WM_ACTION_MOVE", None},
499 {"_NET_WM_ACTION_RESIZE", None},
500 {"_NET_WM_ACTION_FULLSCREEN", None},
501 {"_NET_WM_ACTION_CLOSE", None},
502 };
503
504 void store_float_geom(struct ws_win *win, struct swm_region *r);
505 int floating_toggle_win(struct ws_win *win);
506 void spawn_select(struct swm_region *, union arg *, char *, int *);
507
508 int
509 get_property(Window id, Atom atom, long count, Atom type,
510 unsigned long *n, unsigned char **data)
511 {
512 int format, status;
513 unsigned long tmp, extra;
514 unsigned long *nitems;
515 Atom real;
516
517 nitems = n != NULL ? n : &tmp;
518 status = XGetWindowProperty(display, id, atom, 0L, count, False, type,
519 &real, &format, nitems, &extra, data);
520
521 if (status != Success)
522 return False;
523 if (real != type)
524 return False;
525
526 return True;
527 }
528
529 void
530 update_iconic(struct ws_win *win, int newv)
531 {
532 int32_t v = newv;
533 Atom iprop;
534
535 win->iconic = newv;
536
537 iprop = XInternAtom(display, "_SWM_ICONIC", False);
538 if (!iprop)
539 return;
540 if (newv)
541 XChangeProperty(display, win->id, iprop, XA_INTEGER, 32,
542 PropModeReplace, (unsigned char *)&v, 1);
543 else
544 XDeleteProperty(display, win->id, iprop);
545 }
546
547 int
548 get_iconic(struct ws_win *win)
549 {
550 int32_t v = 0;
551 int retfmt, status;
552 Atom iprop, rettype;
553 unsigned long nitems, extra;
554 unsigned char *prop = NULL;
555
556 iprop = XInternAtom(display, "_SWM_ICONIC", False);
557 if (!iprop)
558 goto out;
559 status = XGetWindowProperty(display, win->id, iprop, 0L, 1L,
560 False, XA_INTEGER, &rettype, &retfmt, &nitems, &extra, &prop);
561 if (status != Success)
562 goto out;
563 if (rettype != XA_INTEGER || retfmt != 32)
564 goto out;
565 if (nitems != 1)
566 goto out;
567 v = *((int32_t *)prop);
568
569 out:
570 if (prop != NULL)
571 XFree(prop);
572 return (v);
573 }
574
575 void
576 setup_ewmh(void)
577 {
578 int i,j;
579 Atom sup_list;
580
581 sup_list = XInternAtom(display, "_NET_SUPPORTED", False);
582
583 for (i = 0; i < LENGTH(ewmh); i++)
584 ewmh[i].atom = XInternAtom(display, ewmh[i].name, False);
585
586 for (i = 0; i < ScreenCount(display); i++) {
587 /* Support check window will be created by workaround(). */
588
589 /* Report supported atoms */
590 XDeleteProperty(display, screens[i].root, sup_list);
591 for (j = 0; j < LENGTH(ewmh); j++)
592 XChangeProperty(display, screens[i].root,
593 sup_list, XA_ATOM, 32,
594 PropModeAppend, (unsigned char *)&ewmh[j].atom,1);
595 }
596 }
597
598 void
599 teardown_ewmh(void)
600 {
601 int i, success;
602 unsigned char *data = NULL;
603 unsigned long n;
604 Atom sup_check, sup_list;
605 Window id;
606
607 sup_check = XInternAtom(display, "_NET_SUPPORTING_WM_CHECK", False);
608 sup_list = XInternAtom(display, "_NET_SUPPORTED", False);
609
610 for (i = 0; i < ScreenCount(display); i++) {
611 /* Get the support check window and destroy it */
612 success = get_property(screens[i].root, sup_check, 1, XA_WINDOW,
613 &n, &data);
614
615 if (success) {
616 id = data[0];
617 XDestroyWindow(display, id);
618 XDeleteProperty(display, screens[i].root, sup_check);
619 XDeleteProperty(display, screens[i].root, sup_list);
620 }
621
622 XFree(data);
623 }
624 }
625
626 void
627 ewmh_autoquirk(struct ws_win *win)
628 {
629 int success, i;
630 unsigned long *data = NULL, n;
631 Atom type;
632
633 success = get_property(win->id, ewmh[_NET_WM_WINDOW_TYPE].atom, (~0L),
634 XA_ATOM, &n, (void *)&data);
635
636 if (!success) {
637 XFree(data);
638 return;
639 }
640
641 for (i = 0; i < n; i++) {
642 type = data[i];
643 if (type == ewmh[_NET_WM_WINDOW_TYPE_NORMAL].atom)
644 break;
645 if (type == ewmh[_NET_WM_WINDOW_TYPE_DOCK].atom ||
646 type == ewmh[_NET_WM_WINDOW_TYPE_TOOLBAR].atom ||
647 type == ewmh[_NET_WM_WINDOW_TYPE_UTILITY].atom) {
648 win->floating = 1;
649 win->quirks = SWM_Q_FLOAT | SWM_Q_ANYWHERE;
650 break;
651 }
652 if (type == ewmh[_NET_WM_WINDOW_TYPE_SPLASH].atom ||
653 type == ewmh[_NET_WM_WINDOW_TYPE_DIALOG].atom) {
654 win->floating = 1;
655 win->quirks = SWM_Q_FLOAT;
656 break;
657 }
658 }
659
660 XFree(data);
661 }
662
663 #define SWM_EWMH_ACTION_COUNT_MAX (6)
664 #define EWMH_F_FULLSCREEN (1<<0)
665 #define EWMH_F_ABOVE (1<<1)
666 #define EWMH_F_HIDDEN (1<<2)
667 #define EWMH_F_SKIP_PAGER (1<<3)
668 #define EWMH_F_SKIP_TASKBAR (1<<4)
669 #define SWM_F_MANUAL (1<<5)
670
671 int
672 ewmh_set_win_fullscreen(struct ws_win *win, int fs)
673 {
674 struct swm_geometry rg;
675
676 if (!win->ws->r)
677 return 0;
678
679 if (!win->floating)
680 return 0;
681
682 DNPRINTF(SWM_D_MISC, "ewmh_set_win_fullscreen: win 0x%lx fs: %d\n",
683 win->id, fs);
684
685 rg = win->ws->r->g;
686
687 if (fs) {
688 store_float_geom(win, win->ws->r);
689
690 win->g.x = rg.x;
691 win->g.y = rg.y;
692 win->g.w = rg.w;
693 win->g.h = rg.h;
694 } else {
695 if (win->g_floatvalid) {
696 /* refloat at last floating relative position */
697 win->g.x = win->g_float.x - win->rg_float.x + rg.x;
698 win->g.y = win->g_float.y - win->rg_float.y + rg.y;
699 win->g.w = win->g_float.w;
700 win->g.h = win->g_float.h;
701 }
702 }
703
704 return 1;
705 }
706
707 void
708 ewmh_update_actions(struct ws_win *win)
709 {
710 Atom actions[SWM_EWMH_ACTION_COUNT_MAX];
711 int n = 0;
712
713 if (win == NULL)
714 return;
715
716 actions[n++] = ewmh[_NET_WM_ACTION_CLOSE].atom;
717
718 if (win->floating) {
719 actions[n++] = ewmh[_NET_WM_ACTION_MOVE].atom;
720 actions[n++] = ewmh[_NET_WM_ACTION_RESIZE].atom;
721 }
722
723 XChangeProperty(display, win->id, ewmh[_NET_WM_ALLOWED_ACTIONS].atom,
724 XA_ATOM, 32, PropModeReplace, (unsigned char *)actions, n);
725 }
726
727 #define _NET_WM_STATE_REMOVE 0 /* remove/unset property */
728 #define _NET_WM_STATE_ADD 1 /* add/set property */
729 #define _NET_WM_STATE_TOGGLE 2 /* toggle property */
730
731 void
732 ewmh_update_win_state(struct ws_win *win, long state, long action)
733 {
734 unsigned int mask = 0;
735 unsigned int changed = 0;
736 unsigned int orig_flags;
737
738 if (win == NULL)
739 return;
740
741 if (state == ewmh[_NET_WM_STATE_FULLSCREEN].atom)
742 mask = EWMH_F_FULLSCREEN;
743 if (state == ewmh[_NET_WM_STATE_ABOVE].atom)
744 mask = EWMH_F_ABOVE;
745 if (state == ewmh[_SWM_WM_STATE_MANUAL].atom)
746 mask = SWM_F_MANUAL;
747 if (state == ewmh[_NET_WM_STATE_SKIP_PAGER].atom)
748 mask = EWMH_F_SKIP_PAGER;
749 if (state == ewmh[_NET_WM_STATE_SKIP_TASKBAR].atom)
750 mask = EWMH_F_SKIP_TASKBAR;
751
752
753 orig_flags = win->ewmh_flags;
754
755 switch (action) {
756 case _NET_WM_STATE_REMOVE:
757 win->ewmh_flags &= ~mask;
758 break;
759 case _NET_WM_STATE_ADD:
760 win->ewmh_flags |= mask;
761 break;
762 case _NET_WM_STATE_TOGGLE:
763 win->ewmh_flags ^= mask;
764 break;
765 }
766
767 changed = (win->ewmh_flags & mask) ^ (orig_flags & mask) ? 1 : 0;
768
769 if (state == ewmh[_NET_WM_STATE_ABOVE].atom)
770 if (changed)
771 if (!floating_toggle_win(win))
772 win->ewmh_flags = orig_flags; /* revert */
773 if (state == ewmh[_SWM_WM_STATE_MANUAL].atom)
774 if (changed)
775 win->manual = (win->ewmh_flags & SWM_F_MANUAL) != 0;
776 if (state == ewmh[_NET_WM_STATE_FULLSCREEN].atom)
777 if (changed)
778 if (!ewmh_set_win_fullscreen(win,
779 win->ewmh_flags & EWMH_F_FULLSCREEN))
780 win->ewmh_flags = orig_flags; /* revert */
781
782 XDeleteProperty(display, win->id, ewmh[_NET_WM_STATE].atom);
783
784 if (win->ewmh_flags & EWMH_F_FULLSCREEN)
785 XChangeProperty(display, win->id, ewmh[_NET_WM_STATE].atom,
786 XA_ATOM, 32, PropModeAppend,
787 (unsigned char *)&ewmh[_NET_WM_STATE_FULLSCREEN].atom, 1);
788 if (win->ewmh_flags & EWMH_F_SKIP_PAGER)
789 XChangeProperty(display, win->id, ewmh[_NET_WM_STATE].atom,
790 XA_ATOM, 32, PropModeAppend,
791 (unsigned char *)&ewmh[_NET_WM_STATE_SKIP_PAGER].atom, 1);
792 if (win->ewmh_flags & EWMH_F_SKIP_TASKBAR)
793 XChangeProperty(display, win->id, ewmh[_NET_WM_STATE].atom,
794 XA_ATOM, 32, PropModeAppend,
795 (unsigned char *)&ewmh[_NET_WM_STATE_SKIP_TASKBAR].atom, 1);
796 if (win->ewmh_flags & EWMH_F_ABOVE)
797 XChangeProperty(display, win->id, ewmh[_NET_WM_STATE].atom,
798 XA_ATOM, 32, PropModeAppend,
799 (unsigned char *)&ewmh[_NET_WM_STATE_ABOVE].atom, 1);
800 if (win->ewmh_flags & SWM_F_MANUAL)
801 XChangeProperty(display, win->id, ewmh[_NET_WM_STATE].atom,
802 XA_ATOM, 32, PropModeAppend,
803 (unsigned char *)&ewmh[_SWM_WM_STATE_MANUAL].atom, 1);
804 }
805
806 void
807 ewmh_get_win_state(struct ws_win *win)
808 {
809 int success, i;
810 unsigned long n;
811 Atom *states;
812
813 if (win == NULL)
814 return;
815
816 win->ewmh_flags = 0;
817 if (win->floating)
818 win->ewmh_flags |= EWMH_F_ABOVE;
819 if (win->manual)
820 win->ewmh_flags |= SWM_F_MANUAL;
821
822 success = get_property(win->id, ewmh[_NET_WM_STATE].atom,
823 (~0L), XA_ATOM, &n, (void *)&states);
824
825 if (!success)
826 return;
827
828 for (i = 0; i < n; i++)
829 ewmh_update_win_state(win, states[i], _NET_WM_STATE_ADD);
830
831 XFree(states);
832 }
833
834 /* events */
835 #ifdef SWM_DEBUG
836 void
837 dumpevent(XEvent *e)
838 {
839 char *name = NULL;
840
841 switch (e->type) {
842 case KeyPress:
843 name = "KeyPress";
844 break;
845 case KeyRelease:
846 name = "KeyRelease";
847 break;
848 case ButtonPress:
849 name = "ButtonPress";
850 break;
851 case ButtonRelease:
852 name = "ButtonRelease";
853 break;
854 case MotionNotify:
855 name = "MotionNotify";
856 break;
857 case EnterNotify:
858 name = "EnterNotify";
859 break;
860 case LeaveNotify:
861 name = "LeaveNotify";
862 break;
863 case FocusIn:
864 name = "FocusIn";
865 break;
866 case FocusOut:
867 name = "FocusOut";
868 break;
869 case KeymapNotify:
870 name = "KeymapNotify";
871 break;
872 case Expose:
873 name = "Expose";
874 break;
875 case GraphicsExpose:
876 name = "GraphicsExpose";
877 break;
878 case NoExpose:
879 name = "NoExpose";
880 break;
881 case VisibilityNotify:
882 name = "VisibilityNotify";
883 break;
884 case CreateNotify:
885 name = "CreateNotify";
886 break;
887 case DestroyNotify:
888 name = "DestroyNotify";
889 break;
890 case UnmapNotify:
891 name = "UnmapNotify";
892 break;
893 case MapNotify:
894 name = "MapNotify";
895 break;
896 case MapRequest:
897 name = "MapRequest";
898 break;
899 case ReparentNotify:
900 name = "ReparentNotify";
901 break;
902 case ConfigureNotify:
903 name = "ConfigureNotify";
904 break;
905 case ConfigureRequest:
906 name = "ConfigureRequest";
907 break;
908 case GravityNotify:
909 name = "GravityNotify";
910 break;
911 case ResizeRequest:
912 name = "ResizeRequest";
913 break;
914 case CirculateNotify:
915 name = "CirculateNotify";
916 break;
917 case CirculateRequest:
918 name = "CirculateRequest";
919 break;
920 case PropertyNotify:
921 name = "PropertyNotify";
922 break;
923 case SelectionClear:
924 name = "SelectionClear";
925 break;
926 case SelectionRequest:
927 name = "SelectionRequest";
928 break;
929 case SelectionNotify:
930 name = "SelectionNotify";
931 break;
932 case ColormapNotify:
933 name = "ColormapNotify";
934 break;
935 case ClientMessage:
936 name = "ClientMessage";
937 break;
938 case MappingNotify:
939 name = "MappingNotify";
940 break;
941 }
942
943 if (name)
944 DNPRINTF(SWM_D_EVENTQ ,"window: %lu event: %s (%d), %d "
945 "remaining\n",
946 e->xany.window, name, e->type, QLength(display));
947 else
948 DNPRINTF(SWM_D_EVENTQ, "window: %lu unknown event %d, %d "
949 "remaining\n",
950 e->xany.window, e->type, QLength(display));
951 }
952
953 void
954 dumpwins(struct swm_region *r, union arg *args)
955 {
956 struct ws_win *win;
957 unsigned int state;
958 XWindowAttributes wa;
959
960 if (r->ws == NULL) {
961 fprintf(stderr, "invalid workspace\n");
962 return;
963 }
964
965 fprintf(stderr, "=== managed window list ws %02d ===\n", r->ws->idx);
966
967 TAILQ_FOREACH(win, &r->ws->winlist, entry) {
968 state = getstate(win->id);
969 if (!XGetWindowAttributes(display, win->id, &wa))
970 fprintf(stderr, "window: %lu failed "
971 "XGetWindowAttributes\n", win->id);
972 fprintf(stderr, "window: %lu map_state: %d state: %d "
973 "transient: %lu\n",
974 win->id, wa.map_state, state, win->transient);
975 }
976
977 fprintf(stderr, "===== unmanaged window list =====\n");
978 TAILQ_FOREACH(win, &r->ws->unmanagedlist, entry) {
979 state = getstate(win->id);
980 if (!XGetWindowAttributes(display, win->id, &wa))
981 fprintf(stderr, "window: %lu failed "
982 "XGetWindowAttributes\n", win->id);
983 fprintf(stderr, "window: %lu map_state: %d state: %d "
984 "transient: %lu\n",
985 win->id, wa.map_state, state, win->transient);
986 }
987
988 fprintf(stderr, "=================================\n");
989 }
990 #else
991 #define dumpevent(e)
992 void
993 dumpwins(struct swm_region *r, union arg *args)
994 {
995 }
996 #endif /* SWM_DEBUG */
997
998 void expose(XEvent *);
999 void keypress(XEvent *);
1000 void buttonpress(XEvent *);
1001 void configurerequest(XEvent *);
1002 void configurenotify(XEvent *);
1003 void destroynotify(XEvent *);
1004 void enternotify(XEvent *);
1005 void focusevent(XEvent *);
1006 void mapnotify(XEvent *);
1007 void mappingnotify(XEvent *);
1008 void maprequest(XEvent *);
1009 void propertynotify(XEvent *);
1010 void unmapnotify(XEvent *);
1011 void visibilitynotify(XEvent *);
1012 void clientmessage(XEvent *);
1013
1014 void (*handler[LASTEvent])(XEvent *) = {
1015 [Expose] = expose,
1016 [KeyPress] = keypress,
1017 [ButtonPress] = buttonpress,
1018 [ConfigureRequest] = configurerequest,
1019 [ConfigureNotify] = configurenotify,
1020 [DestroyNotify] = destroynotify,
1021 [EnterNotify] = enternotify,
1022 [FocusIn] = focusevent,
1023 [FocusOut] = focusevent,
1024 [MapNotify] = mapnotify,
1025 [MappingNotify] = mappingnotify,
1026 [MapRequest] = maprequest,
1027 [PropertyNotify] = propertynotify,
1028 [UnmapNotify] = unmapnotify,
1029 [VisibilityNotify] = visibilitynotify,
1030 [ClientMessage] = clientmessage,
1031 };
1032
1033 void
1034 sighdlr(int sig)
1035 {
1036 int saved_errno, status;
1037 pid_t pid;
1038
1039 saved_errno = errno;
1040
1041 switch (sig) {
1042 case SIGCHLD:
1043 while ((pid = waitpid(WAIT_ANY, &status, WNOHANG)) != 0) {
1044 if (pid == -1) {
1045 if (errno == EINTR)
1046 continue;
1047 #ifdef SWM_DEBUG
1048 if (errno != ECHILD)
1049 warn("sighdlr: waitpid");
1050 #endif /* SWM_DEBUG */
1051 break;
1052 }
1053 if (pid == searchpid)
1054 search_resp = 1;
1055
1056 #ifdef SWM_DEBUG
1057 if (WIFEXITED(status)) {
1058 if (WEXITSTATUS(status) != 0)
1059 warnx("sighdlr: child exit status: %d",
1060 WEXITSTATUS(status));
1061 } else
1062 warnx("sighdlr: child is terminated "
1063 "abnormally");
1064 #endif /* SWM_DEBUG */
1065 }
1066 break;
1067
1068 case SIGHUP:
1069 restart_wm = 1;
1070 break;
1071 case SIGINT:
1072 case SIGTERM:
1073 case SIGQUIT:
1074 running = 0;
1075 break;
1076 }
1077
1078 errno = saved_errno;
1079 }
1080
1081 struct pid_e *
1082 find_pid(long pid)
1083 {
1084 struct pid_e *p = NULL;
1085
1086 DNPRINTF(SWM_D_MISC, "find_pid: %lu\n", pid);
1087
1088 if (pid == 0)
1089 return (NULL);
1090
1091 TAILQ_FOREACH(p, &pidlist, entry) {
1092 if (p->pid == pid)
1093 return (p);
1094 }
1095
1096 return (NULL);
1097 }
1098
1099 unsigned long
1100 name_to_color(char *colorname)
1101 {
1102 Colormap cmap;
1103 Status status;
1104 XColor screen_def, exact_def;
1105 unsigned long result = 0;
1106 char cname[32] = "#";
1107
1108 cmap = DefaultColormap(display, screens[0].idx);
1109 status = XAllocNamedColor(display, cmap, colorname,
1110 &screen_def, &exact_def);
1111 if (!status) {
1112 strlcat(cname, colorname + 2, sizeof cname - 1);
1113 status = XAllocNamedColor(display, cmap, cname, &screen_def,
1114 &exact_def);
1115 }
1116 if (status)
1117 result = screen_def.pixel;
1118 else
1119 fprintf(stderr, "color '%s' not found.\n", colorname);
1120
1121 return (result);
1122 }
1123
1124 void
1125 setscreencolor(char *val, int i, int c)
1126 {
1127 if (i > 0 && i <= ScreenCount(display)) {
1128 screens[i - 1].c[c].color = name_to_color(val);
1129 free(screens[i - 1].c[c].name);
1130 if ((screens[i - 1].c[c].name = strdup(val)) == NULL)
1131 errx(1, "strdup");
1132 } else if (i == -1) {
1133 for (i = 0; i < ScreenCount(display); i++) {
1134 screens[i].c[c].color = name_to_color(val);
1135 free(screens[i].c[c].name);
1136 if ((screens[i].c[c].name = strdup(val)) == NULL)
1137 errx(1, "strdup");
1138 }
1139 } else
1140 errx(1, "invalid screen index: %d out of bounds (maximum %d)\n",
1141 i, ScreenCount(display));
1142 }
1143
1144 void
1145 fancy_stacker(struct workspace *ws)
1146 {
1147 strcpy(ws->stacker, "[ ]");
1148 if (ws->cur_layout->l_stack == vertical_stack)
1149 snprintf(ws->stacker, sizeof ws->stacker, "[%d|%d]",
1150 ws->l_state.vertical_mwin, ws->l_state.vertical_stacks);
1151 if (ws->cur_layout->l_stack == horizontal_stack)
1152 snprintf(ws->stacker, sizeof ws->stacker, "[%d-%d]",
1153 ws->l_state.horizontal_mwin, ws->l_state.horizontal_stacks);
1154 }
1155
1156 void
1157 plain_stacker(struct workspace *ws)
1158 {
1159 strcpy(ws->stacker, "[ ]");
1160 if (ws->cur_layout->l_stack == vertical_stack)
1161 strcpy(ws->stacker, "[|]");
1162 if (ws->cur_layout->l_stack == horizontal_stack)
1163 strcpy(ws->stacker, "[-]");
1164 }
1165
1166 void
1167 custom_region(char *val)
1168 {
1169 unsigned int sidx, x, y, w, h;
1170
1171 if (sscanf(val, "screen[%u]:%ux%u+%u+%u", &sidx, &w, &h, &x, &y) != 5)
1172 errx(1, "invalid custom region, "
1173 "should be 'screen[<n>]:<n>x<n>+<n>+<n>\n");
1174 if (sidx < 1 || sidx > ScreenCount(display))
1175 errx(1, "invalid screen index: %d out of bounds (maximum %d)\n",
1176 sidx, ScreenCount(display));
1177 sidx--;
1178
1179 if (w < 1 || h < 1)
1180 errx(1, "region %ux%u+%u+%u too small\n", w, h, x, y);
1181
1182 if (x > DisplayWidth(display, sidx) ||
1183 y > DisplayHeight(display, sidx) ||
1184 w + x > DisplayWidth(display, sidx) ||
1185 h + y > DisplayHeight(display, sidx)) {
1186 fprintf(stderr, "ignoring region %ux%u+%u+%u "
1187 "- not within screen boundaries "
1188 "(%ux%u)\n", w, h, x, y,
1189 DisplayWidth(display, sidx), DisplayHeight(display, sidx));
1190 return;
1191 }
1192
1193 new_region(&screens[sidx], x, y, w, h);
1194 }
1195
1196 void
1197 socket_setnonblock(int fd)
1198 {
1199 int flags;
1200
1201 if ((flags = fcntl(fd, F_GETFL, 0)) == -1)
1202 err(1, "fcntl F_GETFL");
1203 flags |= O_NONBLOCK;
1204 if ((flags = fcntl(fd, F_SETFL, flags)) == -1)
1205 err(1, "fcntl F_SETFL");
1206 }
1207
1208 void
1209 bar_print(struct swm_region *r, char *s)
1210 {
1211 XClearWindow(display, r->bar_window);
1212 XSetForeground(display, bar_gc, r->s->c[SWM_S_COLOR_BAR_FONT].color);
1213 XDrawString(display, r->bar_window, bar_gc, 4, bar_fs->ascent, s,
1214 strlen(s));
1215 }
1216
1217 void
1218 bar_extra_stop(void)
1219 {
1220 if (bar_pipe[0]) {
1221 close(bar_pipe[0]);
1222 bzero(bar_pipe, sizeof bar_pipe);
1223 }
1224 if (bar_pid) {
1225 kill(bar_pid, SIGTERM);
1226 bar_pid = 0;
1227 }
1228 strlcpy(bar_ext, "", sizeof bar_ext);
1229 bar_extra = 0;
1230 }
1231
1232 void
1233 bar_class_name(char *s, ssize_t sz, struct ws_win *cur_focus)
1234 {
1235 int do_class, do_name;
1236 Status status;
1237 XClassHint *xch = NULL;
1238
1239 if ((title_name_enabled == 1 || title_class_enabled == 1) &&
1240 cur_focus != NULL) {
1241 if ((xch = XAllocClassHint()) == NULL)
1242 goto out;
1243 status = XGetClassHint(display, cur_focus->id, xch);
1244 if (status == BadWindow || status == BadAlloc)
1245 goto out;
1246 do_class = (title_class_enabled && xch->res_class != NULL);
1247 do_name = (title_name_enabled && xch->res_name != NULL);
1248 if (do_class)
1249 strlcat(s, xch->res_class, sz);
1250 if (do_class && do_name)
1251 strlcat(s, ":", sz);
1252 if (do_name)
1253 strlcat(s, xch->res_name, sz);
1254 strlcat(s, " ", sz);
1255 }
1256 out:
1257 if (xch)
1258 XFree(xch);
1259 }
1260
1261 void
1262 bar_window_name(char *s, ssize_t sz, struct ws_win *cur_focus)
1263 {
1264 char *title;
1265
1266 if (window_name_enabled && cur_focus != NULL) {
1267 XFetchName(display, cur_focus->id, &title);
1268 if (title) {
1269 if (cur_focus->floating)
1270 strlcat(s, "(f) ", sz);
1271 strlcat(s, title, sz);
1272 strlcat(s, " ", sz);
1273 XFree(title);
1274 }
1275 }
1276 }
1277
1278 int urgent[SWM_WS_MAX];
1279 void
1280 bar_urgent(char *s, ssize_t sz)
1281 {
1282 XWMHints *wmh = NULL;
1283 struct ws_win *win;
1284 int i, j;
1285 char b[8];
1286
1287 if (urgent_enabled == 0)
1288 return;
1289
1290 for (i = 0; i < SWM_WS_MAX; i++)
1291 urgent[i] = 0;
1292
1293 for (i = 0; i < ScreenCount(display); i++)
1294 for (j = 0; j < SWM_WS_MAX; j++)
1295 TAILQ_FOREACH(win, &screens[i].ws[j].winlist, entry) {
1296 wmh = XGetWMHints(display, win->id);
1297 if (wmh == NULL)
1298 continue;
1299
1300 if (wmh->flags & XUrgencyHint)
1301 urgent[j] = 1;
1302 XFree(wmh);
1303 }
1304
1305 strlcat(s, "* ", sz);
1306 for (i = 0; i < SWM_WS_MAX; i++) {
1307 if (urgent[i])
1308 snprintf(b, sizeof b, "%d ", i + 1);
1309 else
1310 snprintf(b, sizeof b, "- ");
1311 strlcat(s, b, sz);
1312 }
1313 strlcat(s, "* ", sz);
1314 }
1315
1316 void
1317 bar_update(void)
1318 {
1319 time_t tmt;
1320 struct tm tm;
1321 struct swm_region *r;
1322 int i, x;
1323 size_t len;
1324 char s[SWM_BAR_MAX];
1325 char cn[SWM_BAR_MAX];
1326 char loc[SWM_BAR_MAX];
1327 char *b;
1328
1329 if (bar_enabled == 0)
1330 return;
1331 if (bar_extra && bar_extra_running) {
1332 /* ignore short reads; it'll correct itself */
1333 while ((b = fgetln(stdin, &len)) != NULL)
1334 if (b && b[len - 1] == '\n') {
1335 b[len - 1] = '\0';
1336 strlcpy(bar_ext, b, sizeof bar_ext);
1337 }
1338 if (b == NULL && errno != EAGAIN) {
1339 fprintf(stderr, "bar_extra failed: errno: %d %s\n",
1340 errno, strerror(errno));
1341 bar_extra_stop();
1342 }
1343 } else
1344 strlcpy(bar_ext, "", sizeof bar_ext);
1345
1346 if (clock_enabled == 0)
1347 strlcpy(s, "", sizeof s);
1348 else {
1349 time(&tmt);
1350 localtime_r(&tmt, &tm);
1351 strftime(s, sizeof s, clock_format, &tm);
1352 strlcat(s, " ", sizeof s);
1353 }
1354
1355 for (i = 0; i < ScreenCount(display); i++) {
1356 x = 1;
1357 TAILQ_FOREACH(r, &screens[i].rl, entry) {
1358 strlcpy(cn, "", sizeof cn);
1359 if (r && r->ws) {
1360 bar_urgent(cn, sizeof cn);
1361 bar_class_name(cn, sizeof cn, r->ws->focus);
1362 bar_window_name(cn, sizeof cn, r->ws->focus);
1363 }
1364
1365 snprintf(loc, sizeof loc, "%d:%d %s %s%s %s %s",
1366 x++, r->ws->idx + 1, r->ws->stacker, s, cn, bar_ext,
1367 bar_vertext);
1368 bar_print(r, loc);
1369 }
1370 }
1371 alarm(bar_delay);
1372 }
1373
1374 void
1375 bar_signal(int sig)
1376 {
1377 bar_alarm = 1;
1378 }
1379
1380 void
1381 bar_toggle(struct swm_region *r, union arg *args)
1382 {
1383 struct swm_region *tmpr;
1384 int i, sc = ScreenCount(display);
1385
1386 DNPRINTF(SWM_D_MISC, "bar_toggle\n");
1387
1388 if (bar_enabled)
1389 for (i = 0; i < sc; i++)
1390 TAILQ_FOREACH(tmpr, &screens[i].rl, entry)
1391 XUnmapWindow(display, tmpr->bar_window);
1392 else
1393 for (i = 0; i < sc; i++)
1394 TAILQ_FOREACH(tmpr, &screens[i].rl, entry)
1395 XMapRaised(display, tmpr->bar_window);
1396
1397 bar_enabled = !bar_enabled;
1398
1399 stack();
1400 /* must be after stack */
1401 bar_update();
1402 }
1403
1404 void
1405 bar_refresh(void)
1406 {
1407 XSetWindowAttributes wa;
1408 struct swm_region *r;
1409 int i;
1410
1411 /* do this here because the conf file is in memory */
1412 if (bar_extra && bar_extra_running == 0 && bar_argv[0]) {
1413 /* launch external status app */
1414 bar_extra_running = 1;
1415 if (pipe(bar_pipe) == -1)
1416 err(1, "pipe error");
1417 socket_setnonblock(bar_pipe[0]);
1418 socket_setnonblock(bar_pipe[1]); /* XXX hmmm, really? */
1419 if (dup2(bar_pipe[0], 0) == -1)
1420 errx(1, "dup2");
1421 if (dup2(bar_pipe[1], 1) == -1)
1422 errx(1, "dup2");
1423 if (signal(SIGPIPE, SIG_IGN) == SIG_ERR)
1424 err(1, "could not disable SIGPIPE");
1425 switch (bar_pid = fork()) {
1426 case -1:
1427 err(1, "cannot fork");
1428 break;
1429 case 0: /* child */
1430 close(bar_pipe[0]);
1431 execvp(bar_argv[0], bar_argv);
1432 err(1, "%s external app failed", bar_argv[0]);
1433 break;
1434 default: /* parent */
1435 close(bar_pipe[1]);
1436 break;
1437 }
1438 }
1439
1440 bzero(&wa, sizeof wa);
1441 for (i = 0; i < ScreenCount(display); i++)
1442 TAILQ_FOREACH(r, &screens[i].rl, entry) {
1443 wa.border_pixel =
1444 screens[i].c[SWM_S_COLOR_BAR_BORDER].color;
1445 wa.background_pixel =
1446 screens[i].c[SWM_S_COLOR_BAR].color;
1447 XChangeWindowAttributes(display, r->bar_window,
1448 CWBackPixel | CWBorderPixel, &wa);
1449 }
1450 bar_update();
1451 }
1452
1453 void
1454 bar_setup(struct swm_region *r)
1455 {
1456 int i, x, y;
1457
1458 if (bar_fs) {
1459 XFreeFont(display, bar_fs);
1460 bar_fs = NULL;
1461 }
1462
1463 for (i = 0; bar_fonts[i] != NULL; i++) {
1464 bar_fs = XLoadQueryFont(display, bar_fonts[i]);
1465 if (bar_fs) {
1466 bar_fidx = i;
1467 break;
1468 }
1469 }
1470 if (bar_fonts[i] == NULL)
1471 errx(1, "couldn't load font");
1472 if (bar_fs == NULL)
1473 errx(1, "couldn't create font structure");
1474
1475 bar_height = bar_fs->ascent + bar_fs->descent + 1 +
1476 2 * bar_border_width;
1477 x = X(r);
1478 y = bar_at_bottom ? (Y(r) + HEIGHT(r) - bar_height) : Y(r);
1479
1480 r->bar_window = XCreateSimpleWindow(display,
1481 r->s->root, x, y, WIDTH(r) - 2 * bar_border_width,
1482 bar_height - 2 * bar_border_width,
1483 bar_border_width, r->s->c[SWM_S_COLOR_BAR_BORDER].color,
1484 r->s->c[SWM_S_COLOR_BAR].color);
1485 bar_gc = XCreateGC(display, r->bar_window, 0, &bar_gcv);
1486 XSetFont(display, bar_gc, bar_fs->fid);
1487 XSelectInput(display, r->bar_window, VisibilityChangeMask);
1488 if (bar_enabled)
1489 XMapRaised(display, r->bar_window);
1490 DNPRINTF(SWM_D_MISC, "bar_setup: bar_window %lu\n", r->bar_window);
1491
1492 if (signal(SIGALRM, bar_signal) == SIG_ERR)
1493 err(1, "could not install bar_signal");
1494 bar_refresh();
1495 }
1496
1497 void
1498 drain_enter_notify(void)
1499 {
1500 int i = 0;
1501 XEvent cne;
1502
1503 while (XCheckMaskEvent(display, EnterWindowMask, &cne))
1504 i++;
1505
1506 DNPRINTF(SWM_D_MISC, "drain_enter_notify: drained %d\n", i);
1507 }
1508
1509 void
1510 set_win_state(struct ws_win *win, long state)
1511 {
1512 long data[] = {state, None};
1513
1514 DNPRINTF(SWM_D_EVENT, "set_win_state: window: %lu\n", win->id);
1515
1516 if (win == NULL)
1517 return;
1518
1519 XChangeProperty(display, win->id, astate, astate, 32, PropModeReplace,
1520 (unsigned char *)data, 2);
1521 }
1522
1523 long
1524 getstate(Window w)
1525 {
1526 long result = -1;
1527 unsigned char *p = NULL;
1528 unsigned long n;
1529
1530 if (!get_property(w, astate, 2L, astate, &n, &p))
1531 return (-1);
1532 if (n != 0)
1533 result = *((long *)p);
1534 XFree(p);
1535 return (result);
1536 }
1537
1538 void
1539 version(struct swm_region *r, union arg *args)
1540 {
1541 bar_version = !bar_version;
1542 if (bar_version)
1543 snprintf(bar_vertext, sizeof bar_vertext, "Version: %s CVS: %s",
1544 SWM_VERSION, cvstag);
1545 else
1546 strlcpy(bar_vertext, "", sizeof bar_vertext);
1547 bar_update();
1548 }
1549
1550 void
1551 client_msg(struct ws_win *win, Atom a)
1552 {
1553 XClientMessageEvent cm;
1554
1555 if (win == NULL)
1556 return;
1557
1558 bzero(&cm, sizeof cm);
1559 cm.type = ClientMessage;
1560 cm.window = win->id;
1561 cm.message_type = aprot;
1562 cm.format = 32;
1563 cm.data.l[0] = a;
1564 cm.data.l[1] = CurrentTime;
1565 XSendEvent(display, win->id, False, 0L, (XEvent *)&cm);
1566 }
1567
1568 void
1569 config_win(struct ws_win *win, XConfigureRequestEvent *ev)
1570 {
1571 XConfigureEvent ce;
1572
1573 if (win == NULL)
1574 return;
1575
1576 if (ev == NULL) {
1577 DNPRINTF(SWM_D_MISC,
1578 "config_win: win %lu x %d y %d w %d h %d\n",
1579 win->id, win->g.x, win->g.y, win->g.w, win->g.h);
1580
1581 ce.type = ConfigureNotify;
1582 ce.display = display;
1583 ce.event = win->id;
1584 ce.window = win->id;
1585 ce.x = win->g.x;
1586 ce.y = win->g.y;
1587 ce.width = win->g.w;
1588 ce.height = win->g.h;
1589 ce.border_width = border_width;
1590 ce.above = None;
1591 ce.override_redirect = False;
1592 } else {
1593 DNPRINTF(SWM_D_MISC,
1594 "config_win: ev win %lu x %d y %d w %d h %d\n",
1595 ev->window, ev->x, ev->y, ev->width, ev->height);
1596 ce.type = ConfigureNotify;
1597 ce.display = ev->display;
1598 ce.event = ev->window;
1599 ce.window = ev->window;
1600 ce.x = ev->x;
1601 ce.y = ev->y;
1602 ce.width = ev->width;
1603 ce.height = ev->height;
1604 ce.border_width = ev->border_width;
1605 ce.above = ev->above;
1606 ce.override_redirect = False;
1607 }
1608
1609 XSendEvent(display, win->id, False, StructureNotifyMask, (XEvent *)&ce);
1610 }
1611
1612 int
1613 count_win(struct workspace *ws, int count_transient)
1614 {
1615 struct ws_win *win;
1616 int count = 0;
1617
1618 TAILQ_FOREACH(win, &ws->winlist, entry) {
1619 if (count_transient == 0 && win->floating)
1620 continue;
1621 if (count_transient == 0 && win->transient)
1622 continue;
1623 if (win->iconic)
1624 continue;
1625 count++;
1626 }
1627 DNPRINTF(SWM_D_MISC, "count_win: %d\n", count);
1628
1629 return (count);
1630 }
1631
1632 void
1633 quit(struct swm_region *r, union arg *args)
1634 {
1635 DNPRINTF(SWM_D_MISC, "quit\n");
1636 running = 0;
1637 }
1638
1639 void
1640 unmap_window(struct ws_win *win)
1641 {
1642 if (win == NULL)
1643 return;
1644
1645 /* don't unmap again */
1646 if (getstate(win->id) == IconicState)
1647 return;
1648
1649 set_win_state(win, IconicState);
1650
1651 XUnmapWindow(display, win->id);
1652 XSetWindowBorder(display, win->id,
1653 win->s->c[SWM_S_COLOR_UNFOCUS].color);
1654 }
1655
1656 void
1657 unmap_all(void)
1658 {
1659 struct ws_win *win;
1660 int i, j;
1661
1662 for (i = 0; i < ScreenCount(display); i++)
1663 for (j = 0; j < SWM_WS_MAX; j++)
1664 TAILQ_FOREACH(win, &screens[i].ws[j].winlist, entry)
1665 unmap_window(win);
1666 }
1667
1668 void
1669 fake_keypress(struct ws_win *win, int keysym, int modifiers)
1670 {
1671 XKeyEvent event;
1672
1673 if (win == NULL)
1674 return;
1675
1676 event.display = display; /* Ignored, but what the hell */
1677 event.window = win->id;
1678 event.root = win->s->root;
1679 event.subwindow = None;
1680 event.time = CurrentTime;
1681 event.x = win->g.x;
1682 event.y = win->g.y;
1683 event.x_root = 1;
1684 event.y_root = 1;
1685 event.same_screen = True;
1686 event.keycode = XKeysymToKeycode(display, keysym);
1687 event.state = modifiers;
1688
1689 event.type = KeyPress;
1690 XSendEvent(event.display, event.window, True,
1691 KeyPressMask, (XEvent *)&event);
1692
1693 event.type = KeyRelease;
1694 XSendEvent(event.display, event.window, True,
1695 KeyPressMask, (XEvent *)&event);
1696
1697 }
1698
1699 void
1700 restart(struct swm_region *r, union arg *args)
1701 {
1702 DNPRINTF(SWM_D_MISC, "restart: %s\n", start_argv[0]);
1703
1704 /* disable alarm because the following code may not be interrupted */
1705 alarm(0);
1706 if (signal(SIGALRM, SIG_IGN) == SIG_ERR)
1707 errx(1, "can't disable alarm");
1708
1709 bar_extra_stop();
1710 bar_extra = 1;
1711 unmap_all();
1712 XCloseDisplay(display);
1713 execvp(start_argv[0], start_argv);
1714 fprintf(stderr, "execvp failed\n");
1715 perror(" failed");
1716 quit(NULL, NULL);
1717 }
1718
1719 struct swm_region *
1720 root_to_region(Window root)
1721 {
1722 struct swm_region *r = NULL;
1723 Window rr, cr;
1724 int i, x, y, wx, wy;
1725 unsigned int mask;
1726
1727 for (i = 0; i < ScreenCount(display); i++)
1728 if (screens[i].root == root)
1729 break;
1730
1731 if (XQueryPointer(display, screens[i].root,
1732 &rr, &cr, &x, &y, &wx, &wy, &mask) != False) {
1733 /* choose a region based on pointer location */
1734 TAILQ_FOREACH(r, &screens[i].rl, entry)
1735 if (x >= X(r) && x <= X(r) + WIDTH(r) &&
1736 y >= Y(r) && y <= Y(r) + HEIGHT(r))
1737 break;
1738 }
1739
1740 if (r == NULL)
1741 r = TAILQ_FIRST(&screens[i].rl);
1742
1743 return (r);
1744 }
1745
1746 struct ws_win *
1747 find_unmanaged_window(Window id)
1748 {
1749 struct ws_win *win;
1750 int i, j;
1751
1752 for (i = 0; i < ScreenCount(display); i++)
1753 for (j = 0; j < SWM_WS_MAX; j++)
1754 TAILQ_FOREACH(win, &screens[i].ws[j].unmanagedlist,
1755 entry)
1756 if (id == win->id)
1757 return (win);
1758 return (NULL);
1759 }
1760
1761 struct ws_win *
1762 find_window(Window id)
1763 {
1764 struct ws_win *win;
1765 Window wrr, wpr, *wcr = NULL;
1766 int i, j;
1767 unsigned int nc;
1768
1769 for (i = 0; i < ScreenCount(display); i++)
1770 for (j = 0; j < SWM_WS_MAX; j++)
1771 TAILQ_FOREACH(win, &screens[i].ws[j].winlist, entry)
1772 if (id == win->id)
1773 return (win);
1774
1775 /* if we were looking for the parent return that window instead */
1776 if (XQueryTree(display, id, &wrr, &wpr, &wcr, &nc) == 0)
1777 return (NULL);
1778 if (wcr)
1779 XFree(wcr);
1780
1781 /* ignore not found and root */
1782 if (wpr == 0 || wrr == wpr)
1783 return (NULL);
1784
1785 /* look for parent */
1786 for (i = 0; i < ScreenCount(display); i++)
1787 for (j = 0; j < SWM_WS_MAX; j++)
1788 TAILQ_FOREACH(win, &screens[i].ws[j].winlist, entry)
1789 if (wpr == win->id)
1790 return (win);
1791
1792 return (NULL);
1793 }
1794
1795 void
1796 spawn(int ws_idx, union arg *args, int close_fd)
1797 {
1798 int fd;
1799 char *ret = NULL;
1800
1801 DNPRINTF(SWM_D_MISC, "spawn: %s\n", args->argv[0]);
1802
1803 if (display)
1804 close(ConnectionNumber(display));
1805
1806 setenv("LD_PRELOAD", SWM_LIB, 1);
1807
1808 if (asprintf(&ret, "%d", ws_idx) == -1) {
1809 perror("_SWM_WS");
1810 _exit(1);
1811 }
1812 setenv("_SWM_WS", ret, 1);
1813 free(ret);
1814 ret = NULL;
1815
1816 if (asprintf(&ret, "%d", getpid()) == -1) {
1817 perror("_SWM_PID");
1818 _exit(1);
1819 }
1820 setenv("_SWM_PID", ret, 1);
1821 free(ret);
1822 ret = NULL;
1823
1824 if (setsid() == -1) {
1825 perror("setsid");
1826 _exit(1);
1827 }
1828
1829 if (close_fd) {
1830 /*
1831 * close stdin and stdout to prevent interaction between apps
1832 * and the baraction script
1833 * leave stderr open to record errors
1834 */
1835 if ((fd = open(_PATH_DEVNULL, O_RDWR, 0)) == -1) {
1836 perror("open");
1837 _exit(1);
1838 }
1839 dup2(fd, STDIN_FILENO);
1840 dup2(fd, STDOUT_FILENO);
1841 if (fd > 2)
1842 close(fd);
1843 }
1844
1845 execvp(args->argv[0], args->argv);
1846
1847 perror("execvp");
1848 _exit(1);
1849 }
1850
1851 void
1852 spawnterm(struct swm_region *r, union arg *args)
1853 {
1854 DNPRINTF(SWM_D_MISC, "spawnterm\n");
1855
1856 if (fork() == 0) {
1857 if (term_width)
1858 setenv("_SWM_XTERM_FONTADJ", "", 1);
1859 spawn(r->ws->idx, args, 1);
1860 }
1861 }
1862
1863 void
1864 kill_refs(struct ws_win *win)
1865 {
1866 int i, x;
1867 struct swm_region *r;
1868 struct workspace *ws;
1869
1870 if (win == NULL)
1871 return;
1872
1873 for (i = 0; i < ScreenCount(display); i++)
1874 TAILQ_FOREACH(r, &screens[i].rl, entry)
1875 for (x = 0; x < SWM_WS_MAX; x++) {
1876 ws = &r->s->ws[x];
1877 if (win == ws->focus)
1878 ws->focus = NULL;
1879 if (win == ws->focus_prev)
1880 ws->focus_prev = NULL;
1881 }
1882 }
1883
1884 int
1885 validate_win(struct ws_win *testwin)
1886 {
1887 struct ws_win *win;
1888 struct workspace *ws;
1889 struct swm_region *r;
1890 int i, x, foundit = 0;
1891
1892 if (testwin == NULL)
1893 return (0);
1894
1895 for (i = 0, foundit = 0; i < ScreenCount(display); i++)
1896 TAILQ_FOREACH(r, &screens[i].rl, entry)
1897 for (x = 0; x < SWM_WS_MAX; x++) {
1898 ws = &r->s->ws[x];
1899 TAILQ_FOREACH(win, &ws->winlist, entry)
1900 if (win == testwin)
1901 return (0);
1902 }
1903 return (1);
1904 }
1905
1906 int
1907 validate_ws(struct workspace *testws)
1908 {
1909 struct swm_region *r;
1910 struct workspace *ws;
1911 int foundit, i, x;
1912
1913 /* validate all ws */
1914 for (i = 0, foundit = 0; i < ScreenCount(display); i++)
1915 TAILQ_FOREACH(r, &screens[i].rl, entry)
1916 for (x = 0; x < SWM_WS_MAX; x++) {
1917 ws = &r->s->ws[x];
1918 if (ws == testws)
1919 return (0);
1920 }
1921 return (1);
1922 }
1923
1924 void
1925 unfocus_win(struct ws_win *win)
1926 {
1927 XEvent cne;
1928 Window none = None;
1929
1930 DNPRINTF(SWM_D_FOCUS, "unfocus_win: id: %lu\n", WINID(win));
1931
1932 if (win == NULL)
1933 return;
1934 if (win->ws == NULL)
1935 return;
1936
1937 if (validate_ws(win->ws))
1938 return; /* XXX this gets hit with thunderbird, needs fixing */
1939
1940 if (win->ws->r == NULL)
1941 return;
1942
1943 if (validate_win(win)) {
1944 kill_refs(win);
1945 return;
1946 }
1947
1948 if (win->ws->focus == win) {
1949 win->ws->focus = NULL;
1950 win->ws->focus_prev = win;
1951 }
1952
1953 if (validate_win(win->ws->focus)) {
1954 kill_refs(win->ws->focus);
1955 win->ws->focus = NULL;
1956 }
1957 if (validate_win(win->ws->focus_prev)) {
1958 kill_refs(win->ws->focus_prev);
1959 win->ws->focus_prev = NULL;
1960 }
1961
1962 /* drain all previous unfocus events */
1963 while (XCheckTypedEvent(display, FocusOut, &cne) == True)
1964 ;
1965
1966 grabbuttons(win, 0);
1967 XSetWindowBorder(display, win->id,
1968 win->ws->r->s->c[SWM_S_COLOR_UNFOCUS].color);
1969
1970 XChangeProperty(display, win->s->root,
1971 ewmh[_NET_ACTIVE_WINDOW].atom, XA_WINDOW, 32,
1972 PropModeReplace, (unsigned char *)&none,1);
1973 }
1974
1975 void
1976 unfocus_all(void)
1977 {
1978 struct ws_win *win;
1979 int i, j;
1980
1981 DNPRINTF(SWM_D_FOCUS, "unfocus_all\n");
1982
1983 for (i = 0; i < ScreenCount(display); i++)
1984 for (j = 0; j < SWM_WS_MAX; j++)
1985 TAILQ_FOREACH(win, &screens[i].ws[j].winlist, entry)
1986 unfocus_win(win);
1987 }
1988
1989 void
1990 focus_win(struct ws_win *win)
1991 {
1992 XEvent cne;
1993 Window cur_focus;
1994 int rr;
1995 struct ws_win *cfw = NULL;
1996
1997
1998 DNPRINTF(SWM_D_FOCUS, "focus_win: id: %lu\n", win ? win->id : 0);
1999
2000 if (win == NULL)
2001 return;
2002 if (win->ws == NULL)
2003 return;
2004
2005 if (validate_ws(win->ws))
2006 return; /* XXX this gets hit with thunderbird, needs fixing */
2007
2008 if (validate_win(win)) {
2009 kill_refs(win);
2010 return;
2011 }
2012
2013 if (validate_win(win)) {
2014 kill_refs(win);
2015 return;
2016 }
2017
2018 XGetInputFocus(display, &cur_focus, &rr);
2019 if ((cfw = find_window(cur_focus)) != NULL)
2020 unfocus_win(cfw);
2021 else {
2022 /* use larger hammer since the window was killed somehow */
2023 TAILQ_FOREACH(cfw, &win->ws->winlist, entry)
2024 if (cfw->ws && cfw->ws->r && cfw->ws->r->s)
2025 XSetWindowBorder(display, cfw->id,
2026 cfw->ws->r->s->c[SWM_S_COLOR_UNFOCUS].color);
2027 }
2028
2029 win->ws->focus = win;
2030
2031 if (win->ws->r != NULL) {
2032 /* drain all previous focus events */
2033 while (XCheckTypedEvent(display, FocusIn, &cne) == True)
2034 ;
2035
2036 if (win->java == 0)
2037 XSetInputFocus(display, win->id,
2038 RevertToParent, CurrentTime);
2039 grabbuttons(win, 1);
2040 XSetWindowBorder(display, win->id,
2041 win->ws->r->s->c[SWM_S_COLOR_FOCUS].color);
2042 if (win->ws->cur_layout->flags & SWM_L_MAPONFOCUS ||
2043 win->ws->always_raise)
2044 XMapRaised(display, win->id);
2045
2046 XChangeProperty(display, win->s->root,
2047 ewmh[_NET_ACTIVE_WINDOW].atom, XA_WINDOW, 32,
2048 PropModeReplace, (unsigned char *)&win->id,1);
2049 }
2050
2051 if (window_name_enabled)
2052 bar_update();
2053 }
2054
2055 void
2056 switchws(struct swm_region *r, union arg *args)
2057 {
2058 int wsid = args->id, unmap_old = 0;
2059 struct swm_region *this_r, *other_r;
2060 struct ws_win *win;
2061 struct workspace *new_ws, *old_ws;
2062 union arg a;
2063
2064 if (!(r && r->s))
2065 return;
2066
2067 this_r = r;
2068 old_ws = this_r->ws;
2069 new_ws = &this_r->s->ws[wsid];
2070
2071 DNPRINTF(SWM_D_WS, "switchws screen[%d]:%dx%d+%d+%d: "
2072 "%d -> %d\n", r->s->idx, WIDTH(r), HEIGHT(r), X(r), Y(r),
2073 old_ws->idx, wsid);
2074
2075 if (new_ws == NULL || old_ws == NULL)
2076 return;
2077 if (new_ws == old_ws)
2078 return;
2079
2080 other_r = new_ws->r;
2081 if (other_r == NULL) {
2082 /* the other workspace is hidden, hide this one */
2083 old_ws->r = NULL;
2084 unmap_old = 1;
2085 } else {
2086 /* the other ws is visible in another region, exchange them */
2087 other_r->ws_prior = new_ws;
2088 other_r->ws = old_ws;
2089 old_ws->r = other_r;
2090 }
2091 this_r->ws_prior = old_ws;
2092 this_r->ws = new_ws;
2093 new_ws->r = this_r;
2094
2095 /* this is needed so that we can click on a window after a restart */
2096 unfocus_all();
2097
2098 stack();
2099 a.id = SWM_ARG_ID_FOCUSCUR;
2100 focus(new_ws->r, &a);
2101
2102 bar_update();
2103
2104 /* unmap old windows */
2105 if (unmap_old)
2106 TAILQ_FOREACH(win, &old_ws->winlist, entry)
2107 unmap_window(win);
2108
2109 if (focus_mode == SWM_FOCUS_DEFAULT)
2110 drain_enter_notify();
2111 }
2112
2113 void
2114 cyclews(struct swm_region *r, union arg *args)
2115 {
2116 union arg a;
2117 struct swm_screen *s = r->s;
2118
2119 DNPRINTF(SWM_D_WS, "cyclews id %d "
2120 "in screen[%d]:%dx%d+%d+%d ws %d\n", args->id,
2121 r->s->idx, WIDTH(r), HEIGHT(r), X(r), Y(r), r->ws->idx);
2122
2123 a.id = r->ws->idx;
2124 do {
2125 switch (args->id) {
2126 case SWM_ARG_ID_CYCLEWS_UP:
2127 if (a.id < SWM_WS_MAX - 1)
2128 a.id++;
2129 else
2130 a.id = 0;
2131 break;
2132 case SWM_ARG_ID_CYCLEWS_DOWN:
2133 if (a.id > 0)
2134 a.id--;
2135 else
2136 a.id = SWM_WS_MAX - 1;
2137 break;
2138 default:
2139 return;
2140 };
2141
2142 if (cycle_empty == 0 && TAILQ_EMPTY(&s->ws[a.id].winlist))
2143 continue;
2144 if (cycle_visible == 0 && s->ws[a.id].r != NULL)
2145 continue;
2146
2147 switchws(r, &a);
2148 } while (a.id != r->ws->idx);
2149 }
2150
2151 void
2152 priorws(struct swm_region *r, union arg *args)
2153 {
2154 union arg a;
2155
2156 DNPRINTF(SWM_D_WS, "priorws id %d "
2157 "in screen[%d]:%dx%d+%d+%d ws %d\n", args->id,
2158 r->s->idx, WIDTH(r), HEIGHT(r), X(r), Y(r), r->ws->idx);
2159
2160 if (r->ws_prior == NULL)
2161 return;
2162
2163 a.id = r->ws_prior->idx;
2164 switchws(r, &a);
2165 }
2166
2167 void
2168 cyclescr(struct swm_region *r, union arg *args)
2169 {
2170 struct swm_region *rr = NULL;
2171 union arg a;
2172 int i, x, y;
2173
2174 /* do nothing if we don't have more than one screen */
2175 if (!(ScreenCount(display) > 1 || outputs > 1))
2176 return;
2177
2178 i = r->s->idx;
2179 switch (args->id) {
2180 case SWM_ARG_ID_CYCLESC_UP:
2181 rr = TAILQ_NEXT(r, entry);
2182 if (rr == NULL)
2183 rr = TAILQ_FIRST(&screens[i].rl);
2184 break;
2185 case SWM_ARG_ID_CYCLESC_DOWN:
2186 rr = TAILQ_PREV(r, swm_region_list, entry);
2187 if (rr == NULL)
2188 rr = TAILQ_LAST(&screens[i].rl, swm_region_list);
2189 break;
2190 default:
2191 return;
2192 };
2193 if (rr == NULL)
2194 return;
2195
2196 /* move mouse to region */
2197 x = rr->g.x + 1;
2198 y = rr->g.y + 1 + (bar_enabled ? bar_height : 0);
2199 XWarpPointer(display, None, rr->s[i].root, 0, 0, 0, 0, x, y);
2200
2201 a.id = SWM_ARG_ID_FOCUSCUR;
2202 focus(rr, &a);
2203
2204 if (rr->ws->focus) {
2205 /* move to focus window */
2206 x = rr->ws->focus->g.x + 1;
2207 y = rr->ws->focus->g.y + 1;
2208 XWarpPointer(display, None, rr->s[i].root, 0, 0, 0, 0, x, y);
2209 }
2210 }
2211
2212 void
2213 sort_windows(struct ws_win_list *wl)
2214 {
2215 struct ws_win *win, *parent, *nxt;
2216
2217 if (wl == NULL)
2218 return;
2219
2220 for (win = TAILQ_FIRST(wl); win != TAILQ_END(wl); win = nxt) {
2221 nxt = TAILQ_NEXT(win, entry);
2222 if (win->transient) {
2223 parent = find_window(win->transient);
2224 if (parent == NULL) {
2225 fprintf(stderr, "not possible bug\n");
2226 continue;
2227 }
2228 TAILQ_REMOVE(wl, win, entry);
2229 TAILQ_INSERT_AFTER(wl, parent, win, entry);
2230 }
2231 }
2232
2233 }
2234
2235 void
2236 swapwin(struct swm_region *r, union arg *args)
2237 {
2238 struct ws_win *target, *source;
2239 struct ws_win *cur_focus;
2240 struct ws_win_list *wl;
2241
2242
2243 DNPRINTF(SWM_D_WS, "swapwin id %d "
2244 "in screen %d region %dx%d+%d+%d ws %d\n", args->id,
2245 r->s->idx, WIDTH(r), HEIGHT(r), X(r), Y(r), r->ws->idx);
2246
2247 cur_focus = r->ws->focus;
2248 if (cur_focus == NULL)
2249 return;
2250
2251 source = cur_focus;
2252 wl = &source->ws->winlist;
2253
2254 switch (args->id) {
2255 case SWM_ARG_ID_SWAPPREV:
2256 if (source->transient)
2257 source = find_window(source->transient);
2258 target = TAILQ_PREV(source, ws_win_list, entry);
2259 if (target && target->transient)
2260 target = find_window(target->transient);
2261 TAILQ_REMOVE(wl, source, entry);
2262 if (target == NULL)
2263 TAILQ_INSERT_TAIL(wl, source, entry);
2264 else
2265 TAILQ_INSERT_BEFORE(target, source, entry);
2266 break;
2267 case SWM_ARG_ID_SWAPNEXT:
2268 target = TAILQ_NEXT(source, entry);
2269 /* move the parent and let the sort handle the move */
2270 if (source->transient)
2271 source = find_window(source->transient);
2272 TAILQ_REMOVE(wl, source, entry);
2273 if (target == NULL)
2274 TAILQ_INSERT_HEAD(wl, source, entry);
2275 else
2276 TAILQ_INSERT_AFTER(wl, target, source, entry);
2277 break;
2278 case SWM_ARG_ID_SWAPMAIN:
2279 target = TAILQ_FIRST(wl);
2280 if (target == source) {
2281 if (source->ws->focus_prev != NULL &&
2282 source->ws->focus_prev != target)
2283
2284 source = source->ws->focus_prev;
2285 else
2286 return;
2287 }
2288 if (target == NULL || source == NULL)
2289 return;
2290 source->ws->focus_prev = target;
2291 TAILQ_REMOVE(wl, target, entry);
2292 TAILQ_INSERT_BEFORE(source, target, entry);
2293 TAILQ_REMOVE(wl, source, entry);
2294 TAILQ_INSERT_HEAD(wl, source, entry);
2295 break;
2296 case SWM_ARG_ID_MOVELAST:
2297 TAILQ_REMOVE(wl, source, entry);
2298 TAILQ_INSERT_TAIL(wl, source, entry);
2299 break;
2300 default:
2301 DNPRINTF(SWM_D_MOVE, "invalid id: %d\n", args->id);
2302 return;
2303 }
2304
2305 sort_windows(wl);
2306
2307 stack();
2308 }
2309
2310 void
2311 focus_prev(struct ws_win *win)
2312 {
2313 struct ws_win *winfocus = NULL, *winlostfocus = NULL;
2314 struct ws_win *cur_focus = NULL;
2315 struct ws_win_list *wl = NULL;
2316 struct workspace *ws = NULL;
2317
2318 DNPRINTF(SWM_D_FOCUS, "focus_prev: id %lu\n", WINID(win));
2319
2320 if (!(win && win->ws))
2321 return;
2322
2323 ws = win->ws;
2324 wl = &ws->winlist;
2325 cur_focus = ws->focus;
2326 winlostfocus = cur_focus;
2327
2328 /* pickle, just focus on whatever */
2329 if (cur_focus == NULL) {
2330 /* use prev_focus if valid */
2331 if (ws->focus_prev && ws->focus_prev != cur_focus &&
2332 find_window(WINID(ws->focus_prev)))
2333 winfocus = ws->focus_prev;
2334 if (winfocus == NULL)
2335 winfocus = TAILQ_FIRST(wl);
2336 goto done;
2337 }
2338
2339 /* if transient focus on parent */
2340 if (cur_focus->transient) {
2341 winfocus = find_window(cur_focus->transient);
2342 goto done;
2343 }
2344
2345 /* if in max_stack try harder */
2346 if ((win->quirks & SWM_Q_FOCUSPREV) ||
2347 (ws->cur_layout->flags & SWM_L_FOCUSPREV)) {
2348 if (cur_focus != ws->focus_prev)
2349 winfocus = ws->focus_prev;
2350 else if (cur_focus != ws->focus)
2351 winfocus = ws->focus;
2352 else
2353 winfocus = TAILQ_PREV(win, ws_win_list, entry);
2354 if (winfocus)
2355 goto done;
2356 }
2357
2358 if (cur_focus == win)
2359 winfocus = TAILQ_PREV(win, ws_win_list, entry);
2360 if (winfocus == NULL)
2361 winfocus = TAILQ_LAST(wl, ws_win_list);
2362 if (winfocus == NULL || winfocus == win)
2363 winfocus = TAILQ_NEXT(cur_focus, entry);
2364 done:
2365 if (winfocus == winlostfocus || winfocus == NULL)
2366 return;
2367
2368 focus_magic(winfocus);
2369 }
2370
2371 void
2372 focus(struct swm_region *r, union arg *args)
2373 {
2374 struct ws_win *winfocus = NULL, *winlostfocus = NULL, *head;
2375 struct ws_win *cur_focus = NULL;
2376 struct ws_win_list *wl = NULL;
2377 struct workspace *ws = NULL;
2378
2379 if (!(r && r->ws))
2380 return;
2381
2382 DNPRINTF(SWM_D_FOCUS, "focus: id %d\n", args->id);
2383
2384 /* treat FOCUS_CUR special */
2385 if (args->id == SWM_ARG_ID_FOCUSCUR) {
2386 if (r->ws->focus && r->ws->focus->iconic == 0)
2387 winfocus = r->ws->focus;
2388 else if (r->ws->focus_prev && r->ws->focus_prev->iconic == 0)
2389 winfocus = r->ws->focus_prev;
2390 else
2391 TAILQ_FOREACH(winfocus, &r->ws->winlist, entry)
2392 if (winfocus->iconic == 0)
2393 break;
2394
2395 focus_magic(winfocus);
2396 return;
2397 }
2398
2399 if ((cur_focus = r->ws->focus) == NULL)
2400 return;
2401 ws = r->ws;
2402 wl = &ws->winlist;
2403
2404 winlostfocus = cur_focus;
2405
2406 switch (args->id) {
2407 case SWM_ARG_ID_FOCUSPREV:
2408 head = TAILQ_PREV(cur_focus, ws_win_list, entry);
2409 if (head == NULL)
2410 head = TAILQ_LAST(wl, ws_win_list);
2411 winfocus = head;
2412 if (WINID(winfocus) == cur_focus->transient) {
2413 head = TAILQ_PREV(winfocus, ws_win_list, entry);
2414 if (head == NULL)
2415 head = TAILQ_LAST(wl, ws_win_list);
2416 winfocus = head;
2417 }
2418
2419 /* skip iconics */
2420 if (winfocus && winfocus->iconic) {
2421 TAILQ_FOREACH_REVERSE(winfocus, wl, ws_win_list, entry)
2422 if (winfocus->iconic == 0)
2423 break;
2424 }
2425 break;
2426
2427 case SWM_ARG_ID_FOCUSNEXT:
2428 head = TAILQ_NEXT(cur_focus, entry);
2429 if (head == NULL)
2430 head = TAILQ_FIRST(wl);
2431 winfocus = head;
2432
2433 /* skip iconics */
2434 if (winfocus && winfocus->iconic) {
2435 TAILQ_FOREACH(winfocus, wl, entry)
2436 if (winfocus->iconic == 0)
2437 break;
2438 }
2439 break;
2440
2441 case SWM_ARG_ID_FOCUSMAIN:
2442 winfocus = TAILQ_FIRST(wl);
2443 if (winfocus == cur_focus)
2444 winfocus = cur_focus->ws->focus_prev;
2445 break;
2446
2447 default:
2448 return;
2449 }
2450 if (winfocus == winlostfocus || winfocus == NULL)
2451 return;
2452
2453 focus_magic(winfocus);
2454 }
2455
2456 void
2457 cycle_layout(struct swm_region *r, union arg *args)
2458 {
2459 struct workspace *ws = r->ws;
2460 struct ws_win *winfocus;
2461 union arg a;
2462
2463 DNPRINTF(SWM_D_EVENT, "cycle_layout: workspace: %d\n", ws->idx);
2464
2465 winfocus = ws->focus;
2466
2467 ws->cur_layout++;
2468 if (ws->cur_layout->l_stack == NULL)
2469 ws->cur_layout = &layouts[0];
2470
2471 stack();
2472 if (focus_mode == SWM_FOCUS_DEFAULT)
2473 drain_enter_notify();
2474 a.id = SWM_ARG_ID_FOCUSCUR;
2475 focus(r, &a);
2476 bar_update();
2477 }
2478
2479 void
2480 stack_config(struct swm_region *r, union arg *args)
2481 {
2482 struct workspace *ws = r->ws;
2483
2484 DNPRINTF(SWM_D_STACK, "stack_config for workspace %d (id %d\n",
2485 args->id, ws->idx);
2486
2487 if (ws->cur_layout->l_config != NULL)
2488 ws->cur_layout->l_config(ws, args->id);
2489
2490 if (args->id != SWM_ARG_ID_STACKINIT)
2491 stack();
2492 bar_update();
2493 }
2494
2495 void
2496 stack(void) {
2497 struct swm_geometry g;
2498 struct swm_region *r;
2499 int i, j;
2500
2501 DNPRINTF(SWM_D_STACK, "stack\n");
2502
2503 for (i = 0; i < ScreenCount(display); i++) {
2504 j = 0;
2505 TAILQ_FOREACH(r, &screens[i].rl, entry) {
2506 DNPRINTF(SWM_D_STACK, "stacking workspace %d "
2507 "(screen %d, region %d)\n", r->ws->idx, i, j++);
2508
2509 /* start with screen geometry, adjust for bar */
2510 g = r->g;
2511 g.w -= 2 * border_width;
2512 g.h -= 2 * border_width;
2513 if (bar_enabled) {
2514 if (!bar_at_bottom)
2515 g.y += bar_height;
2516 g.h -= bar_height;
2517 }
2518 r->ws->cur_layout->l_stack(r->ws, &g);
2519 r->ws->cur_layout->l_string(r->ws);
2520 /* save r so we can track region changes */
2521 r->ws->old_r = r;
2522 }
2523 }
2524 if (font_adjusted)
2525 font_adjusted--;
2526
2527 if (focus_mode == SWM_FOCUS_DEFAULT)
2528 drain_enter_notify();
2529 }
2530
2531 void
2532 store_float_geom(struct ws_win *win, struct swm_region *r)
2533 {
2534 /* retain window geom and region geom */
2535 win->g_float.x = win->g.x;
2536 win->g_float.y = win->g.y;
2537 win->g_float.w = win->g.w;
2538 win->g_float.h = win->g.h;
2539 win->rg_float.x = r->g.x;
2540 win->rg_float.y = r->g.y;
2541 win->rg_float.w = r->g.w;
2542 win->rg_float.h = r->g.h;
2543 win->g_floatvalid = 1;
2544 }
2545
2546 void
2547 stack_floater(struct ws_win *win, struct swm_region *r)
2548 {
2549 unsigned int mask;
2550 XWindowChanges wc;
2551
2552 if (win == NULL)
2553 return;
2554
2555 bzero(&wc, sizeof wc);
2556 mask = CWX | CWY | CWBorderWidth | CWWidth | CWHeight;
2557
2558 /*
2559 * to allow windows to change their size (e.g. mplayer fs) only retrieve
2560 * geom on ws switches or return from max mode
2561 */
2562
2563 if (win->floatmaxed || (r != r->ws->old_r && win->g_floatvalid
2564 && !(win->ewmh_flags & EWMH_F_FULLSCREEN))) {
2565 /*
2566 * use stored g and rg to set relative position and size
2567 * as in old region or before max stack mode
2568 */
2569 win->g.x = win->g_float.x - win->rg_float.x + r->g.x;
2570 win->g.y = win->g_float.y - win->rg_float.y + r->g.y;
2571 win->g.w = win->g_float.w;
2572 win->g.h = win->g_float.h;
2573 win->g_floatvalid = 0;
2574 }
2575
2576 win->floatmaxed = 0;
2577
2578 if ((win->quirks & SWM_Q_FULLSCREEN) && (win->g.w >= WIDTH(r)) &&
2579 (win->g.h >= HEIGHT(r)))
2580 wc.border_width = 0;
2581 else
2582 wc.border_width = border_width;
2583 if (win->transient && (win->quirks & SWM_Q_TRANSSZ)) {
2584 win->g.w = (double)WIDTH(r) * dialog_ratio;
2585 win->g.h = (double)HEIGHT(r) * dialog_ratio;
2586 }
2587
2588 if (!win->manual) {
2589 /*
2590 * floaters and transients are auto-centred unless moved
2591 * or resized
2592 */
2593 win->g.x = r->g.x + (WIDTH(r) - win->g.w) /
2594 2 - wc.border_width;
2595 win->g.y = r->g.y + (HEIGHT(r) - win->g.h) /
2596 2 - wc.border_width;
2597 }
2598
2599 /* win can be outside r if new r smaller than old r */
2600 /* Ensure top left corner inside r (move probs otherwise) */
2601 if (win->g.x < r->g.x - wc.border_width)
2602 win->g.x = r->g.x - wc.border_width;
2603 if (win->g.x > r->g.x + r->g.w - 1)
2604 win->g.x = (win->g.w > r->g.w) ? r->g.x :
2605 (r->g.x + r->g.w - win->g.w - 2 * wc.border_width);
2606 if (win->g.y < r->g.y - wc.border_width)
2607 win->g.y = r->g.y - wc.border_width;
2608 if (win->g.y > r->g.y + r->g.h - 1)
2609 win->g.y = (win->g.h > r->g.h) ? r->g.y :
2610 (r->g.y + r->g.h - win->g.h - 2 * wc.border_width);
2611
2612 wc.x = win->g.x;
2613 wc.y = win->g.y;
2614 wc.width = win->g.w;
2615 wc.height = win->g.h;
2616
2617 /*
2618 * Retain floater and transient geometry for correct positioning
2619 * when ws changes region
2620 */
2621 if (!(win->ewmh_flags & EWMH_F_FULLSCREEN))
2622 store_float_geom(win, r);
2623
2624 DNPRINTF(SWM_D_MISC, "stack_floater: win %lu x %d y %d w %d h %d\n",
2625 win->id, wc.x, wc.y, wc.width, wc.height);
2626
2627 XConfigureWindow(display, win->id, mask, &wc);
2628 }
2629
2630 /*
2631 * Send keystrokes to terminal to decrease/increase the font size as the
2632 * window size changes.
2633 */
2634 void
2635 adjust_font(struct ws_win *win)
2636 {
2637 if (!(win->quirks & SWM_Q_XTERM_FONTADJ) ||
2638 win->floating || win->transient)
2639 return;
2640
2641 if (win->sh.width_inc && win->last_inc != win->sh.width_inc &&
2642 win->g.w / win->sh.width_inc < term_width &&
2643 win->font_steps < SWM_MAX_FONT_STEPS) {
2644 win->font_size_boundary[win->font_steps] =
2645 (win->sh.width_inc * term_width) + win->sh.base_width;
2646 win->font_steps++;
2647 font_adjusted++;
2648 win->last_inc = win->sh.width_inc;
2649 fake_keypress(win, XK_KP_Subtract, ShiftMask);
2650 } else if (win->font_steps && win->last_inc != win->sh.width_inc &&
2651 win->g.w > win->font_size_boundary[win->font_steps - 1]) {
2652 win->font_steps--;
2653 font_adjusted++;
2654 win->last_inc = win->sh.width_inc;
2655 fake_keypress(win, XK_KP_Add, ShiftMask);
2656 }
2657 }
2658
2659 #define SWAPXY(g) do { \
2660 int tmp; \
2661 tmp = (g)->y; (g)->y = (g)->x; (g)->x = tmp; \
2662 tmp = (g)->h; (g)->h = (g)->w; (g)->w = tmp; \
2663 } while (0)
2664 void
2665 stack_master(struct workspace *ws, struct swm_geometry *g, int rot, int flip)
2666 {
2667 XWindowChanges wc;
2668 XWindowAttributes wa;
2669 struct swm_geometry win_g, r_g = *g;
2670 struct ws_win *win, *fs_win = 0;
2671 int i, j, s, stacks;
2672 int w_inc = 1, h_inc, w_base = 1, h_base;
2673 int hrh, extra = 0, h_slice, last_h = 0;
2674 int split, colno, winno, mwin, msize, mscale;
2675 int remain, missing, v_slice, reconfigure;
2676 unsigned int mask;
2677
2678 DNPRINTF(SWM_D_STACK, "stack_master: workspace: %d\n rot=%s flip=%s",
2679 ws->idx, rot ? "yes" : "no", flip ? "yes" : "no");
2680
2681 winno = count_win(ws, 0);
2682 if (winno == 0 && count_win(ws, 1) == 0)
2683 return;
2684
2685 TAILQ_FOREACH(win, &ws->winlist, entry)
2686 if (win->transient == 0 && win->floating == 0
2687 && win->iconic == 0)
2688 break;
2689
2690 if (win == NULL)
2691 goto notiles;
2692
2693 if (rot) {
2694 w_inc = win->sh.width_inc;
2695 w_base = win->sh.base_width;
2696 mwin = ws->l_state.horizontal_mwin;
2697 mscale = ws->l_state.horizontal_msize;
2698 stacks = ws->l_state.horizontal_stacks;
2699 SWAPXY(&r_g);
2700 } else {
2701 w_inc = win->sh.height_inc;
2702 w_base = win->sh.base_height;
2703 mwin = ws->l_state.vertical_mwin;
2704 mscale = ws->l_state.vertical_msize;
2705 stacks = ws->l_state.vertical_stacks;
2706 }
2707 win_g = r_g;
2708
2709 if (stacks > winno - mwin)
2710 stacks = winno - mwin;
2711 if (stacks < 1)
2712 stacks = 1;
2713
2714 h_slice = r_g.h / SWM_H_SLICE;
2715 if (mwin && winno > mwin) {
2716 v_slice = r_g.w / SWM_V_SLICE;
2717
2718 split = mwin;
2719 colno = split;
2720 win_g.w = v_slice * mscale;
2721
2722 if (w_inc > 1 && w_inc < v_slice) {
2723 /* adjust for window's requested size increment */
2724 remain = (win_g.w - w_base) % w_inc;
2725 missing = w_inc - remain;
2726 win_g.w -= remain;
2727 extra += remain;
2728 }
2729
2730 msize = win_g.w;
2731 if (flip)
2732 win_g.x += r_g.w - msize;
2733 } else {
2734 msize = -2;
2735 colno = split = winno / stacks;
2736 win_g.w = ((r_g.w - (stacks * 2 * border_width) +
2737 2 * border_width) / stacks);
2738 }
2739 hrh = r_g.h / colno;
2740 extra = r_g.h - (colno * hrh);
2741 win_g.h = hrh - 2 * border_width;
2742
2743 /* stack all the tiled windows */
2744 i = j = 0, s = stacks;
2745 TAILQ_FOREACH(win, &ws->winlist, entry) {
2746 if (win->transient != 0 || win->floating != 0)
2747 continue;
2748 if (win->iconic != 0)
2749 continue;
2750
2751 if (win->ewmh_flags & EWMH_F_FULLSCREEN) {
2752 fs_win = win;
2753 continue;
2754 }
2755
2756 if (split && i == split) {
2757 colno = (winno - mwin) / stacks;
2758 if (s <= (winno - mwin) % stacks)
2759 colno++;
2760 split = split + colno;
2761 hrh = (r_g.h / colno);
2762 extra = r_g.h - (colno * hrh);
2763 if (flip)
2764 win_g.x = r_g.x;
2765 else
2766 win_g.x += win_g.w + 2 * border_width;
2767 win_g.w = (r_g.w - msize -
2768 (stacks * 2 * border_width)) / stacks;
2769 if (s == 1)
2770 win_g.w += (r_g.w - msize -
2771 (stacks * 2 * border_width)) % stacks;
2772 s--;
2773 j = 0;
2774 }
2775 win_g.h = hrh - 2 * border_width;
2776 if (rot) {
2777 h_inc = win->sh.width_inc;
2778 h_base = win->sh.base_width;
2779 } else {
2780 h_inc = win->sh.height_inc;
2781 h_base = win->sh.base_height;
2782 }
2783 if (j == colno - 1) {
2784 win_g.h = hrh + extra;
2785 } else if (h_inc > 1 && h_inc < h_slice) {
2786 /* adjust for window's requested size increment */
2787 remain = (win_g.h - h_base) % h_inc;
2788 missing = h_inc - remain;
2789
2790 if (missing <= extra || j == 0) {
2791 extra -= missing;
2792 win_g.h += missing;
2793 } else {
2794 win_g.h -= remain;
2795 extra += remain;
2796 }
2797 }
2798
2799 if (j == 0)
2800 win_g.y = r_g.y;
2801 else
2802 win_g.y += last_h + 2 * border_width;
2803
2804 bzero(&wc, sizeof wc);
2805 if (disable_border && bar_enabled == 0 && winno == 1){
2806 wc.border_width = 0;
2807 win_g.w += 2 * border_width;
2808 win_g.h += 2 * border_width;
2809 } else
2810 wc.border_width = border_width;
2811 reconfigure = 0;
2812 if (rot) {
2813 if (win->g.x != win_g.y || win->g.y != win_g.x ||
2814 win->g.w != win_g.h || win->g.h != win_g.w) {
2815 reconfigure = 1;
2816 win->g.x = wc.x = win_g.y;
2817 win->g.y = wc.y = win_g.x;
2818 win->g.w = wc.width = win_g.h;
2819 win->g.h = wc.height = win_g.w;
2820 }
2821 } else {
2822 if (win->g.x != win_g.x || win->g.y != win_g.y ||
2823 win->g.w != win_g.w || win->g.h != win_g.h) {
2824 reconfigure = 1;
2825 win->g.x = wc.x = win_g.x;
2826 win->g.y = wc.y = win_g.y;
2827 win->g.w = wc.width = win_g.w;
2828 win->g.h = wc.height = win_g.h;
2829 }
2830 }
2831 if (reconfigure) {
2832 adjust_font(win);
2833 mask = CWX | CWY | CWWidth | CWHeight | CWBorderWidth;
2834 XConfigureWindow(display, win->id, mask, &wc);
2835 }
2836
2837 if (XGetWindowAttributes(display, win->id, &wa))
2838 if (wa.map_state == IsUnmapped)
2839 XMapRaised(display, win->id);
2840
2841 last_h = win_g.h;
2842 i++;
2843 j++;
2844 }
2845
2846 notiles:
2847 /* now, stack all the floaters and transients */
2848 TAILQ_FOREACH(win, &ws->winlist, entry) {
2849 if (win->transient == 0 && win->floating == 0)
2850 continue;
2851 if (win->iconic == 1)
2852 continue;
2853 if (win->ewmh_flags & EWMH_F_FULLSCREEN) {
2854 fs_win = win;
2855 continue;
2856 }
2857
2858 stack_floater(win, ws->r);
2859 XMapRaised(display, win->id);
2860 }
2861
2862 if (fs_win) {
2863 stack_floater(fs_win, ws->r);
2864 XMapRaised(display, fs_win->id);
2865 }
2866 }
2867
2868 void
2869 vertical_config(struct workspace *ws, int id)
2870 {
2871 DNPRINTF(SWM_D_STACK, "vertical_resize: workspace: %d\n", ws->idx);
2872
2873 switch (id) {
2874 case SWM_ARG_ID_STACKRESET:
2875 case SWM_ARG_ID_STACKINIT:
2876 ws->l_state.vertical_msize = SWM_V_SLICE / 2;
2877 ws->l_state.vertical_mwin = 1;
2878 ws->l_state.vertical_stacks = 1;
2879 break;
2880 case SWM_ARG_ID_MASTERSHRINK:
2881 if (ws->l_state.vertical_msize > 1)
2882 ws->l_state.vertical_msize--;
2883 break;
2884 case SWM_ARG_ID_MASTERGROW:
2885 if (ws->l_state.vertical_msize < SWM_V_SLICE - 1)
2886 ws->l_state.vertical_msize++;
2887 break;
2888 case SWM_ARG_ID_MASTERADD:
2889 ws->l_state.vertical_mwin++;
2890 break;
2891 case SWM_ARG_ID_MASTERDEL:
2892 if (ws->l_state.vertical_mwin > 0)
2893 ws->l_state.vertical_mwin--;
2894 break;
2895 case SWM_ARG_ID_STACKINC:
2896 ws->l_state.vertical_stacks++;
2897 break;
2898 case SWM_ARG_ID_STACKDEC:
2899 if (ws->l_state.vertical_stacks > 1)
2900 ws->l_state.vertical_stacks--;
2901 break;
2902 default:
2903 return;
2904 }
2905 }
2906
2907 void
2908 vertical_stack(struct workspace *ws, struct swm_geometry *g)
2909 {
2910 DNPRINTF(SWM_D_STACK, "vertical_stack: workspace: %d\n", ws->idx);
2911
2912 stack_master(ws, g, 0, 0);
2913 }
2914
2915 void
2916 horizontal_config(struct workspace *ws, int id)
2917 {
2918 DNPRINTF(SWM_D_STACK, "horizontal_config: workspace: %d\n", ws->idx);
2919
2920 switch (id) {
2921 case SWM_ARG_ID_STACKRESET:
2922 case SWM_ARG_ID_STACKINIT:
2923 ws->l_state.horizontal_mwin = 1;
2924 ws->l_state.horizontal_msize = SWM_H_SLICE / 2;
2925 ws->l_state.horizontal_stacks = 1;
2926 break;
2927 case SWM_ARG_ID_MASTERSHRINK:
2928 if (ws->l_state.horizontal_msize > 1)
2929 ws->l_state.horizontal_msize--;
2930 break;
2931 case SWM_ARG_ID_MASTERGROW:
2932 if (ws->l_state.horizontal_msize < SWM_H_SLICE - 1)
2933 ws->l_state.horizontal_msize++;
2934 break;
2935 case SWM_ARG_ID_MASTERADD:
2936 ws->l_state.horizontal_mwin++;
2937 break;
2938 case SWM_ARG_ID_MASTERDEL:
2939 if (ws->l_state.horizontal_mwin > 0)
2940 ws->l_state.horizontal_mwin--;
2941 break;
2942 case SWM_ARG_ID_STACKINC:
2943 ws->l_state.horizontal_stacks++;
2944 break;
2945 case SWM_ARG_ID_STACKDEC:
2946 if (ws->l_state.horizontal_stacks > 1)
2947 ws->l_state.horizontal_stacks--;
2948 break;
2949 default:
2950 return;
2951 }
2952 }
2953
2954 void
2955 horizontal_stack(struct workspace *ws, struct swm_geometry *g)
2956 {
2957 DNPRINTF(SWM_D_STACK, "horizontal_stack: workspace: %d\n", ws->idx);
2958
2959 stack_master(ws, g, 1, 0);
2960 }
2961
2962 /* fullscreen view */
2963 void
2964 max_stack(struct workspace *ws, struct swm_geometry *g)
2965 {
2966 XWindowChanges wc;
2967 struct swm_geometry gg = *g;
2968 struct ws_win *win, *wintrans = NULL, *parent = NULL;
2969 unsigned int mask;
2970 int winno;
2971
2972 DNPRINTF(SWM_D_STACK, "max_stack: workspace: %d\n", ws->idx);
2973
2974 if (ws == NULL)
2975 return;
2976
2977 winno = count_win(ws, 0);
2978 if (winno == 0 && count_win(ws, 1) == 0)
2979 return;
2980
2981 TAILQ_FOREACH(win, &ws->winlist, entry) {
2982 if (win->transient) {
2983 wintrans = win;
2984 parent = find_window(win->transient);
2985 continue;
2986 }
2987
2988 if (win->floating && win->floatmaxed == 0 ) {
2989 /*
2990 * retain geometry for retrieval on exit from
2991 * max_stack mode
2992 */
2993 store_float_geom(win, ws->r);
2994 win->floatmaxed = 1;
2995 }
2996
2997 /* only reconfigure if necessary */
2998 if (win->g.x != gg.x || win->g.y != gg.y || win->g.w != gg.w ||
2999 win->g.h != gg.h) {
3000 bzero(&wc, sizeof wc);
3001 win->g.x = wc.x = gg.x;
3002 win->g.y = wc.y = gg.y;
3003 if (bar_enabled){
3004 wc.border_width = border_width;
3005 win->g.w = wc.width = gg.w;
3006 win->g.h = wc.height = gg.h;
3007 } else {
3008 wc.border_width = 0;
3009 win->g.w = wc.width = gg.w + 2 * border_width;
3010 win->g.h = wc.height = gg.h + 2 * border_width;
3011 }
3012 mask = CWX | CWY | CWWidth | CWHeight | CWBorderWidth;
3013 XConfigureWindow(display, win->id, mask, &wc);
3014 }
3015 /* unmap only if we don't have multi screen */
3016 if (win != ws->focus)
3017 if (!(ScreenCount(display) > 1 || outputs > 1))
3018 unmap_window(win);
3019 }
3020
3021 /* put the last transient on top */
3022 if (wintrans) {
3023 if (parent)
3024 XMapRaised(display, parent->id);
3025 stack_floater(wintrans, ws->r);
3026 focus_magic(wintrans);
3027 }
3028 }
3029
3030 void
3031 send_to_ws(struct swm_region *r, union arg *args)
3032 {
3033 int wsid = args->id;
3034 struct ws_win *win = NULL, *parent;
3035 struct workspace *ws, *nws;
3036 Atom ws_idx_atom = 0;
3037 unsigned char ws_idx_str[SWM_PROPLEN];
3038 union arg a;
3039
3040 if (r && r->ws)
3041 win = r->ws->focus;
3042 else
3043 return;
3044 if (win == NULL)
3045 return;
3046 if (win->ws->idx == wsid)
3047 return;
3048
3049 DNPRINTF(SWM_D_MOVE, "send_to_ws: win: %lu\n", win->id);
3050
3051 ws = win->ws;
3052 nws = &win->s->ws[wsid];
3053
3054 a.id = SWM_ARG_ID_FOCUSPREV;
3055 focus(r, &a);
3056 if (win->transient) {
3057 parent = find_window(win->transient);
3058 if (parent) {
3059 unmap_window(parent);
3060 TAILQ_REMOVE(&ws->winlist, parent, entry);
3061 TAILQ_INSERT_TAIL(&nws->winlist, parent, entry);
3062 parent->ws = nws;
3063 }
3064 }
3065 unmap_window(win);
3066 TAILQ_REMOVE(&ws->winlist, win, entry);
3067 TAILQ_INSERT_TAIL(&nws->winlist, win, entry);
3068 win->ws = nws;
3069
3070 /* Try to update the window's workspace property */
3071 ws_idx_atom = XInternAtom(display, "_SWM_WS", False);
3072 if (ws_idx_atom &&
3073 snprintf((char *)ws_idx_str, SWM_PROPLEN, "%d", nws->idx) <
3074 SWM_PROPLEN) {
3075 DNPRINTF(SWM_D_PROP, "setting property _SWM_WS to %s\n",
3076 ws_idx_str);
3077 XChangeProperty(display, win->id, ws_idx_atom, XA_STRING, 8,
3078 PropModeReplace, ws_idx_str, SWM_PROPLEN);
3079 }
3080
3081 stack();
3082 }
3083
3084 void
3085 pressbutton(struct swm_region *r, union arg *args)
3086 {
3087 XTestFakeButtonEvent(display, args->id, True, CurrentTime);
3088 XTestFakeButtonEvent(display, args->id, False, CurrentTime);
3089 }
3090
3091 void
3092 raise_toggle(struct swm_region *r, union arg *args)
3093 {
3094 if (r && r->ws == NULL)
3095 return;
3096
3097 r->ws->always_raise = !r->ws->always_raise;
3098
3099 /* bring floaters back to top */
3100 if (r->ws->always_raise == 0)
3101 stack();
3102 }
3103
3104 void
3105 iconify(struct swm_region *r, union arg *args)
3106 {
3107 union arg a;
3108
3109 if (r->ws->focus == NULL)
3110 return;
3111 unmap_window(r->ws->focus);
3112 update_iconic(r->ws->focus, 1);
3113 stack();
3114 if (focus_mode == SWM_FOCUS_DEFAULT)
3115 drain_enter_notify();
3116 r->ws->focus = NULL;
3117 a.id = SWM_ARG_ID_FOCUSCUR;
3118 focus(r, &a);
3119 }
3120
3121 unsigned char *
3122 get_win_name(Display *dpy, Window win, Atom wname, Atom stype,
3123 unsigned long *slen)
3124 {
3125 int status, retfmt;
3126 unsigned long nitems, nbytes, nextra;
3127 unsigned char *prop = NULL;
3128 Atom rettype;
3129
3130 status = XGetWindowProperty(dpy, win, wname, 0L, 0L, False, stype,
3131 &rettype, &retfmt, &nitems, &nbytes, &prop);
3132 if (status != Success)
3133 return (NULL);
3134 XFree(prop);
3135
3136 status = XGetWindowProperty(dpy, win, wname, 0L, nbytes, False,
3137 stype, &rettype, &retfmt, &nitems, &nextra, &prop);
3138 if (status != Success) {
3139 XFree(prop);
3140 return (NULL);
3141 }
3142 if (rettype != stype) {
3143 XFree(prop);
3144 return (NULL);
3145 }
3146 *slen = nitems;
3147 return (prop);
3148 }
3149
3150 void
3151 uniconify(struct swm_region *r, union arg *args)
3152 {
3153 struct ws_win *win;
3154 FILE *lfile;
3155 unsigned char *name;
3156 int count = 0;
3157 unsigned long len;
3158
3159 DNPRINTF(SWM_D_MISC, "uniconify\n");
3160
3161 if (r && r->ws == NULL)
3162 return;
3163
3164 /* make sure we have anything to uniconify */
3165 TAILQ_FOREACH(win, &r->ws->winlist, entry) {
3166 if (win->ws == NULL)
3167 continue; /* should never happen */
3168 if (win->iconic == 0)
3169 continue;
3170 count++;
3171 }
3172 if (count == 0)
3173 return;
3174
3175 search_r = r;
3176
3177 spawn_select(r, args, "uniconify", &searchpid);
3178
3179 if ((lfile = fdopen(select_list_pipe[1], "w")) == NULL)
3180 return;
3181
3182 TAILQ_FOREACH(win, &r->ws->winlist, entry) {
3183 if (win->ws == NULL)
3184 continue; /* should never happen */
3185 if (win->iconic == 0)
3186 continue;
3187
3188 name = get_win_name(display, win->id, a_wmname, a_string,
3189 &len);
3190 if (name == NULL)
3191 continue;
3192 fprintf(lfile, "%s.%lu\n", name, win->id);
3193 XFree(name);
3194 }
3195
3196 fclose(lfile);
3197 }
3198
3199 #define MAX_RESP_LEN 1024
3200
3201 void
3202 search_do_resp(void)
3203 {
3204 ssize_t rbytes;
3205 struct ws_win *win;
3206 unsigned char *name;
3207 char *resp, *s;
3208 unsigned long len;
3209
3210 DNPRINTF(SWM_D_MISC, "search_do_resp:\n");
3211
3212 search_resp = 0;
3213 searchpid = 0;
3214
3215 if ((resp = calloc(1, MAX_RESP_LEN + 1)) == NULL) {
3216 fprintf(stderr, "search: calloc\n");
3217 return;
3218 }
3219
3220 rbytes = read(select_resp_pipe[0], resp, MAX_RESP_LEN);
3221 if (rbytes <= 0) {
3222 fprintf(stderr, "search: read error: %s\n", strerror(errno));
3223 goto done;
3224 }
3225 resp[rbytes] = '\0';
3226 len = strlen(resp);
3227
3228 DNPRINTF(SWM_D_MISC, "search_do_resp: resp %s\n", resp);
3229 TAILQ_FOREACH(win, &search_r->ws->winlist, entry) {
3230 if (win->iconic == 0)
3231 continue;
3232 name = get_win_name(display, win->id, a_wmname, a_string, &len);
3233 if (name == NULL)
3234 continue;
3235 if (asprintf(&s, "%s.%lu", name, win->id) == -1) {
3236 XFree(name);
3237 continue;
3238 }
3239 XFree(name);
3240 if (strncmp(s, resp, len) == 0) {
3241 /* XXX this should be a callback to generalize */
3242 update_iconic(win, 0);
3243 free(s);
3244 break;
3245 }
3246 free(s);
3247 }
3248 done:
3249 free(resp);
3250 }
3251
3252 void
3253 wkill(struct swm_region *r, union arg *args)
3254 {
3255 DNPRINTF(SWM_D_MISC, "wkill %d\n", args->id);
3256
3257 if (r->ws->focus == NULL)
3258 return;
3259
3260 if (args->id == SWM_ARG_ID_KILLWINDOW)
3261 XKillClient(display, r->ws->focus->id);
3262 else
3263 if (r->ws->focus->can_delete)
3264 client_msg(r->ws->focus, adelete);
3265 }
3266
3267
3268 int
3269 floating_toggle_win(struct ws_win *win)
3270 {
3271 struct swm_region *r;
3272
3273 if (win == NULL)
3274 return 0;
3275
3276 if (!win->ws->r)
3277 return 0;
3278
3279 r = win->ws->r;
3280
3281 /* reject floating toggles in max stack mode */
3282 if (win->ws->cur_layout == &layouts[SWM_MAX_STACK])
3283 return 0;
3284
3285 if (win->floating) {
3286 if (!win->floatmaxed) {
3287 /* retain position for refloat */
3288 store_float_geom(win, r);
3289 }
3290 win->floating = 0;
3291 } else {
3292 if (win->g_floatvalid) {
3293 /* refloat at last floating relative position */
3294 win->g.x = win->g_float.x - win->rg_float.x + r->g.x;
3295 win->g.y = win->g_float.y - win->rg_float.y + r->g.y;
3296 win->g.w = win->g_float.w;
3297 win->g.h = win->g_float.h;
3298 }
3299 win->floating = 1;
3300 }
3301
3302 ewmh_update_actions(win);
3303
3304 return 1;
3305 }
3306
3307 void
3308 floating_toggle(struct swm_region *r, union arg *args)
3309 {
3310 struct ws_win *win = r->ws->focus;
3311 union arg a;
3312
3313 if (win == NULL)
3314 return;
3315
3316 ewmh_update_win_state(win, ewmh[_NET_WM_STATE_ABOVE].atom,
3317 _NET_WM_STATE_TOGGLE);
3318
3319 stack();
3320 if (focus_mode == SWM_FOCUS_DEFAULT)
3321 drain_enter_notify();
3322
3323 if (win == win->ws->focus) {
3324 a.id = SWM_ARG_ID_FOCUSCUR;
3325 focus(win->ws->r, &a);
3326 }
3327 }
3328
3329 void
3330 resize_window(struct ws_win *win, int center)
3331 {
3332 unsigned int mask;
3333 XWindowChanges wc;
3334 struct swm_region *r;
3335
3336 r = root_to_region(win->wa.root);
3337 bzero(&wc, sizeof wc);
3338 mask = CWBorderWidth | CWWidth | CWHeight;
3339 wc.border_width = border_width;
3340 wc.width = win->g.w;
3341 wc.height = win->g.h;
3342 if (center == SWM_ARG_ID_CENTER) {
3343 wc.x = (WIDTH(r) - win->g.w) / 2 - border_width;
3344 wc.y = (HEIGHT(r) - win->g.h) / 2 - border_width;
3345 mask |= CWX | CWY;
3346 }
3347
3348 DNPRINTF(SWM_D_STACK, "resize_window: win %lu x %d y %d w %d h %d\n",
3349 win->id, wc.x, wc.y, wc.width, wc.height);
3350
3351 XConfigureWindow(display, win->id, mask, &wc);
3352 }
3353
3354 #define SWM_RESIZE_STEPS (50)
3355
3356 void
3357 resize(struct ws_win *win, union arg *args)
3358 {
3359 XEvent ev;
3360 Time time = 0;
3361 struct swm_region *r = NULL;
3362 int relx, rely;
3363 int resize_step = 0;
3364
3365 if (win == NULL)
3366 return;
3367 r = win->ws->r;
3368
3369 DNPRINTF(SWM_D_MOUSE, "resize: win %lu floating %d trans %lu\n",
3370 win->id, win->floating, win->transient);
3371
3372 if (!(win->transient != 0 || win->floating != 0))
3373 return;
3374
3375 /* reject resizes in max mode for floaters (transient ok) */
3376 if (win->floatmaxed)
3377 return;
3378
3379 win->manual = 1;
3380 ewmh_update_win_state(win, ewmh[_SWM_WM_STATE_MANUAL].atom,
3381 _NET_WM_STATE_ADD);
3382
3383 stack();
3384
3385 switch (args->id) {
3386 case SWM_ARG_ID_WIDTHSHRINK:
3387 win->g.w -= SWM_RESIZE_STEPS;
3388 resize_step = 1;
3389 break;
3390 case SWM_ARG_ID_WIDTHGROW:
3391 win->g.w += SWM_RESIZE_STEPS;
3392 resize_step = 1;
3393 break;
3394 case SWM_ARG_ID_HEIGHTSHRINK:
3395 win->g.h -= SWM_RESIZE_STEPS;
3396 resize_step = 1;
3397 break;
3398 case SWM_ARG_ID_HEIGHTGROW:
3399 win->g.h += SWM_RESIZE_STEPS;
3400 resize_step = 1;
3401 break;
3402 default:
3403 break;
3404 }
3405 if (resize_step) {
3406 resize_window(win, 0);
3407 store_float_geom(win,r);
3408 return;
3409 }
3410
3411 if (focus_mode == SWM_FOCUS_DEFAULT)
3412 drain_enter_notify();
3413
3414 if (XGrabPointer(display, win->id, False, MOUSEMASK, GrabModeAsync,
3415 GrabModeAsync, None, None /* cursor */, CurrentTime) != GrabSuccess)
3416 return;
3417
3418 /* place pointer at bottom left corner or nearest point inside r */
3419 if ( win->g.x + win->g.w < r->g.x + r->g.w - 1)
3420 relx = win->g.w - 1;
3421 else
3422 relx = r->g.x + r->g.w - win->g.x - 1;
3423
3424 if ( win->g.y + win->g.h < r->g.y + r->g.h - 1)
3425 rely = win->g.h - 1;
3426 else
3427 rely = r->g.y + r->g.h - win->g.y - 1;
3428
3429 XWarpPointer(display, None, win->id, 0, 0, 0, 0, relx, rely);
3430 do {
3431 XMaskEvent(display, MOUSEMASK | ExposureMask |
3432 SubstructureRedirectMask, &ev);
3433 switch (ev.type) {
3434 case ConfigureRequest:
3435 case Expose:
3436 case MapRequest:
3437 handler[ev.type](&ev);
3438 break;
3439 case MotionNotify:
3440 /* do not allow resize outside of region */
3441 if ( ev.xmotion.x_root < r->g.x ||
3442 ev.xmotion.x_root > r->g.x + r->g.w - 1 ||
3443 ev.xmotion.y_root < r->g.y ||
3444 ev.xmotion.y_root > r->g.y + r->g.h - 1)
3445 continue;
3446
3447 if (ev.xmotion.x <= 1)
3448 ev.xmotion.x = 1;
3449 if (ev.xmotion.y <= 1)
3450 ev.xmotion.y = 1;
3451 win->g.w = ev.xmotion.x + 1;
3452 win->g.h = ev.xmotion.y + 1;
3453
3454 /* not free, don't sync more than 120 times / second */
3455 if ((ev.xmotion.time - time) > (1000 / 120) ) {
3456 time = ev.xmotion.time;
3457 XSync(display, False);
3458 resize_window(win, args->id);
3459 }
3460 break;
3461 }
3462 } while (ev.type != ButtonRelease);
3463 if (time) {
3464 XSync(display, False);
3465 resize_window(win, args->id);
3466 }
3467 store_float_geom(win,r);
3468
3469 XWarpPointer(display, None, win->id, 0, 0, 0, 0, win->g.w - 1,
3470 win->g.h - 1);
3471 XUngrabPointer(display, CurrentTime);
3472
3473 /* drain events */
3474 drain_enter_notify();
3475 }
3476
3477 void
3478 resize_step(struct swm_region *r, union arg *args)
3479 {
3480 struct ws_win *win = NULL;
3481
3482 if (r && r->ws && r->ws->focus)
3483 win = r->ws->focus;
3484 else
3485 return;
3486
3487 resize(win, args);
3488 }
3489
3490
3491 void
3492 move_window(struct ws_win *win)
3493 {
3494 unsigned int mask;
3495 XWindowChanges wc;
3496 struct swm_region *r;
3497
3498 r = root_to_region(win->wa.root);
3499 bzero(&wc, sizeof wc);
3500 mask = CWX | CWY;
3501 wc.x = win->g.x;
3502 wc.y = win->g.y;
3503 wc.border_width = border_width;
3504
3505 DNPRINTF(SWM_D_STACK, "move_window: win %lu x %d y %d w %d h %d\n",
3506 win->id, wc.x, wc.y, wc.width, wc.height);
3507
3508 XConfigureWindow(display, win->id, mask, &wc);
3509 }
3510
3511 #define SWM_MOVE_STEPS (50)
3512
3513 void
3514 move(struct ws_win *win, union arg *args)
3515 {
3516 XEvent ev;
3517 Time time = 0;
3518 int move_step = 0;
3519 struct swm_region *r = NULL;
3520
3521 if (win == NULL)
3522 return;
3523 r = win->ws->r;
3524
3525 DNPRINTF(SWM_D_MOUSE, "move: win %lu floating %d trans %lu\n",
3526 win->id, win->floating, win->transient);
3527
3528 /* in max_stack mode should only move transients */
3529 if (win->ws->cur_layout == &layouts[SWM_MAX_STACK] && !win->transient)
3530 return;
3531
3532 win->manual = 1;
3533 if (win->floating == 0 && !win->transient) {
3534 ewmh_update_win_state(win, ewmh[_NET_WM_STATE_ABOVE].atom,
3535 _NET_WM_STATE_ADD);
3536 }
3537 ewmh_update_win_state(win, ewmh[_SWM_WM_STATE_MANUAL].atom,
3538 _NET_WM_STATE_ADD);
3539
3540 stack();
3541
3542 move_step = 0;
3543 switch (args->id) {
3544 case SWM_ARG_ID_MOVELEFT:
3545 win->g.x -= (SWM_MOVE_STEPS - border_width);
3546 move_step = 1;
3547 break;
3548 case SWM_ARG_ID_MOVERIGHT:
3549 win->g.x += (SWM_MOVE_STEPS - border_width);
3550 move_step = 1;
3551 break;
3552 case SWM_ARG_ID_MOVEUP:
3553 win->g.y -= (SWM_MOVE_STEPS - border_width);
3554 move_step = 1;
3555 break;
3556 case SWM_ARG_ID_MOVEDOWN:
3557 win->g.y += (SWM_MOVE_STEPS - border_width);
3558 move_step = 1;
3559 break;
3560 default:
3561 break;
3562 }
3563 if (move_step) {
3564 move_window(win);
3565 store_float_geom(win,r);
3566 return;
3567 }
3568
3569
3570 if (XGrabPointer(display, win->id, False, MOUSEMASK, GrabModeAsync,
3571 GrabModeAsync, None, None /* cursor */, CurrentTime) != GrabSuccess)
3572 return;
3573 XWarpPointer(display, None, win->id, 0, 0, 0, 0, 0, 0);
3574 do {
3575 XMaskEvent(display, MOUSEMASK | ExposureMask |
3576 SubstructureRedirectMask, &ev);
3577 switch (ev.type) {
3578 case ConfigureRequest:
3579 case Expose:
3580 case MapRequest:
3581 handler[ev.type](&ev);
3582 break;
3583 case MotionNotify:
3584 /* don't allow to move window origin out of region */
3585 if ( ev.xmotion.x_root < r->g.x ||
3586 ev.xmotion.x_root > r->g.x + r->g.w - 1 ||
3587 ev.xmotion.y_root < r->g.y ||
3588 ev.xmotion.y_root > r->g.y + r->g.h - 1)
3589 continue;
3590
3591 win->g.x = ev.xmotion.x_root - border_width;
3592 win->g.y = ev.xmotion.y_root - border_width;
3593
3594 /* not free, don't sync more than 120 times / second */
3595 if ((ev.xmotion.time - time) > (1000 / 120) ) {
3596 time = ev.xmotion.time;
3597 XSync(display, False);
3598 move_window(win);
3599 }
3600 break;
3601 }
3602 } while (ev.type != ButtonRelease);
3603 if (time) {
3604 XSync(display, False);
3605 move_window(win);
3606 }
3607 store_float_geom(win,r);
3608 XWarpPointer(display, None, win->id, 0, 0, 0, 0, 0, 0);
3609 XUngrabPointer(display, CurrentTime);
3610
3611 /* drain events */
3612 drain_enter_notify();
3613 }
3614
3615 void
3616 move_step(struct swm_region *r, union arg *args)
3617 {
3618 struct ws_win *win = NULL;
3619
3620 if (r && r->ws && r->ws->focus)
3621 win = r->ws->focus;
3622 else
3623 return;
3624
3625 if (!(win->transient != 0 || win->floating != 0))
3626 return;
3627
3628 move(win, args);
3629 }
3630
3631
3632 /* user/key callable function IDs */
3633 enum keyfuncid {
3634 kf_cycle_layout,
3635 kf_stack_reset,
3636 kf_master_shrink,
3637 kf_master_grow,
3638 kf_master_add,
3639 kf_master_del,
3640 kf_stack_inc,
3641 kf_stack_dec,
3642 kf_swap_main,
3643 kf_focus_next,
3644 kf_focus_prev,
3645 kf_swap_next,
3646 kf_swap_prev,
3647 kf_spawn_term,
3648 kf_spawn_menu,
3649 kf_quit,
3650 kf_restart,
3651 kf_focus_main,
3652 kf_ws_1,
3653 kf_ws_2,
3654 kf_ws_3,
3655 kf_ws_4,
3656 kf_ws_5,
3657 kf_ws_6,
3658 kf_ws_7,
3659 kf_ws_8,
3660 kf_ws_9,
3661 kf_ws_10,
3662 kf_ws_next,
3663 kf_ws_prev,
3664 kf_ws_prior,
3665 kf_screen_next,
3666 kf_screen_prev,
3667 kf_mvws_1,
3668 kf_mvws_2,
3669 kf_mvws_3,
3670 kf_mvws_4,
3671 kf_mvws_5,
3672 kf_mvws_6,
3673 kf_mvws_7,
3674 kf_mvws_8,
3675 kf_mvws_9,
3676 kf_mvws_10,
3677 kf_bar_toggle,
3678 kf_wind_kill,
3679 kf_wind_del,
3680 kf_screenshot_all,
3681 kf_screenshot_wind,
3682 kf_float_toggle,
3683 kf_version,
3684 kf_spawn_lock,
3685 kf_spawn_initscr,
3686 kf_spawn_custom,
3687 kf_iconify,
3688 kf_uniconify,
3689 kf_raise_toggle,
3690 kf_button2,
3691 kf_width_shrink,
3692 kf_width_grow,
3693 kf_height_shrink,
3694 kf_height_grow,
3695 kf_move_left,
3696 kf_move_right,
3697 kf_move_up,
3698 kf_move_down,
3699 kf_dumpwins, /* MUST BE LAST */
3700 kf_invalid
3701 };
3702
3703 /* key definitions */
3704 void
3705 dummykeyfunc(struct swm_region *r, union arg *args)
3706 {
3707 };
3708
3709 void
3710 legacyfunc(struct swm_region *r, union arg *args)
3711 {
3712 };
3713
3714 struct keyfunc {
3715 char name[SWM_FUNCNAME_LEN];
3716 void (*func)(struct swm_region *r, union arg *);
3717 union arg args;
3718 } keyfuncs[kf_invalid + 1] = {
3719 /* name function argument */
3720 { "cycle_layout", cycle_layout, {0} },
3721 { "stack_reset", stack_config, {.id = SWM_ARG_ID_STACKRESET} },
3722 { "master_shrink", stack_config, {.id = SWM_ARG_ID_MASTERSHRINK} },
3723 { "master_grow", stack_config, {.id = SWM_ARG_ID_MASTERGROW} },
3724 { "master_add", stack_config, {.id = SWM_ARG_ID_MASTERADD} },
3725 { "master_del", stack_config, {.id = SWM_ARG_ID_MASTERDEL} },
3726 { "stack_inc", stack_config, {.id = SWM_ARG_ID_STACKINC} },
3727 { "stack_dec", stack_config, {.id = SWM_ARG_ID_STACKDEC} },
3728 { "swap_main", swapwin, {.id = SWM_ARG_ID_SWAPMAIN} },
3729 { "focus_next", focus, {.id = SWM_ARG_ID_FOCUSNEXT} },
3730 { "focus_prev", focus, {.id = SWM_ARG_ID_FOCUSPREV} },
3731 { "swap_next", swapwin, {.id = SWM_ARG_ID_SWAPNEXT} },
3732 { "swap_prev", swapwin, {.id = SWM_ARG_ID_SWAPPREV} },
3733 { "spawn_term", spawnterm, {.argv = spawn_term} },
3734 { "spawn_menu", legacyfunc, {0} },
3735 { "quit", quit, {0} },
3736 { "restart", restart, {0} },
3737 { "focus_main", focus, {.id = SWM_ARG_ID_FOCUSMAIN} },
3738 { "ws_1", switchws, {.id = 0} },
3739 { "ws_2", switchws, {.id = 1} },
3740 { "ws_3", switchws, {.id = 2} },
3741 { "ws_4", switchws, {.id = 3} },
3742 { "ws_5", switchws, {.id = 4} },
3743 { "ws_6", switchws, {.id = 5} },
3744 { "ws_7", switchws, {.id = 6} },
3745 { "ws_8", switchws, {.id = 7} },
3746 { "ws_9", switchws, {.id = 8} },
3747 { "ws_10", switchws, {.id = 9} },
3748 { "ws_next", cyclews, {.id = SWM_ARG_ID_CYCLEWS_UP} },
3749 { "ws_prev", cyclews, {.id = SWM_ARG_ID_CYCLEWS_DOWN} },
3750 { "ws_prior", priorws, {0} },
3751 { "screen_next", cyclescr, {.id = SWM_ARG_ID_CYCLESC_UP} },
3752 { "screen_prev", cyclescr, {.id = SWM_ARG_ID_CYCLESC_DOWN} },
3753 { "mvws_1", send_to_ws, {.id = 0} },
3754 { "mvws_2", send_to_ws, {.id = 1} },
3755 { "mvws_3", send_to_ws, {.id = 2} },
3756 { "mvws_4", send_to_ws, {.id = 3} },
3757 { "mvws_5", send_to_ws, {.id = 4} },
3758 { "mvws_6", send_to_ws, {.id = 5} },
3759 { "mvws_7", send_to_ws, {.id = 6} },
3760 { "mvws_8", send_to_ws, {.id = 7} },
3761 { "mvws_9", send_to_ws, {.id = 8} },
3762 { "mvws_10", send_to_ws, {.id = 9} },
3763 { "bar_toggle", bar_toggle, {0} },
3764 { "wind_kill", wkill, {.id = SWM_ARG_ID_KILLWINDOW} },
3765 { "wind_del", wkill, {.id = SWM_ARG_ID_DELETEWINDOW} },
3766 { "screenshot_all", legacyfunc, {0} },
3767 { "screenshot_wind", legacyfunc, {0} },
3768 { "float_toggle", floating_toggle,{0} },
3769 { "version", version, {0} },
3770 { "spawn_lock", legacyfunc, {0} },
3771 { "spawn_initscr", legacyfunc, {0} },
3772 { "spawn_custom", dummykeyfunc, {0} },
3773 { "iconify", iconify, {0} },
3774 { "uniconify", uniconify, {0} },
3775 { "raise_toggle", raise_toggle, {0} },
3776 { "button2", pressbutton, {2} },
3777 { "width_shrink", resize_step, {.id = SWM_ARG_ID_WIDTHSHRINK} },
3778 { "width_grow", resize_step, {.id = SWM_ARG_ID_WIDTHGROW} },
3779 { "height_shrink", resize_step, {.id = SWM_ARG_ID_HEIGHTSHRINK} },
3780 { "height_grow", resize_step, {.id = SWM_ARG_ID_HEIGHTGROW} },
3781 { "move_left", move_step, {.id = SWM_ARG_ID_MOVELEFT} },
3782 { "move_right", move_step, {.id = SWM_ARG_ID_MOVERIGHT} },
3783 { "move_up", move_step, {.id = SWM_ARG_ID_MOVEUP} },
3784 { "move_down", move_step, {.id = SWM_ARG_ID_MOVEDOWN} },
3785 { "dumpwins", dumpwins, {0} }, /* MUST BE LAST */
3786 { "invalid key func", NULL, {0} },
3787 };
3788 struct key {
3789 unsigned int mod;
3790 KeySym keysym;
3791 enum keyfuncid funcid;
3792 char *spawn_name;
3793 };
3794 int keys_size = 0, keys_length = 0;
3795 struct key *keys = NULL;
3796
3797 /* mouse */
3798 enum { client_click, root_click };
3799 struct button {
3800 unsigned int action;
3801 unsigned int mask;
3802 unsigned int button;
3803 void (*func)(struct ws_win *, union arg *);
3804 union arg args;
3805 } buttons[] = {
3806 /* action key mouse button func args */
3807 { client_click, MODKEY, Button3, resize, {.id = SWM_ARG_ID_DONTCENTER} },
3808 { client_click, MODKEY | ShiftMask, Button3, resize, {.id = SWM_ARG_ID_CENTER} },
3809 { client_click, MODKEY, Button1, move, {0} },
3810 };
3811
3812 void
3813 update_modkey(unsigned int mod)
3814 {
3815 int i;
3816
3817 mod_key = mod;
3818 for (i = 0; i < keys_length; i++)
3819 if (keys[i].mod & ShiftMask)
3820 keys[i].mod = mod | ShiftMask;
3821 else
3822 keys[i].mod = mod;
3823
3824 for (i = 0; i < LENGTH(buttons); i++)
3825 if (buttons[i].mask & ShiftMask)
3826 buttons[i].mask = mod | ShiftMask;
3827 else
3828 buttons[i].mask = mod;
3829 }
3830
3831 /* spawn */
3832 struct spawn_prog {
3833 char *name;
3834 int argc;
3835 char **argv;
3836 };
3837
3838 int spawns_size = 0, spawns_length = 0;
3839 struct spawn_prog *spawns = NULL;
3840
3841 int
3842 spawn_expand(struct swm_region *r, union arg *args, char *spawn_name,
3843 char ***ret_args)
3844 {
3845 struct spawn_prog *prog = NULL;
3846 int i;
3847 char *ap, **real_args;
3848
3849 DNPRINTF(SWM_D_SPAWN, "spawn_expand %s\n", spawn_name);
3850
3851 /* find program */
3852 for (i = 0; i < spawns_length; i++) {
3853 if (!strcasecmp(spawn_name, spawns[i].name))
3854 prog = &spawns[i];
3855 }
3856 if (prog == NULL) {
3857 fprintf(stderr, "spawn_custom: program %s not found\n",
3858 spawn_name);
3859 return (-1);
3860 }
3861
3862 /* make room for expanded args */
3863 if ((real_args = calloc(prog->argc + 1, sizeof(char *))) == NULL)
3864 err(1, "spawn_custom: calloc real_args");
3865
3866 /* expand spawn_args into real_args */
3867 for (i = 0; i < prog->argc; i++) {
3868 ap = prog->argv[i];
3869 DNPRINTF(SWM_D_SPAWN, "spawn_custom: raw arg = %s\n", ap);
3870 if (!strcasecmp(ap, "$bar_border")) {
3871 if ((real_args[i] =
3872 strdup(r->s->c[SWM_S_COLOR_BAR_BORDER].name))
3873 == NULL)
3874 err(1, "spawn_custom border color");
3875 } else if (!strcasecmp(ap, "$bar_color")) {
3876 if ((real_args[i] =
3877 strdup(r->s->c[SWM_S_COLOR_BAR].name))
3878 == NULL)
3879 err(1, "spawn_custom bar color");
3880 } else if (!strcasecmp(ap, "$bar_font")) {
3881 if ((real_args[i] = strdup(bar_fonts[bar_fidx]))
3882 == NULL)
3883 err(1, "spawn_custom bar fonts");
3884 } else if (!strcasecmp(ap, "$bar_font_color")) {
3885 if ((real_args[i] =
3886 strdup(r->s->c[SWM_S_COLOR_BAR_FONT].name))
3887 == NULL)
3888 err(1, "spawn_custom color font");
3889 } else if (!strcasecmp(ap, "$color_focus")) {
3890 if ((real_args[i] =
3891 strdup(r->s->c[SWM_S_COLOR_FOCUS].name))
3892 == NULL)
3893 err(1, "spawn_custom color focus");
3894 } else if (!strcasecmp(ap, "$color_unfocus")) {
3895 if ((real_args[i] =
3896 strdup(r->s->c[SWM_S_COLOR_UNFOCUS].name))
3897 == NULL)
3898 err(1, "spawn_custom color unfocus");
3899 } else {
3900 /* no match --> copy as is */
3901 if ((real_args[i] = strdup(ap)) == NULL)
3902 err(1, "spawn_custom strdup(ap)");
3903 }
3904 DNPRINTF(SWM_D_SPAWN, "spawn_custom: cooked arg = %s\n",
3905 real_args[i]);
3906 }
3907
3908 #ifdef SWM_DEBUG
3909 if ((swm_debug & SWM_D_SPAWN) != 0) {
3910 fprintf(stderr, "spawn_custom: result = ");
3911 for (i = 0; i < prog->argc; i++)
3912 fprintf(stderr, "\"%s\" ", real_args[i]);
3913 fprintf(stderr, "\n");
3914 }
3915 #endif
3916 *ret_args = real_args;
3917 return (prog->argc);
3918 }
3919
3920 void
3921 spawn_custom(struct swm_region *r, union arg *args, char *spawn_name)
3922 {
3923 union arg a;
3924 char **real_args;
3925 int spawn_argc, i;
3926
3927 if ((spawn_argc = spawn_expand(r, args, spawn_name, &real_args)) < 0)
3928 return;
3929 a.argv = real_args;
3930 if (fork() == 0)
3931 spawn(r->ws->idx, &a, 1);
3932
3933 for (i = 0; i < spawn_argc; i++)
3934 free(real_args[i]);
3935 free(real_args);
3936 }
3937
3938 void
3939 spawn_select(struct swm_region *r, union arg *args, char *spawn_name, int *pid)
3940 {
3941 union arg a;
3942 char **real_args;
3943 int i, spawn_argc;
3944
3945 if ((spawn_argc = spawn_expand(r, args, spawn_name, &real_args)) < 0)
3946 return;
3947 a.argv = real_args;
3948
3949 if (pipe(select_list_pipe) == -1)
3950 err(1, "pipe error");
3951 if (pipe(select_resp_pipe) == -1)
3952 err(1, "pipe error");
3953
3954 if (signal(SIGPIPE, SIG_IGN) == SIG_ERR)
3955 err(1, "could not disable SIGPIPE");
3956 switch (*pid = fork()) {
3957 case -1:
3958 err(1, "cannot fork");
3959 break;
3960 case 0: /* child */
3961 if (dup2(select_list_pipe[0], 0) == -1)
3962 errx(1, "dup2");
3963 if (dup2(select_resp_pipe[1], 1) == -1)
3964 errx(1, "dup2");
3965 close(select_list_pipe[1]);
3966 close(select_resp_pipe[0]);
3967 spawn(r->ws->idx, &a, 0);
3968 break;
3969 default: /* parent */
3970 close(select_list_pipe[0]);
3971 close(select_resp_pipe[1]);
3972 break;
3973 }
3974
3975 for (i = 0; i < spawn_argc; i++)
3976 free(real_args[i]);
3977 free(real_args);
3978 }
3979
3980 void
3981 setspawn(struct spawn_prog *prog)
3982 {
3983 int i, j;
3984
3985 if (prog == NULL || prog->name == NULL)
3986 return;
3987
3988 /* find existing */
3989 for (i = 0; i < spawns_length; i++) {
3990 if (!strcmp(spawns[i].name, prog->name)) {
3991 /* found */
3992 if (prog->argv == NULL) {
3993 /* delete */
3994 DNPRINTF(SWM_D_SPAWN,
3995 "setspawn: delete #%d %s\n",
3996 i, spawns[i].name);
3997 free(spawns[i].name);
3998 for (j = 0; j < spawns[i].argc; j++)
3999 free(spawns[i].argv[j]);
4000 free(spawns[i].argv);
4001 j = spawns_length - 1;
4002 if (i < j)
4003 spawns[i] = spawns[j];
4004 spawns_length--;
4005 free(prog->name);
4006 } else {
4007 /* replace */
4008 DNPRINTF(SWM_D_SPAWN,
4009 "setspawn: replace #%d %s\n",
4010 i, spawns[i].name);
4011 free(spawns[i].name);
4012 for (j = 0; j < spawns[i].argc; j++)
4013 free(spawns[i].argv[j]);
4014 free(spawns[i].argv);
4015 spawns[i] = *prog;
4016 }
4017 /* found case handled */
4018 free(prog);
4019 return;
4020 }
4021 }
4022
4023 if (prog->argv == NULL) {
4024 fprintf(stderr,
4025 "error: setspawn: cannot find program %s", prog->name);
4026 free(prog);
4027 return;
4028 }
4029
4030 /* not found: add */
4031 if (spawns_size == 0 || spawns == NULL) {
4032 spawns_size = 4;
4033 DNPRINTF(SWM_D_SPAWN, "setspawn: init list %d\n", spawns_size);
4034 spawns = malloc((size_t)spawns_size *
4035 sizeof(struct spawn_prog));
4036 if (spawns == NULL) {
4037 fprintf(stderr, "setspawn: malloc failed\n");
4038 perror(" failed");
4039 quit(NULL, NULL);
4040 }
4041 } else if (spawns_length == spawns_size) {
4042 spawns_size *= 2;
4043 DNPRINTF(SWM_D_SPAWN, "setspawn: grow list %d\n", spawns_size);
4044 spawns = realloc(spawns, (size_t)spawns_size *
4045 sizeof(struct spawn_prog));
4046 if (spawns == NULL) {
4047 fprintf(stderr, "setspawn: realloc failed\n");
4048 perror(" failed");
4049 quit(NULL, NULL);
4050 }
4051 }
4052
4053 if (spawns_length < spawns_size) {
4054 DNPRINTF(SWM_D_SPAWN, "setspawn: add #%d %s\n",
4055 spawns_length, prog->name);
4056 i = spawns_length++;
4057 spawns[i] = *prog;
4058 } else {
4059 fprintf(stderr, "spawns array problem?\n");
4060 if (spawns == NULL) {
4061 fprintf(stderr, "spawns array is NULL!\n");
4062 quit(NULL, NULL);
4063 }
4064 }
4065 free(prog);
4066 }
4067
4068 int
4069 setconfspawn(char *selector, char *value, int flags)
4070 {
4071 struct spawn_prog *prog;
4072 char *vp, *cp, *word;
4073
4074 DNPRINTF(SWM_D_SPAWN, "setconfspawn: [%s] [%s]\n", selector, value);
4075 if ((prog = calloc(1, sizeof *prog)) == NULL)
4076 err(1, "setconfspawn: calloc prog");
4077 prog->name = strdup(selector);
4078 if (prog->name == NULL)
4079 err(1, "setconfspawn prog->name");
4080 if ((cp = vp = strdup(value)) == NULL)
4081 err(1, "setconfspawn: strdup(value) ");
4082 while ((word = strsep(&cp, " \t")) != NULL) {
4083 DNPRINTF(SWM_D_SPAWN, "setconfspawn: arg [%s]\n", word);
4084 if (cp)
4085 cp += (long)strspn(cp, " \t");
4086 if (strlen(word) > 0) {
4087 prog->argc++;
4088 if ((prog->argv = realloc(prog->argv,
4089 prog->argc * sizeof(char *))) == NULL)
4090 err(1, "setconfspawn: realloc");
4091 if ((prog->argv[prog->argc - 1] = strdup(word)) == NULL)
4092 err(1, "setconfspawn: strdup");
4093 }
4094 }
4095 free(vp);
4096
4097 setspawn(prog);
4098
4099 DNPRINTF(SWM_D_SPAWN, "setconfspawn: done\n");
4100 return (0);
4101 }
4102
4103 void
4104 setup_spawn(void)
4105 {
4106 setconfspawn("term", "xterm", 0);
4107 setconfspawn("screenshot_all", "screenshot.sh full", 0);
4108 setconfspawn("screenshot_wind", "screenshot.sh window", 0);
4109 setconfspawn("lock", "xlock", 0);
4110 setconfspawn("initscr", "initscreen.sh", 0);
4111 setconfspawn("menu", "dmenu_run"
4112 " -fn $bar_font"
4113 " -nb $bar_color"
4114 " -nf $bar_font_color"
4115 " -sb $bar_border"
4116 " -sf $bar_color", 0);
4117 setconfspawn("uniconify", "dmenu"
4118 " -i"
4119 " -fn $bar_font"
4120 " -nb $bar_color"
4121 " -nf $bar_font_color"
4122 " -sb $bar_border"
4123 " -sf $bar_color", 0);
4124 }
4125
4126 /* key bindings */
4127 #define SWM_MODNAME_SIZE 32
4128 #define SWM_KEY_WS "\n+ \t"
4129 int
4130 parsekeys(char *keystr, unsigned int currmod, unsigned int *mod, KeySym *ks)
4131 {
4132 char *cp, *name;
4133 KeySym uks;
4134 DNPRINTF(SWM_D_KEY, "parsekeys: enter [%s]\n", keystr);
4135 if (mod == NULL || ks == NULL) {
4136 DNPRINTF(SWM_D_KEY, "parsekeys: no mod or key vars\n");
4137 return (1);
4138 }
4139 if (keystr == NULL || strlen(keystr) == 0) {
4140 DNPRINTF(SWM_D_KEY, "parsekeys: no keystr\n");
4141 return (1);
4142 }
4143 cp = keystr;
4144 *ks = NoSymbol;
4145 *mod = 0;
4146 while ((name = strsep(&cp, SWM_KEY_WS)) != NULL) {
4147 DNPRINTF(SWM_D_KEY, "parsekeys: key [%s]\n", name);
4148 if (cp)
4149 cp += (long)strspn(cp, SWM_KEY_WS);
4150 if (strncasecmp(name, "MOD", SWM_MODNAME_SIZE) == 0)
4151 *mod |= currmod;
4152 else if (!strncasecmp(name, "Mod1", SWM_MODNAME_SIZE))
4153 *mod |= Mod1Mask;
4154 else if (!strncasecmp(name, "Mod2", SWM_MODNAME_SIZE))
4155 *mod += Mod2Mask;
4156 else if (!strncmp(name, "Mod3", SWM_MODNAME_SIZE))
4157 *mod |= Mod3Mask;
4158 else if (!strncmp(name, "Mod4", SWM_MODNAME_SIZE))
4159 *mod |= Mod4Mask;
4160 else if (strncasecmp(name, "SHIFT", SWM_MODNAME_SIZE) == 0)
4161 *mod |= ShiftMask;
4162 else if (strncasecmp(name, "CONTROL", SWM_MODNAME_SIZE) == 0)
4163 *mod |= ControlMask;
4164 else {
4165 *ks = XStringToKeysym(name);
4166 XConvertCase(*ks, ks, &uks);
4167 if (ks == NoSymbol) {
4168 DNPRINTF(SWM_D_KEY,
4169 "parsekeys: invalid key %s\n",
4170 name);
4171 return (1);
4172 }
4173 }
4174 }
4175 DNPRINTF(SWM_D_KEY, "parsekeys: leave ok\n");
4176 return (0);
4177 }
4178
4179 char *
4180 strdupsafe(char *str)
4181 {
4182 if (str == NULL)
4183 return (NULL);
4184 else
4185 return (strdup(str));
4186 }
4187
4188 void
4189 setkeybinding(unsigned int mod, KeySym ks, enum keyfuncid kfid,
4190 char *spawn_name)
4191 {
4192 int i, j;
4193 DNPRINTF(SWM_D_KEY, "setkeybinding: enter %s [%s]\n",
4194 keyfuncs[kfid].name, spawn_name);
4195 /* find existing */
4196 for (i = 0; i < keys_length; i++) {
4197 if (keys[i].mod == mod && keys[i].keysym == ks) {
4198 if (kfid == kf_invalid) {
4199 /* found: delete */
4200 DNPRINTF(SWM_D_KEY,
4201 "setkeybinding: delete #%d %s\n",
4202 i, keyfuncs[keys[i].funcid].name);
4203 free(keys[i].spawn_name);
4204 j = keys_length - 1;
4205 if (i < j)
4206 keys[i] = keys[j];
4207 keys_length--;
4208 DNPRINTF(SWM_D_KEY, "setkeybinding: leave\n");
4209 return;
4210 } else {
4211 /* found: replace */
4212 DNPRINTF(SWM_D_KEY,
4213 "setkeybinding: replace #%d %s %s\n",
4214 i, keyfuncs[keys[i].funcid].name,
4215 spawn_name);
4216 free(keys[i].spawn_name);
4217 keys[i].mod = mod;
4218 keys[i].keysym = ks;
4219 keys[i].funcid = kfid;
4220 keys[i].spawn_name = strdupsafe(spawn_name);
4221 DNPRINTF(SWM_D_KEY, "setkeybinding: leave\n");
4222 return;
4223 }
4224 }
4225 }
4226 if (kfid == kf_invalid) {
4227 fprintf(stderr,
4228 "error: setkeybinding: cannot find mod/key combination");
4229 DNPRINTF(SWM_D_KEY, "setkeybinding: leave\n");
4230 return;
4231 }
4232 /* not found: add */
4233 if (keys_size == 0 || keys == NULL) {
4234 keys_size = 4;
4235 DNPRINTF(SWM_D_KEY, "setkeybinding: init list %d\n", keys_size);
4236 keys = malloc((size_t)keys_size * sizeof(struct key));
4237 if (keys == NULL) {
4238 fprintf(stderr, "malloc failed\n");
4239 perror(" failed");
4240 quit(NULL, NULL);
4241 }
4242 } else if (keys_length == keys_size) {
4243 keys_size *= 2;
4244 DNPRINTF(SWM_D_KEY, "setkeybinding: grow list %d\n", keys_size);
4245 keys = realloc(keys, (size_t)keys_size * sizeof(struct key));
4246 if (keys == NULL) {
4247 fprintf(stderr, "realloc failed\n");
4248 perror(" failed");
4249 quit(NULL, NULL);
4250 }
4251 }
4252 if (keys_length < keys_size) {
4253 j = keys_length++;
4254 DNPRINTF(SWM_D_KEY, "setkeybinding: add #%d %s %s\n",
4255 j, keyfuncs[kfid].name, spawn_name);
4256 keys[j].mod = mod;
4257 keys[j].keysym = ks;
4258 keys[j].funcid = kfid;
4259 keys[j].spawn_name = strdupsafe(spawn_name);
4260 } else {
4261 fprintf(stderr, "keys array problem?\n");
4262 if (keys == NULL) {
4263 fprintf(stderr, "keys array problem\n");
4264 quit(NULL, NULL);
4265 }
4266 }
4267 DNPRINTF(SWM_D_KEY, "setkeybinding: leave\n");
4268 }
4269
4270 int
4271 setconfbinding(char *selector, char *value, int flags)
4272 {
4273 enum keyfuncid kfid;
4274 unsigned int mod;
4275 KeySym ks;
4276 int i;
4277 DNPRINTF(SWM_D_KEY, "setconfbinding: enter\n");
4278 if (selector == NULL) {
4279 DNPRINTF(SWM_D_KEY, "setconfbinding: unbind %s\n", value);
4280 if (parsekeys(value, mod_key, &mod, &ks) == 0) {
4281 kfid = kf_invalid;
4282 setkeybinding(mod, ks, kfid, NULL);
4283 return (0);
4284 } else
4285 return (1);
4286 }
4287 /* search by key function name */
4288 for (kfid = 0; kfid < kf_invalid; (kfid)++) {
4289 if (strncasecmp(selector, keyfuncs[kfid].name,
4290 SWM_FUNCNAME_LEN) == 0) {
4291 DNPRINTF(SWM_D_KEY, "setconfbinding: %s: match\n",
4292 selector);
4293 if (parsekeys(value, mod_key, &mod, &ks) == 0) {
4294 setkeybinding(mod, ks, kfid, NULL);
4295 return (0);
4296 } else
4297 return (1);
4298 }
4299 }
4300 /* search by custom spawn name */
4301 for (i = 0; i < spawns_length; i++) {
4302 if (strcasecmp(selector, spawns[i].name) == 0) {
4303 DNPRINTF(SWM_D_KEY, "setconfbinding: %s: match\n",
4304 selector);
4305 if (parsekeys(value, mod_key, &mod, &ks) == 0) {
4306 setkeybinding(mod, ks, kf_spawn_custom,
4307 spawns[i].name);
4308 return (0);
4309 } else
4310 return (1);
4311 }
4312 }
4313 DNPRINTF(SWM_D_KEY, "setconfbinding: no match\n");
4314 return (1);
4315 }
4316
4317 void
4318 setup_keys(void)
4319 {
4320 setkeybinding(MODKEY, XK_space, kf_cycle_layout,NULL);
4321 setkeybinding(MODKEY|ShiftMask, XK_space, kf_stack_reset, NULL);
4322 setkeybinding(MODKEY, XK_h, kf_master_shrink,NULL);
4323 setkeybinding(MODKEY, XK_l, kf_master_grow, NULL);
4324 setkeybinding(MODKEY, XK_comma, kf_master_add, NULL);
4325 setkeybinding(MODKEY, XK_period, kf_master_del, NULL);
4326 setkeybinding(MODKEY|ShiftMask, XK_comma, kf_stack_inc, NULL);
4327 setkeybinding(MODKEY|ShiftMask, XK_period, kf_stack_dec, NULL);
4328 setkeybinding(MODKEY, XK_Return, kf_swap_main, NULL);
4329 setkeybinding(MODKEY, XK_j, kf_focus_next, NULL);
4330 setkeybinding(MODKEY, XK_k, kf_focus_prev, NULL);
4331 setkeybinding(MODKEY|ShiftMask, XK_j, kf_swap_next, NULL);
4332 setkeybinding(MODKEY|ShiftMask, XK_k, kf_swap_prev, NULL);
4333 setkeybinding(MODKEY|ShiftMask, XK_Return, kf_spawn_term, NULL);
4334 setkeybinding(MODKEY, XK_p, kf_spawn_custom,"menu");
4335 setkeybinding(MODKEY|ShiftMask, XK_q, kf_quit, NULL);
4336 setkeybinding(MODKEY, XK_q, kf_restart, NULL);
4337 setkeybinding(MODKEY, XK_m, kf_focus_main, NULL);
4338 setkeybinding(MODKEY, XK_1, kf_ws_1, NULL);
4339 setkeybinding(MODKEY, XK_2, kf_ws_2, NULL);
4340 setkeybinding(MODKEY, XK_3, kf_ws_3, NULL);
4341 setkeybinding(MODKEY, XK_4, kf_ws_4, NULL);
4342 setkeybinding(MODKEY, XK_5, kf_ws_5, NULL);
4343 setkeybinding(MODKEY, XK_6, kf_ws_6, NULL);
4344 setkeybinding(MODKEY, XK_7, kf_ws_7, NULL);
4345 setkeybinding(MODKEY, XK_8, kf_ws_8, NULL);
4346 setkeybinding(MODKEY, XK_9, kf_ws_9, NULL);
4347 setkeybinding(MODKEY, XK_0, kf_ws_10, NULL);
4348 setkeybinding(MODKEY, XK_Right, kf_ws_next, NULL);
4349 setkeybinding(MODKEY, XK_Left, kf_ws_prev, NULL);
4350 setkeybinding(MODKEY, XK_a, kf_ws_prior, NULL);
4351 setkeybinding(MODKEY|ShiftMask, XK_Right, kf_screen_next, NULL);
4352 setkeybinding(MODKEY|ShiftMask, XK_Left, kf_screen_prev, NULL);
4353 setkeybinding(MODKEY|ShiftMask, XK_1, kf_mvws_1, NULL);
4354 setkeybinding(MODKEY|ShiftMask, XK_2, kf_mvws_2, NULL);
4355 setkeybinding(MODKEY|ShiftMask, XK_3, kf_mvws_3, NULL);
4356 setkeybinding(MODKEY|ShiftMask, XK_4, kf_mvws_4, NULL);
4357 setkeybinding(MODKEY|ShiftMask, XK_5, kf_mvws_5, NULL);
4358 setkeybinding(MODKEY|ShiftMask, XK_6, kf_mvws_6, NULL);
4359 setkeybinding(MODKEY|ShiftMask, XK_7, kf_mvws_7, NULL);
4360 setkeybinding(MODKEY|ShiftMask, XK_8, kf_mvws_8, NULL);
4361 setkeybinding(MODKEY|ShiftMask, XK_9, kf_mvws_9, NULL);
4362 setkeybinding(MODKEY|ShiftMask, XK_0, kf_mvws_10, NULL);
4363 setkeybinding(MODKEY, XK_b, kf_bar_toggle, NULL);
4364 setkeybinding(MODKEY, XK_Tab, kf_focus_next, NULL);
4365 setkeybinding(MODKEY|ShiftMask, XK_Tab, kf_focus_prev, NULL);
4366 setkeybinding(MODKEY|ShiftMask, XK_x, kf_wind_kill, NULL);
4367 setkeybinding(MODKEY, XK_x, kf_wind_del, NULL);
4368 setkeybinding(MODKEY, XK_s, kf_spawn_custom,"screenshot_all");
4369 setkeybinding(MODKEY|ShiftMask, XK_s, kf_spawn_custom,"screenshot_wind");
4370 setkeybinding(MODKEY, XK_t, kf_float_toggle,NULL);
4371 setkeybinding(MODKEY|ShiftMask, XK_v, kf_version, NULL);
4372 setkeybinding(MODKEY|ShiftMask, XK_Delete, kf_spawn_custom,"lock");
4373 setkeybinding(MODKEY|ShiftMask, XK_i, kf_spawn_custom,"initscr");
4374 setkeybinding(MODKEY, XK_w, kf_iconify, NULL);
4375 setkeybinding(MODKEY|ShiftMask, XK_w, kf_uniconify, NULL);
4376 setkeybinding(MODKEY|ShiftMask, XK_r, kf_raise_toggle,NULL);
4377 setkeybinding(MODKEY, XK_v, kf_button2, NULL);
4378 setkeybinding(MODKEY, XK_equal, kf_width_grow, NULL);
4379 setkeybinding(MODKEY, XK_minus, kf_width_shrink,NULL);
4380 setkeybinding(MODKEY|ShiftMask, XK_equal, kf_height_grow, NULL);
4381 setkeybinding(MODKEY|ShiftMask, XK_minus, kf_height_shrink,NULL);
4382 setkeybinding(MODKEY, XK_bracketleft, kf_move_left, NULL);
4383 setkeybinding(MODKEY, XK_bracketright,kf_move_right, NULL);
4384 setkeybinding(MODKEY|ShiftMask, XK_bracketleft, kf_move_up, NULL);
4385 setkeybinding(MODKEY|ShiftMask, XK_bracketright,kf_move_down, NULL);
4386 #ifdef SWM_DEBUG
4387 setkeybinding(MODKEY|ShiftMask, XK_d, kf_dumpwins, NULL);
4388 #endif
4389 }
4390
4391 void
4392 clear_keys(void)
4393 {
4394 int i;
4395
4396 /* clear all key bindings, if any */
4397 for (i = 0; i < keys_length; i++)
4398 free(keys[i].spawn_name);
4399 keys_length = 0;
4400 }
4401
4402 int
4403 setkeymapping(char *selector, char *value, int flags)
4404 {
4405 char keymapping_file[PATH_MAX];
4406 DNPRINTF(SWM_D_KEY, "setkeymapping: enter\n");
4407 if (value[0] == '~')
4408 snprintf(keymapping_file, sizeof keymapping_file, "%s/%s",
4409 pwd->pw_dir, &value[1]);
4410 else
4411 strlcpy(keymapping_file, value, sizeof keymapping_file);
4412 clear_keys();
4413 /* load new key bindings; if it fails, revert to default bindings */
4414 if (conf_load(keymapping_file, SWM_CONF_KEYMAPPING)) {
4415 clear_keys();
4416 setup_keys();
4417 }
4418 DNPRINTF(SWM_D_KEY, "setkeymapping: leave\n");
4419 return (0);
4420 }
4421
4422 void
4423 updatenumlockmask(void)
4424 {
4425 unsigned int i, j;
4426 XModifierKeymap *modmap;
4427
4428 DNPRINTF(SWM_D_MISC, "updatenumlockmask\n");
4429 numlockmask = 0;
4430 modmap = XGetModifierMapping(display);
4431 for (i = 0; i < 8; i++)
4432 for (j = 0; j < modmap->max_keypermod; j++)
4433 if (modmap->modifiermap[i * modmap->max_keypermod + j]
4434 == XKeysymToKeycode(display, XK_Num_Lock))
4435 numlockmask = (1 << i);
4436
4437 XFreeModifiermap(modmap);
4438 }
4439
4440 void
4441 grabkeys(void)
4442 {
4443 unsigned int i, j, k;
4444 KeyCode code;
4445 unsigned int modifiers[] =
4446 { 0, LockMask, numlockmask, numlockmask | LockMask };
4447
4448 DNPRINTF(SWM_D_MISC, "grabkeys\n");
4449 updatenumlockmask();
4450
4451 for (k = 0; k < ScreenCount(display); k++) {
4452 if (TAILQ_EMPTY(&screens[k].rl))
4453 continue;
4454 XUngrabKey(display, AnyKey, AnyModifier, screens[k].root);
4455 for (i = 0; i < keys_length; i++) {
4456 if ((code = XKeysymToKeycode(display, keys[i].keysym)))
4457 for (j = 0; j < LENGTH(modifiers); j++)
4458 XGrabKey(display, code,
4459 keys[i].mod | modifiers[j],
4460 screens[k].root, True,
4461 GrabModeAsync, GrabModeAsync);
4462 }
4463 }
4464 }
4465
4466 void
4467 grabbuttons(struct ws_win *win, int focused)
4468 {
4469 unsigned int i, j;
4470 unsigned int modifiers[] =
4471 { 0, LockMask, numlockmask, numlockmask|LockMask };
4472
4473 updatenumlockmask();
4474 XUngrabButton(display, AnyButton, AnyModifier, win->id);
4475 if (focused) {
4476 for (i = 0; i < LENGTH(buttons); i++)
4477 if (buttons[i].action == client_click)
4478 for (j = 0; j < LENGTH(modifiers); j++)
4479 XGrabButton(display, buttons[i].button,
4480 buttons[i].mask | modifiers[j],
4481 win->id, False, BUTTONMASK,
4482 GrabModeAsync, GrabModeSync, None,
4483 None);
4484 } else
4485 XGrabButton(display, AnyButton, AnyModifier, win->id, False,
4486 BUTTONMASK, GrabModeAsync, GrabModeSync, None, None);
4487 }
4488
4489 const char *quirkname[] = {
4490 "NONE", /* config string for "no value" */
4491 "FLOAT",
4492 "TRANSSZ",
4493 "ANYWHERE",
4494 "XTERM_FONTADJ",
4495 "FULLSCREEN",
4496 "FOCUSPREV",
4497 };
4498
4499 /* SWM_Q_WS: retain '|' for back compat for now (2009-08-11) */
4500 #define SWM_Q_WS "\n|+ \t"
4501 int
4502 parsequirks(char *qstr, unsigned long *quirk)
4503 {
4504 char *cp, *name;
4505 int i;
4506
4507 if (quirk == NULL)
4508 return (1);
4509
4510 cp = qstr;
4511 *quirk = 0;
4512 while ((name = strsep(&cp, SWM_Q_WS)) != NULL) {
4513 if (cp)
4514 cp += (long)strspn(cp, SWM_Q_WS);
4515 for (i = 0; i < LENGTH(quirkname); i++) {
4516 if (!strncasecmp(name, quirkname[i], SWM_QUIRK_LEN)) {
4517 DNPRINTF(SWM_D_QUIRK,
4518 "parsequirks: %s\n", name);
4519 if (i == 0) {
4520 *quirk = 0;
4521 return (0);
4522 }
4523 *quirk |= 1 << (i-1);
4524 break;
4525 }
4526 }
4527 if (i >= LENGTH(quirkname)) {
4528 DNPRINTF(SWM_D_QUIRK,
4529 "parsequirks: invalid quirk [%s]\n", name);
4530 return (1);
4531 }
4532 }
4533 return (0);
4534 }
4535
4536 void
4537 setquirk(const char *class, const char *name, const int quirk)
4538 {
4539 int i, j;
4540
4541 /* find existing */
4542 for (i = 0; i < quirks_length; i++) {
4543 if (!strcmp(quirks[i].class, class) &&
4544 !strcmp(quirks[i].name, name)) {
4545 if (!quirk) {
4546 /* found: delete */
4547 DNPRINTF(SWM_D_QUIRK,
4548 "setquirk: delete #%d %s:%s\n",
4549 i, quirks[i].class, quirks[i].name);
4550 free(quirks[i].class);
4551 free(quirks[i].name);
4552 j = quirks_length - 1;
4553 if (i < j)
4554 quirks[i] = quirks[j];
4555 quirks_length--;
4556 return;
4557 } else {
4558 /* found: replace */
4559 DNPRINTF(SWM_D_QUIRK,
4560 "setquirk: replace #%d %s:%s\n",
4561 i, quirks[i].class, quirks[i].name);
4562 free(quirks[i].class);
4563 free(quirks[i].name);
4564 quirks[i].class = strdup(class);
4565 quirks[i].name = strdup(name);
4566 quirks[i].quirk = quirk;
4567 return;
4568 }
4569 }
4570 }
4571 if (!quirk) {
4572 fprintf(stderr,
4573 "error: setquirk: cannot find class/name combination");
4574 return;
4575 }
4576 /* not found: add */
4577 if (quirks_size == 0 || quirks == NULL) {
4578 quirks_size = 4;
4579 DNPRINTF(SWM_D_QUIRK, "setquirk: init list %d\n", quirks_size);
4580 quirks = malloc((size_t)quirks_size * sizeof(struct quirk));
4581 if (quirks == NULL) {
4582 fprintf(stderr, "setquirk: malloc failed\n");
4583 perror(" failed");
4584 quit(NULL, NULL);
4585 }
4586 } else if (quirks_length == quirks_size) {
4587 quirks_size *= 2;
4588 DNPRINTF(SWM_D_QUIRK, "setquirk: grow list %d\n", quirks_size);
4589 quirks = realloc(quirks,
4590 (size_t)quirks_size * sizeof(struct quirk));
4591 if (quirks == NULL) {
4592 fprintf(stderr, "setquirk: realloc failed\n");
4593 perror(" failed");
4594 quit(NULL, NULL);
4595 }
4596 }
4597 if (quirks_length < quirks_size) {
4598 DNPRINTF(SWM_D_QUIRK, "setquirk: add %d\n", quirks_length);
4599 j = quirks_length++;
4600 quirks[j].class = strdup(class);
4601 quirks[j].name = strdup(name);
4602 quirks[j].quirk = quirk;
4603 } else {
4604 fprintf(stderr, "quirks array problem?\n");
4605 if (quirks == NULL) {
4606 fprintf(stderr, "quirks array problem!\n");
4607 quit(NULL, NULL);
4608 }
4609 }
4610 }
4611
4612 int
4613 setconfquirk(char *selector, char *value, int flags)
4614 {
4615 char *cp, *class, *name;
4616 int retval;
4617 unsigned long quirks;
4618 if (selector == NULL)
4619 return (0);
4620 if ((cp = strchr(selector, ':')) == NULL)
4621 return (0);
4622 *cp = '\0';
4623 class = selector;
4624 name = cp + 1;
4625 if ((retval = parsequirks(value, &quirks)) == 0)
4626 setquirk(class, name, quirks);
4627 return (retval);
4628 }
4629
4630 void
4631 setup_quirks(void)
4632 {
4633 setquirk("MPlayer", "xv", SWM_Q_FLOAT | SWM_Q_FULLSCREEN | SWM_Q_FOCUSPREV);
4634 setquirk("OpenOffice.org 3.2", "VCLSalFrame", SWM_Q_FLOAT);
4635 setquirk("Firefox-bin", "firefox-bin", SWM_Q_TRANSSZ);
4636 setquirk("Firefox", "Dialog", SWM_Q_FLOAT);
4637 setquirk("Gimp", "gimp", SWM_Q_FLOAT | SWM_Q_ANYWHERE);
4638 setquirk("XTerm", "xterm", SWM_Q_XTERM_FONTADJ);
4639 setquirk("xine", "Xine Window", SWM_Q_FLOAT | SWM_Q_ANYWHERE);
4640 setquirk("Xitk", "Xitk Combo", SWM_Q_FLOAT | SWM_Q_ANYWHERE);
4641 setquirk("xine", "xine Panel", SWM_Q_FLOAT | SWM_Q_ANYWHERE);
4642 setquirk("Xitk", "Xine Window", SWM_Q_FLOAT | SWM_Q_ANYWHERE);
4643 setquirk("xine", "xine Video Fullscreen Window", SWM_Q_FULLSCREEN | SWM_Q_FLOAT);
4644 setquirk("pcb", "pcb", SWM_Q_FLOAT);
4645 setquirk("SDL_App", "SDL_App", SWM_Q_FLOAT | SWM_Q_FULLSCREEN);
4646 }
4647
4648 /* conf file stuff */
4649 #define SWM_CONF_FILE "scrotwm.conf"
4650
4651 enum { SWM_S_BAR_DELAY, SWM_S_BAR_ENABLED, SWM_S_BAR_BORDER_WIDTH,
4652 SWM_S_STACK_ENABLED, SWM_S_CLOCK_ENABLED, SWM_S_CLOCK_FORMAT,
4653 SWM_S_CYCLE_EMPTY, SWM_S_CYCLE_VISIBLE, SWM_S_SS_ENABLED,
4654 SWM_S_TERM_WIDTH, SWM_S_TITLE_CLASS_ENABLED,
4655 SWM_S_TITLE_NAME_ENABLED, SWM_S_WINDOW_NAME_ENABLED, SWM_S_URGENT_ENABLED,
4656 SWM_S_FOCUS_MODE, SWM_S_DISABLE_BORDER, SWM_S_BORDER_WIDTH,
4657 SWM_S_BAR_FONT, SWM_S_BAR_ACTION, SWM_S_SPAWN_TERM,
4658 SWM_S_SS_APP, SWM_S_DIALOG_RATIO, SWM_S_BAR_AT_BOTTOM,
4659 SWM_S_VERBOSE_LAYOUT
4660 };
4661
4662 int
4663 setconfvalue(char *selector, char *value, int flags)
4664 {
4665 int i;
4666 switch (flags) {
4667 case SWM_S_BAR_DELAY:
4668 bar_delay = atoi(value);
4669 break;
4670 case SWM_S_BAR_ENABLED:
4671 bar_enabled = atoi(value);
4672 break;
4673 case SWM_S_BAR_BORDER_WIDTH:
4674 bar_border_width = atoi(value);
4675 break;
4676 case SWM_S_BAR_AT_BOTTOM:
4677 bar_at_bottom = atoi(value);
4678 break;
4679 case SWM_S_STACK_ENABLED:
4680 stack_enabled = atoi(value);
4681 break;
4682 case SWM_S_CLOCK_ENABLED:
4683 clock_enabled = atoi(value);
4684 break;
4685 case SWM_S_CLOCK_FORMAT:
4686 #ifndef SWM_DENY_CLOCK_FORMAT
4687 free(clock_format);
4688 if ((clock_format = strdup(value)) == NULL)
4689 err(1, "setconfvalue: clock_format");
4690 #endif
4691 break;
4692 case SWM_S_CYCLE_EMPTY:
4693 cycle_empty = atoi(value);
4694 break;
4695 case SWM_S_CYCLE_VISIBLE:
4696 cycle_visible = atoi(value);
4697 break;
4698 case SWM_S_SS_ENABLED:
4699 ss_enabled = atoi(value);
4700 break;
4701 case SWM_S_TERM_WIDTH:
4702 term_width = atoi(value);
4703 break;
4704 case SWM_S_TITLE_CLASS_ENABLED:
4705 title_class_enabled = atoi(value);
4706 break;
4707 case SWM_S_WINDOW_NAME_ENABLED:
4708 window_name_enabled = atoi(value);
4709 break;
4710 case SWM_S_TITLE_NAME_ENABLED:
4711 title_name_enabled = atoi(value);
4712 break;
4713 case SWM_S_URGENT_ENABLED:
4714 urgent_enabled = atoi(value);
4715 break;
4716 case SWM_S_FOCUS_MODE:
4717 if (!strcmp(value, "default"))
4718 focus_mode = SWM_FOCUS_DEFAULT;
4719 else if (!strcmp(value, "follow_cursor"))
4720 focus_mode = SWM_FOCUS_FOLLOW;
4721 else if (!strcmp(value, "synergy"))
4722 focus_mode = SWM_FOCUS_SYNERGY;
4723 else
4724 err(1, "focus_mode");
4725 break;
4726 case SWM_S_DISABLE_BORDER:
4727 disable_border = atoi(value);
4728 break;
4729 case SWM_S_BORDER_WIDTH:
4730 border_width = atoi(value);
4731 break;
4732 case SWM_S_BAR_FONT:
4733 free(bar_fonts[0]);
4734 if ((bar_fonts[0] = strdup(value)) == NULL)
4735 err(1, "setconfvalue: bar_font");
4736 break;
4737 case SWM_S_BAR_ACTION:
4738 free(bar_argv[0]);
4739 if ((bar_argv[0] = strdup(value)) == NULL)
4740 err(1, "setconfvalue: bar_action");
4741 break;
4742 case SWM_S_SPAWN_TERM:
4743 free(spawn_term[0]);
4744 if ((spawn_term[0] = strdup(value)) == NULL)
4745 err(1, "setconfvalue: spawn_term");
4746 break;
4747 case SWM_S_SS_APP:
4748 break;
4749 case SWM_S_DIALOG_RATIO:
4750 dialog_ratio = atof(value);
4751 if (dialog_ratio > 1.0 || dialog_ratio <= .3)
4752 dialog_ratio = .6;
4753 break;
4754 case SWM_S_VERBOSE_LAYOUT:
4755 verbose_layout = atoi(value);
4756 for (i = 0; layouts[i].l_stack != NULL; i++) {
4757 if (verbose_layout)
4758 layouts[i].l_string = fancy_stacker;
4759 else
4760 layouts[i].l_string = plain_stacker;
4761 }
4762 break;
4763 default:
4764 return (1);
4765 }
4766 return (0);
4767 }
4768
4769 int
4770 setconfmodkey(char *selector, char *value, int flags)
4771 {
4772 if (!strncasecmp(value, "Mod1", strlen("Mod1")))
4773 update_modkey(Mod1Mask);
4774 else if (!strncasecmp(value, "Mod2", strlen("Mod2")))
4775 update_modkey(Mod2Mask);
4776 else if (!strncasecmp(value, "Mod3", strlen("Mod3")))
4777 update_modkey(Mod3Mask);
4778 else if (!strncasecmp(value, "Mod4", strlen("Mod4")))
4779 update_modkey(Mod4Mask);
4780 else
4781 return (1);
4782 return (0);
4783 }
4784
4785 int
4786 setconfcolor(char *selector, char *value, int flags)
4787 {
4788 setscreencolor(value, ((selector == NULL)?-1:atoi(selector)), flags);
4789 return (0);
4790 }
4791
4792 int
4793 setconfregion(char *selector, char *value, int flags)
4794 {
4795 custom_region(value);
4796 return (0);
4797 }
4798
4799 int
4800 setautorun(char *selector, char *value, int flags)
4801 {
4802 int ws_id;
4803 char s[1024];
4804 char *ap, *sp = s;
4805 union arg a;
4806 int argc = 0;
4807 long pid;
4808 struct pid_e *p;
4809
4810 if (getenv("SWM_STARTED"))
4811 return (0);
4812
4813 bzero(s, sizeof s);
4814 if (sscanf(value, "ws[%d]:%1023c", &ws_id, s) != 2)
4815 errx(1, "invalid autorun entry, should be 'ws[<idx>]:command'\n");
4816 ws_id--;
4817 if (ws_id < 0 || ws_id >= SWM_WS_MAX)
4818 errx(1, "autorun: invalid workspace %d\n", ws_id + 1);
4819
4820 /*
4821 * This is a little intricate
4822 *
4823 * If the pid already exists we simply reuse it because it means it was
4824 * used before AND not claimed by manage_window. We get away with
4825 * altering it in the parent after INSERT because this can not be a race
4826 */
4827 a.argv = NULL;
4828 while ((ap = strsep(&sp, " \t")) != NULL) {
4829 if (*ap == '\0')
4830 continue;
4831 DNPRINTF(SWM_D_SPAWN, "setautorun: arg [%s]\n", ap);
4832 argc++;
4833 if ((a.argv = realloc(a.argv, argc * sizeof(char *))) == NULL)
4834 err(1, "setautorun: realloc");
4835 a.argv[argc - 1] = ap;
4836 }
4837
4838 if ((a.argv = realloc(a.argv, (argc + 1) * sizeof(char *))) == NULL)
4839 err(1, "setautorun: realloc");
4840 a.argv[argc] = NULL;
4841
4842 if ((pid = fork()) == 0) {
4843 spawn(ws_id, &a, 1);
4844 /* NOTREACHED */
4845 _exit(1);
4846 }
4847 free(a.argv);
4848
4849 /* parent */
4850 p = find_pid(pid);
4851 if (p == NULL) {
4852 p = calloc(1, sizeof *p);
4853 if (p == NULL)
4854 return (1);
4855 TAILQ_INSERT_TAIL(&pidlist, p, entry);
4856 }
4857
4858 p->pid = pid;
4859 p->ws = ws_id;
4860
4861 return (0);
4862 }
4863
4864 int
4865 setlayout(char *selector, char *value, int flags)
4866 {
4867 int ws_id, i, x, mg, ma, si, raise;
4868 int st = SWM_V_STACK;
4869 char s[1024];
4870 struct workspace *ws;
4871
4872 if (getenv("SWM_STARTED"))
4873 return (0);
4874
4875 bzero(s, sizeof s);
4876 if (sscanf(value, "ws[%d]:%d:%d:%d:%d:%1023c",
4877 &ws_id, &mg, &ma, &si, &raise, s) != 6)
4878 errx(1, "invalid layout entry, should be 'ws[<idx>]:"
4879 "<master_grow>:<master_add>:<stack_inc>:<always_raise>:"
4880 "<type>'\n");
4881 ws_id--;
4882 if (ws_id < 0 || ws_id >= SWM_WS_MAX)
4883 errx(1, "layout: invalid workspace %d\n", ws_id + 1);
4884
4885 if (!strcasecmp(s, "vertical"))
4886 st = SWM_V_STACK;
4887 else if (!strcasecmp(s, "horizontal"))
4888 st = SWM_H_STACK;
4889 else if (!strcasecmp(s, "fullscreen"))
4890 st = SWM_MAX_STACK;
4891 else
4892 errx(1, "invalid layout entry, should be 'ws[<idx>]:"
4893 "<master_grow>:<master_add>:<stack_inc>:<always_raise>:"
4894 "<type>'\n");
4895
4896 for (i = 0; i < ScreenCount(display); i++) {
4897 ws = (struct workspace *)&screens[i].ws;
4898 ws[ws_id].cur_layout = &layouts[st];
4899
4900 ws[ws_id].always_raise = raise;
4901 if (st == SWM_MAX_STACK)
4902 continue;
4903
4904 /* master grow */
4905 for (x = 0; x < abs(mg); x++) {
4906 ws[ws_id].cur_layout->l_config(&ws[ws_id],
4907 mg >= 0 ? SWM_ARG_ID_MASTERGROW :
4908 SWM_ARG_ID_MASTERSHRINK);
4909 stack();
4910 }
4911 /* master add */
4912 for (x = 0; x < abs(ma); x++) {
4913 ws[ws_id].cur_layout->l_config(&ws[ws_id],
4914 ma >= 0 ? SWM_ARG_ID_MASTERADD :
4915 SWM_ARG_ID_MASTERDEL);
4916 stack();
4917 }
4918 /* stack inc */
4919 for (x = 0; x < abs(si); x++) {
4920 ws[ws_id].cur_layout->l_config(&ws[ws_id],
4921 si >= 0 ? SWM_ARG_ID_STACKINC :
4922 SWM_ARG_ID_STACKDEC);
4923 stack();
4924 }
4925 }
4926
4927 return (0);
4928 }
4929
4930 /* config options */
4931 struct config_option {
4932 char *optname;
4933 int (*func)(char*, char*, int);
4934 int funcflags;
4935 };
4936 struct config_option configopt[] = {
4937 { "bar_enabled", setconfvalue, SWM_S_BAR_ENABLED },
4938 { "bar_at_bottom", setconfvalue, SWM_S_BAR_AT_BOTTOM },
4939 { "bar_border", setconfcolor, SWM_S_COLOR_BAR_BORDER },
4940 { "bar_border_width", setconfvalue, SWM_S_BAR_BORDER_WIDTH },
4941 { "bar_color", setconfcolor, SWM_S_COLOR_BAR },
4942 { "bar_font_color", setconfcolor, SWM_S_COLOR_BAR_FONT },
4943 { "bar_font", setconfvalue, SWM_S_BAR_FONT },
4944 { "bar_action", setconfvalue, SWM_S_BAR_ACTION },
4945 { "bar_delay", setconfvalue, SWM_S_BAR_DELAY },
4946 { "keyboard_mapping", setkeymapping, 0 },
4947 { "bind", setconfbinding, 0 },
4948 { "stack_enabled", setconfvalue, SWM_S_STACK_ENABLED },
4949 { "clock_enabled", setconfvalue, SWM_S_CLOCK_ENABLED },
4950 { "clock_format", setconfvalue, SWM_S_CLOCK_FORMAT },
4951 { "color_focus", setconfcolor, SWM_S_COLOR_FOCUS },
4952 { "color_unfocus", setconfcolor, SWM_S_COLOR_UNFOCUS },
4953 { "cycle_empty", setconfvalue, SWM_S_CYCLE_EMPTY },
4954 { "cycle_visible", setconfvalue, SWM_S_CYCLE_VISIBLE },
4955 { "dialog_ratio", setconfvalue, SWM_S_DIALOG_RATIO },
4956 { "verbose_layout", setconfvalue, SWM_S_VERBOSE_LAYOUT },
4957 { "modkey", setconfmodkey, 0 },
4958 { "program", setconfspawn, 0 },
4959 { "quirk", setconfquirk, 0 },
4960 { "region", setconfregion, 0 },
4961 { "spawn_term", setconfvalue, SWM_S_SPAWN_TERM },
4962 { "screenshot_enabled", setconfvalue, SWM_S_SS_ENABLED },
4963 { "screenshot_app", setconfvalue, SWM_S_SS_APP },
4964 { "window_name_enabled", setconfvalue, SWM_S_WINDOW_NAME_ENABLED },
4965 { "urgent_enabled", setconfvalue, SWM_S_URGENT_ENABLED },
4966 { "term_width", setconfvalue, SWM_S_TERM_WIDTH },
4967 { "title_class_enabled", setconfvalue, SWM_S_TITLE_CLASS_ENABLED },
4968 { "title_name_enabled", setconfvalue, SWM_S_TITLE_NAME_ENABLED },
4969 { "focus_mode", setconfvalue, SWM_S_FOCUS_MODE },
4970 { "disable_border", setconfvalue, SWM_S_DISABLE_BORDER },
4971 { "border_width", setconfvalue, SWM_S_BORDER_WIDTH },
4972 { "autorun", setautorun, 0 },
4973 { "layout", setlayout, 0 },
4974 };
4975
4976
4977 int
4978 conf_load(char *filename, int keymapping)
4979 {
4980 FILE *config;
4981 char *line, *cp, *optsub, *optval;
4982 size_t linelen, lineno = 0;
4983 int wordlen, i, optind;
4984 struct config_option *opt;
4985
4986 DNPRINTF(SWM_D_CONF, "conf_load begin\n");
4987
4988 if (filename == NULL) {
4989 fprintf(stderr, "conf_load: no filename\n");
4990 return (1);
4991 }
4992 if ((config = fopen(filename, "r")) == NULL) {
4993 warn("conf_load: fopen: %s", filename);
4994 return (1);
4995 }
4996
4997 while (!feof(config)) {
4998 if ((line = fparseln(config, &linelen, &lineno, NULL, 0))
4999 == NULL) {
5000 if (ferror(config))
5001 err(1, "%s", filename);
5002 else
5003 continue;
5004 }
5005 cp = line;
5006 cp += strspn(cp, " \t\n"); /* eat whitespace */
5007 if (cp[0] == '\0') {
5008 /* empty line */
5009 free(line);
5010 continue;
5011 }
5012 /* get config option */
5013 wordlen = strcspn(cp, "=[ \t\n");
5014 if (wordlen == 0) {
5015 warnx("%s: line %zd: no option found",
5016 filename, lineno);
5017 return (1);
5018 }
5019 optind = -1;
5020 for (i = 0; i < LENGTH(configopt); i++) {
5021 opt = &configopt[i];
5022 if (!strncasecmp(cp, opt->optname, wordlen) &&
5023 strlen(opt->optname) == wordlen) {
5024 optind = i;
5025 break;
5026 }
5027 }
5028 if (optind == -1) {
5029 warnx("%s: line %zd: unknown option %.*s",
5030 filename, lineno, wordlen, cp);
5031 return (1);
5032 }
5033 if (keymapping && strcmp(opt->optname, "bind")) {
5034 warnx("%s: line %zd: invalid option %.*s",
5035 filename, lineno, wordlen, cp);
5036 return (1);
5037 }
5038 cp += wordlen;
5039 cp += strspn(cp, " \t\n"); /* eat whitespace */
5040 /* get [selector] if any */
5041 optsub = NULL;
5042 if (*cp == '[') {
5043 cp++;
5044 wordlen = strcspn(cp, "]");
5045 if (*cp != ']') {
5046 if (wordlen == 0) {
5047 warnx("%s: line %zd: syntax error",
5048 filename, lineno);
5049 return (1);
5050 }
5051 asprintf(&optsub, "%.*s", wordlen, cp);
5052 }
5053 cp += wordlen;
5054 cp += strspn(cp, "] \t\n"); /* eat trailing */
5055 }
5056 cp += strspn(cp, "= \t\n"); /* eat trailing */
5057 /* get RHS value */
5058 optval = strdup(cp);
5059 /* call function to deal with it all */
5060 if (configopt[optind].func(optsub, optval,
5061 configopt[optind].funcflags) != 0) {
5062 fprintf(stderr, "%s line %zd: %s\n",
5063 filename, lineno, line);
5064 errx(1, "%s: line %zd: invalid data for %s",
5065 filename, lineno, configopt[optind].optname);
5066 }
5067 free(optval);
5068 free(optsub);
5069 free(line);
5070 }
5071
5072 fclose(config);
5073 DNPRINTF(SWM_D_CONF, "conf_load end\n");
5074
5075 return (0);
5076 }
5077
5078 void
5079 set_child_transient(struct ws_win *win, Window *trans)
5080 {
5081 struct ws_win *parent, *w;
5082 XWMHints *wmh = NULL;
5083 struct swm_region *r;
5084 struct workspace *ws;
5085
5086 parent = find_window(win->transient);
5087 if (parent)
5088 parent->child_trans = win;
5089 else {
5090 DNPRINTF(SWM_D_MISC, "set_child_transient: parent doesn't exist"
5091 " for %lu trans %lu\n", win->id, win->transient);
5092
5093 if (win->hints == NULL) {
5094 fprintf(stderr, "no hints for %lu\n", win->id);
5095 return;
5096 }
5097
5098 r = root_to_region(win->wa.root);
5099 ws = r->ws;
5100 /* parent doen't exist in our window list */
5101 TAILQ_FOREACH(w, &ws->winlist, entry) {
5102 if (wmh)
5103 XFree(wmh);
5104
5105 if ((wmh = XGetWMHints(display, w->id)) == NULL) {
5106 fprintf(stderr, "can't get hints for %lu\n",
5107 w->id);
5108 continue;
5109 }
5110
5111 if (win->hints->window_group != wmh->window_group)
5112 continue;
5113
5114 w->child_trans = win;
5115 win->transient = w->id;
5116 *trans = w->id;
5117 DNPRINTF(SWM_D_MISC, "set_child_transient: asjusting "
5118 "transient to %lu\n", win->transient);
5119 break;
5120 }
5121 }
5122
5123 if (wmh)
5124 XFree(wmh);
5125 }
5126
5127 long
5128 window_get_pid(Window win)
5129 {
5130 Atom actual_type_return;
5131 int actual_format_return = 0;
5132 unsigned long nitems_return = 0;
5133 unsigned long bytes_after_return = 0;
5134 long *pid = NULL;
5135 long ret = 0;
5136 const char *errstr;
5137 unsigned char *prop = NULL;
5138
5139 if (XGetWindowProperty(display, win,
5140 XInternAtom(display, "_NET_WM_PID", False), 0, 1, False,
5141 XA_CARDINAL, &actual_type_return, &actual_format_return,
5142 &nitems_return, &bytes_after_return,
5143 (unsigned char**)(void*)&pid) != Success)
5144 goto tryharder;
5145 if (actual_type_return != XA_CARDINAL)
5146 goto tryharder;
5147 if (pid == NULL)
5148 goto tryharder;
5149
5150 ret = *pid;
5151 XFree(pid);
5152
5153 return (ret);
5154
5155 tryharder:
5156 if (XGetWindowProperty(display, win,
5157 XInternAtom(display, "_SWM_PID", False), 0, SWM_PROPLEN, False,
5158 XA_STRING, &actual_type_return, &actual_format_return,
5159 &nitems_return, &bytes_after_return, &prop) != Success)
5160 return (0);
5161 if (actual_type_return != XA_STRING)
5162 return (0);
5163 if (prop == NULL)
5164 return (0);
5165
5166 ret = strtonum((const char *)prop, 0, UINT_MAX, &errstr);
5167 /* ignore error because strtonum returns 0 anyway */
5168 XFree(prop);
5169
5170 return (ret);
5171 }
5172
5173 struct ws_win *
5174 manage_window(Window id)
5175 {
5176 Window trans = 0;
5177 struct workspace *ws;
5178 struct ws_win *win, *ww;
5179 int format, i, ws_idx, n, border_me = 0;
5180 unsigned long nitems, bytes;
5181 Atom ws_idx_atom = 0, type;
5182 Atom *prot = NULL, *pp;
5183 unsigned char ws_idx_str[SWM_PROPLEN], *prop = NULL;
5184 struct swm_region *r;
5185 long mask;
5186 const char *errstr;
5187 XWindowChanges wc;
5188 struct pid_e *p;
5189
5190 if ((win = find_window(id)) != NULL)
5191 return (win); /* already being managed */
5192
5193 /* see if we are on the unmanaged list */
5194 if ((win = find_unmanaged_window(id)) != NULL) {
5195 DNPRINTF(SWM_D_MISC, "manage previously unmanaged window "
5196 "%lu\n", win->id);
5197 TAILQ_REMOVE(&win->ws->unmanagedlist, win, entry);
5198 if (win->transient) {
5199 set_child_transient(win, &trans);
5200 } if (trans && (ww = find_window(trans)))
5201 TAILQ_INSERT_AFTER(&win->ws->winlist, ww, win, entry);
5202 else
5203 TAILQ_INSERT_TAIL(&win->ws->winlist, win, entry);
5204 ewmh_update_actions(win);
5205 return (win);
5206 }
5207
5208 if ((win = calloc(1, sizeof(struct ws_win))) == NULL)
5209 errx(1, "calloc: failed to allocate memory for new window");
5210
5211 win->id = id;
5212
5213 /* see if we need to override the workspace */
5214 p = find_pid(window_get_pid(id));
5215
5216 /* Get all the window data in one shot */
5217 ws_idx_atom = XInternAtom(display, "_SWM_WS", False);
5218 if (ws_idx_atom) {
5219 XGetWindowProperty(display, id, ws_idx_atom, 0, SWM_PROPLEN,
5220 False, XA_STRING, &type, &format, &nitems, &bytes, &prop);
5221 }
5222 XGetWindowAttributes(display, id, &win->wa);
5223 XGetWMNormalHints(display, id, &win->sh, &mask);
5224 win->hints = XGetWMHints(display, id);
5225 XGetTransientForHint(display, id, &trans);
5226 if (trans) {
5227 win->transient = trans;
5228 set_child_transient(win, &trans);
5229 DNPRINTF(SWM_D_MISC, "manage_window: win %lu transient %lu\n",
5230 win->id, win->transient);
5231 }
5232
5233 /* get supported protocols */
5234 if (XGetWMProtocols(display, id, &prot, &n)) {
5235 for (i = 0, pp = prot; i < n; i++, pp++) {
5236 if (*pp == takefocus)
5237 win->take_focus = 1;
5238 if (*pp == adelete)
5239 win->can_delete = 1;
5240 }
5241 if (prot)
5242 XFree(prot);
5243 }
5244
5245 win->iconic = get_iconic(win);
5246
5247 /*
5248 * Figure out where to put the window. If it was previously assigned to
5249 * a workspace (either by spawn() or manually moving), and isn't
5250 * transient, * put it in the same workspace
5251 */
5252 r = root_to_region(win->wa.root);
5253 if (p) {
5254 ws = &r->s->ws[p->ws];
5255 TAILQ_REMOVE(&pidlist, p, entry);
5256 free(p);
5257 p = NULL;
5258 } else if (prop && win->transient == 0) {
5259 DNPRINTF(SWM_D_PROP, "got property _SWM_WS=%s\n", prop);
5260 ws_idx = strtonum((const char *)prop, 0, 9, &errstr);
5261 if (errstr) {
5262 DNPRINTF(SWM_D_EVENT, "window idx is %s: %s",
5263 errstr, prop);
5264 }
5265 ws = &r->s->ws[ws_idx];
5266 } else {
5267 ws = r->ws;
5268 /* this should launch transients in the same ws as parent */
5269 if (id && trans)
5270 if ((ww = find_window(trans)) != NULL)
5271 if (ws->r) {
5272 ws = ww->ws;
5273 if (ww->ws->r)
5274 r = ww->ws->r;
5275 else
5276 fprintf(stderr,
5277 "fix this bug mcbride\n");
5278 border_me = 1;
5279 }
5280 }
5281
5282 /* set up the window layout */
5283 win->id = id;
5284 win->ws = ws;
5285 win->s = r->s; /* this never changes */
5286 if (trans && (ww = find_window(trans)))
5287 TAILQ_INSERT_AFTER(&ws->winlist, ww, win, entry);
5288 else
5289 TAILQ_INSERT_TAIL(&ws->winlist, win, entry);
5290
5291 win->g.w = win->wa.width;
5292 win->g.h = win->wa.height;
5293 win->g.x = win->wa.x;
5294 win->g.y = win->wa.y;
5295 win->g_floatvalid = 0;
5296 win->floatmaxed = 0;
5297 win->ewmh_flags = 0;
5298
5299 /* Set window properties so we can remember this after reincarnation */
5300 if (ws_idx_atom && prop == NULL &&
5301 snprintf((char *)ws_idx_str, SWM_PROPLEN, "%d", ws->idx) <
5302 SWM_PROPLEN) {
5303 DNPRINTF(SWM_D_PROP, "setting property _SWM_WS to %s\n",
5304 ws_idx_str);
5305 XChangeProperty(display, win->id, ws_idx_atom, XA_STRING, 8,
5306 PropModeReplace, ws_idx_str, SWM_PROPLEN);
5307 }
5308 if (prop)
5309 XFree(prop);
5310
5311 ewmh_autoquirk(win);
5312
5313 if (XGetClassHint(display, win->id, &win->ch)) {
5314 DNPRINTF(SWM_D_CLASS, "class: %s name: %s\n",
5315 win->ch.res_class, win->ch.res_name);
5316
5317 /* java is retarded so treat it special */
5318 if (strstr(win->ch.res_name, "sun-awt")) {
5319 win->java = 1;
5320 border_me = 1;
5321 }
5322
5323 for (i = 0; i < quirks_length; i++){
5324 if (!strcmp(win->ch.res_class, quirks[i].class) &&
5325 !strcmp(win->ch.res_name, quirks[i].name)) {
5326 DNPRINTF(SWM_D_CLASS, "found: %s name: %s\n",
5327 win->ch.res_class, win->ch.res_name);
5328 if (quirks[i].quirk & SWM_Q_FLOAT) {
5329 win->floating = 1;
5330 border_me = 1;
5331 }
5332 win->quirks = quirks[i].quirk;
5333 }
5334 }
5335 }
5336
5337 /* alter window position if quirky */
5338 if (win->quirks & SWM_Q_ANYWHERE) {
5339 win->manual = 1; /* don't center the quirky windows */
5340 bzero(&wc, sizeof wc);
5341 mask = 0;
5342 if (bar_enabled && win->g.y < bar_height) {
5343 win->g.y = wc.y = bar_height;
5344 mask |= CWY;
5345 }
5346 if (win->g.w + win->g.x > WIDTH(r)) {
5347 win->g.x = wc.x = WIDTH(r) - win->g.w - 2;
5348 mask |= CWX;
5349 }
5350 border_me = 1;
5351 }
5352
5353 /* Reset font sizes (the bruteforce way; no default keybinding). */
5354 if (win->quirks & SWM_Q_XTERM_FONTADJ) {
5355 for (i = 0; i < SWM_MAX_FONT_STEPS; i++)
5356 fake_keypress(win, XK_KP_Subtract, ShiftMask);
5357 for (i = 0; i < SWM_MAX_FONT_STEPS; i++)
5358 fake_keypress(win, XK_KP_Add, ShiftMask);
5359 }
5360
5361 ewmh_get_win_state(win);
5362 ewmh_update_actions(win);
5363 ewmh_update_win_state(win, None, _NET_WM_STATE_REMOVE);
5364
5365 /* border me */
5366 if (border_me) {
5367 bzero(&wc, sizeof wc);
5368 wc.border_width = border_width;
5369 mask = CWBorderWidth;
5370 XConfigureWindow(display, win->id, mask, &wc);
5371 }
5372
5373 XSelectInput(display, id, EnterWindowMask | FocusChangeMask |
5374 PropertyChangeMask | StructureNotifyMask);
5375
5376 /* floaters need to be mapped if they are in the current workspace */
5377 if ((win->floating || win->transient) && (ws->idx == r->ws->idx))
5378 XMapRaised(display, win->id);
5379
5380 return (win);
5381 }
5382
5383 void
5384 free_window(struct ws_win *win)
5385 {
5386 DNPRINTF(SWM_D_MISC, "free_window: %lu\n", win->id);
5387
5388 if (win == NULL)
5389 return;
5390
5391 /* needed for restart wm */
5392 set_win_state(win, WithdrawnState);
5393
5394 TAILQ_REMOVE(&win->ws->unmanagedlist, win, entry);
5395
5396 if (win->ch.res_class)
5397 XFree(win->ch.res_class);
5398 if (win->ch.res_name)
5399 XFree(win->ch.res_name);
5400
5401 kill_refs(win);
5402
5403 /* paint memory */
5404 memset(win, 0xff, sizeof *win); /* XXX kill later */
5405
5406 free(win);
5407 }
5408
5409 void
5410 unmanage_window(struct ws_win *win)
5411 {
5412 struct ws_win *parent;
5413
5414 if (win == NULL)
5415 return;
5416
5417 DNPRINTF(SWM_D_MISC, "unmanage_window: %lu\n", win->id);
5418
5419 if (win->transient) {
5420 parent = find_window(win->transient);
5421 if (parent)
5422 parent->child_trans = NULL;
5423 }
5424
5425 /* focus on root just in case */
5426 XSetInputFocus(display, PointerRoot, PointerRoot, CurrentTime);
5427
5428 focus_prev(win);
5429
5430 TAILQ_REMOVE(&win->ws->winlist, win, entry);
5431 TAILQ_INSERT_TAIL(&win->ws->unmanagedlist, win, entry);
5432
5433 kill_refs(win);
5434 }
5435
5436 void
5437 focus_magic(struct ws_win *win)
5438 {
5439 DNPRINTF(SWM_D_FOCUS, "focus_magic: %lu\n", WINID(win));
5440
5441 if (win == NULL)
5442 return;
5443
5444 if (win->child_trans) {
5445 /* win = parent & has a transient so focus on that */
5446 if (win->java) {
5447 focus_win(win->child_trans);
5448 if (win->child_trans->take_focus)
5449 client_msg(win, takefocus);
5450 } else {
5451 /* make sure transient hasn't dissapeared */
5452 if (validate_win(win->child_trans) == 0) {
5453 focus_win(win->child_trans);
5454 if (win->child_trans->take_focus)
5455 client_msg(win->child_trans, takefocus);
5456 } else {
5457 win->child_trans = NULL;
5458 focus_win(win);
5459 if (win->take_focus)
5460 client_msg(win, takefocus);
5461 }
5462 }
5463 } else {
5464 /* regular focus */
5465 focus_win(win);
5466 if (win->take_focus)
5467 client_msg(win, takefocus);
5468 }
5469 }
5470
5471 void
5472 expose(XEvent *e)
5473 {
5474 DNPRINTF(SWM_D_EVENT, "expose: window: %lu\n", e->xexpose.window);
5475 }
5476
5477 void
5478 keypress(XEvent *e)
5479 {
5480 unsigned int i;
5481 KeySym keysym;
5482 XKeyEvent *ev = &e->xkey;
5483
5484 DNPRINTF(SWM_D_EVENT, "keypress: window: %lu\n", ev->window);
5485
5486 keysym = XKeycodeToKeysym(display, (KeyCode)ev->keycode, 0);
5487 for (i = 0; i < keys_length; i++)
5488 if (keysym == keys[i].keysym
5489 && CLEANMASK(keys[i].mod) == CLEANMASK(ev->state)
5490 && keyfuncs[keys[i].funcid].func) {
5491 if (keys[i].funcid == kf_spawn_custom)
5492 spawn_custom(
5493 root_to_region(ev->root),
5494 &(keyfuncs[keys[i].funcid].args),
5495 keys[i].spawn_name
5496 );
5497 else
5498 keyfuncs[keys[i].funcid].func(
5499 root_to_region(ev->root),
5500 &(keyfuncs[keys[i].funcid].args)
5501 );
5502 }
5503 }
5504
5505 void
5506 buttonpress(XEvent *e)
5507 {
5508 struct ws_win *win;
5509 int i, action;
5510 XButtonPressedEvent *ev = &e->xbutton;
5511
5512 DNPRINTF(SWM_D_EVENT, "buttonpress: window: %lu\n", ev->window);
5513
5514 if ((win = find_window(ev->window)) == NULL)
5515 return;
5516
5517 focus_magic(win);
5518 action = client_click;
5519
5520 for (i = 0; i < LENGTH(buttons); i++)
5521 if (action == buttons[i].action && buttons[i].func &&
5522 buttons[i].button == ev->button &&
5523 CLEANMASK(buttons[i].mask) == CLEANMASK(ev->state))
5524 buttons[i].func(win, &buttons[i].args);
5525 }
5526
5527 void
5528 configurerequest(XEvent *e)
5529 {
5530 XConfigureRequestEvent *ev = &e->xconfigurerequest;
5531 struct ws_win *win;
5532 int new = 0;
5533 XWindowChanges wc;
5534
5535 if ((win = find_window(ev->window)) == NULL)
5536 if ((win = find_unmanaged_window(ev->window)) == NULL)
5537 new = 1;
5538
5539 if (new) {
5540 DNPRINTF(SWM_D_EVENT, "configurerequest: new window: %lu\n",
5541 ev->window);
5542 bzero(&wc, sizeof wc);
5543 wc.x = ev->x;
5544 wc.y = ev->y;
5545 wc.width = ev->width;
5546 wc.height = ev->height;
5547 wc.border_width = ev->border_width;
5548 wc.sibling = ev->above;
5549 wc.stack_mode = ev->detail;
5550 XConfigureWindow(display, ev->window, ev->value_mask, &wc);
5551 } else {
5552 DNPRINTF(SWM_D_EVENT, "configurerequest: change window: %lu\n",
5553 ev->window);
5554 config_win(win, ev);
5555 }
5556 }
5557
5558 void
5559 configurenotify(XEvent *e)
5560 {
5561 struct ws_win *win;
5562 long mask;
5563
5564 DNPRINTF(SWM_D_EVENT, "configurenotify: window: %lu\n",
5565 e->xconfigure.window);
5566
5567 win = find_window(e->xconfigure.window);
5568 if (win) {
5569 XGetWMNormalHints(display, win->id, &win->sh, &mask);
5570 adjust_font(win);
5571 if (font_adjusted)
5572 stack();
5573 if (focus_mode == SWM_FOCUS_DEFAULT)
5574 drain_enter_notify();
5575 }
5576 }
5577
5578 void
5579 destroynotify(XEvent *e)
5580 {
5581 struct ws_win *win;
5582 XDestroyWindowEvent *ev = &e->xdestroywindow;
5583
5584 DNPRINTF(SWM_D_EVENT, "destroynotify: window %lu\n", ev->window);
5585
5586 if ((win = find_window(ev->window)) == NULL) {
5587 if ((win = find_unmanaged_window(ev->window)) == NULL)
5588 return;
5589 free_window(win);
5590 return;
5591 }
5592
5593 /* make sure we focus on something */
5594 win->floating = 0;
5595
5596 unmanage_window(win);
5597 stack();
5598 if (focus_mode == SWM_FOCUS_DEFAULT)
5599 drain_enter_notify();
5600 free_window(win);
5601 }
5602
5603 void
5604 enternotify(XEvent *e)
5605 {
5606 XCrossingEvent *ev = &e->xcrossing;
5607 XEvent cne;
5608 struct ws_win *win;
5609 #if 0
5610 struct ws_win *w;
5611 Window focus_return;
5612 int revert_to_return;
5613 #endif
5614 DNPRINTF(SWM_D_FOCUS, "enternotify: window: %lu mode %d detail %d root "
5615 "%lu subwindow %lu same_screen %d focus %d state %d\n",
5616 ev->window, ev->mode, ev->detail, ev->root, ev->subwindow,
5617 ev->same_screen, ev->focus, ev->state);
5618
5619 switch (focus_mode) {
5620 case SWM_FOCUS_DEFAULT:
5621 break;
5622 case SWM_FOCUS_FOLLOW:
5623 break;
5624 case SWM_FOCUS_SYNERGY:
5625 #if 0
5626 /*
5627 * all these checks need to be in this order because the
5628 * XCheckTypedWindowEvent relies on weeding out the previous events
5629 *
5630 * making this code an option would enable a follow mouse for focus
5631 * feature
5632 */
5633
5634 /*
5635 * state is set when we are switching workspaces and focus is set when
5636 * the window or a subwindow already has focus (occurs during restart).
5637 *
5638 * Only honor the focus flag if last_focus_event is not FocusOut,
5639 * this allows scrotwm to continue to control focus when another
5640 * program is also playing with it.
5641 */
5642 if (ev->state || (ev->focus && last_focus_event != FocusOut)) {
5643 DNPRINTF(SWM_D_EVENT, "ignoring enternotify: focus\n");
5644 return;
5645 }
5646
5647 /*
5648 * happens when a window is created or destroyed and the border
5649 * crosses the mouse pointer and when switching ws
5650 *
5651 * we need the subwindow test to see if we came from root in order
5652 * to give focus to floaters
5653 */
5654 if (ev->mode == NotifyNormal && ev->detail == NotifyVirtual &&
5655 ev->subwindow == 0) {
5656 DNPRINTF(SWM_D_EVENT, "ignoring enternotify: NotifyVirtual\n");
5657 return;
5658 }
5659
5660 /* this window already has focus */
5661 if (ev->mode == NotifyNormal && ev->detail == NotifyInferior) {
5662 DNPRINTF(SWM_D_EVENT, "ignoring enternotify: win has focus\n");
5663 return;
5664 }
5665
5666 /* this window is being deleted or moved to another ws */
5667 if (XCheckTypedWindowEvent(display, ev->window, ConfigureNotify,
5668 &cne) == True) {
5669 DNPRINTF(SWM_D_EVENT, "ignoring enternotify: configurenotify\n");
5670 XPutBackEvent(display, &cne);
5671 return;
5672 }
5673
5674 if ((win = find_window(ev->window)) == NULL) {
5675 DNPRINTF(SWM_D_EVENT, "ignoring enternotify: win == NULL\n");
5676 return;
5677 }
5678
5679 /*
5680 * In fullstack kill all enters unless they come from a different ws
5681 * (i.e. another region) or focus has been grabbed externally.
5682 */
5683 if (win->ws->cur_layout->flags & SWM_L_FOCUSPREV &&
5684 last_focus_event != FocusOut) {
5685 XGetInputFocus(display, &focus_return, &revert_to_return);
5686 if ((w = find_window(focus_return)) == NULL ||
5687 w->ws == win->ws) {
5688 DNPRINTF(SWM_D_EVENT, "ignoring event: fullstack\n");
5689 return;
5690 }
5691 }
5692 #endif
5693 break;
5694 }
5695
5696 if ((win = find_window(ev->window)) == NULL) {
5697 DNPRINTF(SWM_D_EVENT, "ignoring enternotify: win == NULL\n");
5698 return;
5699 }
5700
5701 /*
5702 * if we have more enternotifies let them handle it in due time
5703 */
5704 if (XCheckTypedEvent(display, EnterNotify, &cne) == True) {
5705 DNPRINTF(SWM_D_EVENT,
5706 "ignoring enternotify: got more enternotify\n");
5707 XPutBackEvent(display, &cne);
5708 return;
5709 }
5710
5711 focus_magic(win);
5712 }
5713
5714 /* lets us use one switch statement for arbitrary mode/detail combinations */
5715 #define MERGE_MEMBERS(a,b) (((a & 0xffff) << 16) | (b & 0xffff))
5716
5717 void
5718 focusevent(XEvent *e)
5719 {
5720 #if 0
5721 struct ws_win *win;
5722 u_int32_t mode_detail;
5723 XFocusChangeEvent *ev = &e->xfocus;
5724
5725 DNPRINTF(SWM_D_EVENT, "focusevent: %s window: %lu mode %d detail %d\n",
5726 ev->type == FocusIn ? "entering" : "leaving",
5727 ev->window, ev->mode, ev->detail);
5728
5729 if (last_focus_event == ev->type) {
5730 DNPRINTF(SWM_D_FOCUS, "ignoring focusevent: bad ordering\n");
5731 return;
5732 }
5733
5734 last_focus_event = ev->type;
5735 mode_detail = MERGE_MEMBERS(ev->mode, ev->detail);
5736
5737 switch (mode_detail) {
5738 /* synergy client focus operations */
5739 case MERGE_MEMBERS(NotifyNormal, NotifyNonlinear):
5740 case MERGE_MEMBERS(NotifyNormal, NotifyNonlinearVirtual):
5741
5742 /* synergy server focus operations */
5743 case MERGE_MEMBERS(NotifyWhileGrabbed, NotifyNonlinear):
5744
5745 /* Entering applications like rdesktop that mangle the pointer */
5746 case MERGE_MEMBERS(NotifyNormal, NotifyPointer):
5747
5748 if ((win = find_window(e->xfocus.window)) != NULL && win->ws->r)
5749 XSetWindowBorder(display, win->id,
5750 win->ws->r->s->c[ev->type == FocusIn ?
5751 SWM_S_COLOR_FOCUS : SWM_S_COLOR_UNFOCUS].color);
5752 break;
5753 default:
5754 fprintf(stderr, "ignoring focusevent\n");
5755 DNPRINTF(SWM_D_FOCUS, "ignoring focusevent\n");
5756 break;
5757 }
5758 #endif
5759 }
5760
5761 void
5762 mapnotify(XEvent *e)
5763 {
5764 struct ws_win *win;
5765 XMapEvent *ev = &e->xmap;
5766
5767 DNPRINTF(SWM_D_EVENT, "mapnotify: window: %lu\n", ev->window);
5768
5769 win = manage_window(ev->window);
5770 if (win)
5771 set_win_state(win, NormalState);
5772 }
5773
5774 void
5775 mappingnotify(XEvent *e)
5776 {
5777 XMappingEvent *ev = &e->xmapping;
5778
5779 XRefreshKeyboardMapping(ev);
5780 if (ev->request == MappingKeyboard)
5781 grabkeys();
5782 }
5783
5784 void
5785 maprequest(XEvent *e)
5786 {
5787 struct ws_win *win;
5788 struct swm_region *r;
5789 XWindowAttributes wa;
5790 XMapRequestEvent *ev = &e->xmaprequest;
5791
5792 DNPRINTF(SWM_D_EVENT, "maprequest: window: %lu\n",
5793 e->xmaprequest.window);
5794
5795 if (!XGetWindowAttributes(display, ev->window, &wa))
5796 return;
5797 if (wa.override_redirect)
5798 return;
5799
5800 win = manage_window(e->xmaprequest.window);
5801 if (win == NULL)
5802 return; /* can't happen */
5803
5804 stack();
5805
5806 /* make new win focused */
5807 r = root_to_region(win->wa.root);
5808 if (win->ws == r->ws)
5809 focus_magic(win);
5810 }
5811
5812 void
5813 propertynotify(XEvent *e)
5814 {
5815 struct ws_win *win;
5816 XPropertyEvent *ev = &e->xproperty;
5817
5818 DNPRINTF(SWM_D_EVENT, "propertynotify: window: %lu\n",
5819 ev->window);
5820
5821 win = find_window(ev->window);
5822 if (win == NULL)
5823 return;
5824
5825 if (ev->state == PropertyDelete && ev->atom == a_swm_iconic) {
5826 update_iconic(win, 0);
5827 XMapRaised(display, win->id);
5828 stack();
5829 focus_win(win);
5830 return;
5831 }
5832
5833 switch (ev->atom) {
5834 case XA_WM_NORMAL_HINTS:
5835 #if 0
5836 long mask;
5837 XGetWMNormalHints(display, win->id, &win->sh, &mask);
5838 fprintf(stderr, "normal hints: flag 0x%x\n", win->sh.flags);
5839 if (win->sh.flags & PMinSize) {
5840 win->g.w = win->sh.min_width;
5841 win->g.h = win->sh.min_height;
5842 fprintf(stderr, "min %d %d\n", win->g.w, win->g.h);
5843 }
5844 XMoveResizeWindow(display, win->id,
5845 win->g.x, win->g.y, win->g.w, win->g.h);
5846 #endif
5847 if (window_name_enabled)
5848 bar_update();
5849 break;
5850 default:
5851 break;
5852 }
5853 }
5854
5855 void
5856 unmapnotify(XEvent *e)
5857 {
5858 struct ws_win *win;
5859
5860 DNPRINTF(SWM_D_EVENT, "unmapnotify: window: %lu\n", e->xunmap.window);
5861
5862 /* determine if we need to help unmanage this window */
5863 win = find_window(e->xunmap.window);
5864 if (win == NULL)
5865 return;
5866
5867 if (getstate(e->xunmap.window) == NormalState) {
5868 unmanage_window(win);
5869 stack();
5870
5871 /* giant hack for apps that don't destroy transient windows */
5872 /* eat a bunch of events to prevent remanaging the window */
5873 XEvent cne;
5874 while (XCheckWindowEvent(display, e->xunmap.window,
5875 EnterWindowMask, &cne))
5876 ;
5877 while (XCheckWindowEvent(display, e->xunmap.window,
5878 StructureNotifyMask, &cne))
5879 ;
5880 while (XCheckWindowEvent(display, e->xunmap.window,
5881 SubstructureNotifyMask, &cne))
5882 ;
5883 /* resend unmap because we ated it */
5884 XUnmapWindow(display, e->xunmap.window);
5885 }
5886
5887 if (focus_mode == SWM_FOCUS_DEFAULT)
5888 drain_enter_notify();
5889 }
5890
5891 void
5892 visibilitynotify(XEvent *e)
5893 {
5894 int i;
5895 struct swm_region *r;
5896
5897 DNPRINTF(SWM_D_EVENT, "visibilitynotify: window: %lu\n",
5898 e->xvisibility.window);
5899 if (e->xvisibility.state == VisibilityUnobscured)
5900 for (i = 0; i < ScreenCount(display); i++)
5901 TAILQ_FOREACH(r, &screens[i].rl, entry)
5902 if (e->xvisibility.window == r->bar_window)
5903 bar_update();
5904 }
5905
5906 void
5907 clientmessage(XEvent *e)
5908 {
5909 XClientMessageEvent *ev;
5910 struct ws_win *win;
5911
5912 ev = &e->xclient;
5913
5914 win = find_window(ev->window);
5915 if (win == NULL)
5916 return;
5917
5918 DNPRINTF(SWM_D_EVENT, "clientmessage: window: 0x%lx type: %ld \n",
5919 ev->window, ev->message_type);
5920
5921 if (ev->message_type == ewmh[_NET_ACTIVE_WINDOW].atom) {
5922 DNPRINTF(SWM_D_EVENT, "clientmessage: _NET_ACTIVE_WINDOW \n");
5923 focus_win(win);
5924 }
5925 if (ev->message_type == ewmh[_NET_CLOSE_WINDOW].atom) {
5926 DNPRINTF(SWM_D_EVENT, "clientmessage: _NET_CLOSE_WINDOW \n");
5927 if (win->can_delete)
5928 client_msg(win, adelete);
5929 else
5930 XKillClient(display, win->id);
5931 }
5932 if (ev->message_type == ewmh[_NET_MOVERESIZE_WINDOW].atom) {
5933 DNPRINTF(SWM_D_EVENT,
5934 "clientmessage: _NET_MOVERESIZE_WINDOW \n");
5935 if (win->floating) {
5936 if (ev->data.l[0] & (1<<8)) /* x */
5937 win->g.x = ev->data.l[1];
5938 if (ev->data.l[0] & (1<<9)) /* y */
5939 win->g.y = ev->data.l[2];
5940 if (ev->data.l[0] & (1<<10)) /* width */
5941 win->g.w = ev->data.l[3];
5942 if (ev->data.l[0] & (1<<11)) /* height */
5943 win->g.h = ev->data.l[4];
5944 }
5945 else {
5946 /* TODO: Change stack sizes */
5947 }
5948 config_win(win, NULL);
5949 }
5950 if (ev->message_type == ewmh[_NET_WM_STATE].atom) {
5951 DNPRINTF(SWM_D_EVENT, "clientmessage: _NET_WM_STATE \n");
5952 ewmh_update_win_state(win, ev->data.l[1], ev->data.l[0]);
5953 if (ev->data.l[2])
5954 ewmh_update_win_state(win, ev->data.l[2],
5955 ev->data.l[0]);
5956
5957 stack();
5958 }
5959 }
5960
5961 int
5962 xerror_start(Display *d, XErrorEvent *ee)
5963 {
5964 other_wm = 1;
5965 return (-1);
5966 }
5967
5968 int
5969 xerror(Display *d, XErrorEvent *ee)
5970 {
5971 /* fprintf(stderr, "error: %p %p\n", display, ee); */
5972 return (-1);
5973 }
5974
5975 int
5976 active_wm(void)
5977 {
5978 other_wm = 0;
5979 xerrorxlib = XSetErrorHandler(xerror_start);
5980
5981 /* this causes an error if some other window manager is running */
5982 XSelectInput(display, DefaultRootWindow(display),
5983 SubstructureRedirectMask);
5984 XSync(display, False);
5985 if (other_wm)
5986 return (1);
5987
5988 XSetErrorHandler(xerror);
5989 XSync(display, False);
5990 return (0);
5991 }
5992
5993 void
5994 new_region(struct swm_screen *s, int x, int y, int w, int h)
5995 {
5996 struct swm_region *r, *n;
5997 struct workspace *ws = NULL;
5998 int i;
5999
6000 DNPRINTF(SWM_D_MISC, "new region: screen[%d]:%dx%d+%d+%d\n",
6001 s->idx, w, h, x, y);
6002
6003 /* remove any conflicting regions */
6004 n = TAILQ_FIRST(&s->rl);
6005 while (n) {
6006 r = n;
6007 n = TAILQ_NEXT(r, entry);
6008 if (X(r) < (x + w) &&
6009 (X(r) + WIDTH(r)) > x &&
6010 Y(r) < (y + h) &&
6011 (Y(r) + HEIGHT(r)) > y) {
6012 if (r->ws->r != NULL)
6013 r->ws->old_r = r->ws->r;
6014 r->ws->r = NULL;
6015 XDestroyWindow(display, r->bar_window);
6016 TAILQ_REMOVE(&s->rl, r, entry);
6017 TAILQ_INSERT_TAIL(&s->orl, r, entry);
6018 }
6019 }
6020
6021 /* search old regions for one to reuse */
6022
6023 /* size + location match */
6024 TAILQ_FOREACH(r, &s->orl, entry)
6025 if (X(r) == x && Y(r) == y &&
6026 HEIGHT(r) == h && WIDTH(r) == w)
6027 break;
6028
6029 /* size match */
6030 TAILQ_FOREACH(r, &s->orl, entry)
6031 if (HEIGHT(r) == h && WIDTH(r) == w)
6032 break;
6033
6034 if (r != NULL) {
6035 TAILQ_REMOVE(&s->orl, r, entry);
6036 /* try to use old region's workspace */
6037 if (r->ws->r == NULL)
6038 ws = r->ws;
6039 } else
6040 if ((r = calloc(1, sizeof(struct swm_region))) == NULL)
6041 errx(1, "calloc: failed to allocate memory for screen");
6042
6043 /* if we don't have a workspace already, find one */
6044 if (ws == NULL) {
6045 for (i = 0; i < SWM_WS_MAX; i++)
6046 if (s->ws[i].r == NULL) {
6047 ws = &s->ws[i];
6048 break;
6049 }
6050 }
6051
6052 if (ws == NULL)
6053 errx(1, "no free workspaces\n");
6054
6055 X(r) = x;
6056 Y(r) = y;
6057 WIDTH(r) = w;
6058 HEIGHT(r) = h;
6059 r->s = s;
6060 r->ws = ws;
6061 r->ws_prior = NULL;
6062 ws->r = r;
6063 outputs++;
6064 TAILQ_INSERT_TAIL(&s->rl, r, entry);
6065 }
6066
6067 void
6068 scan_xrandr(int i)
6069 {
6070 #ifdef SWM_XRR_HAS_CRTC
6071 XRRCrtcInfo *ci;
6072 XRRScreenResources *sr;
6073 int c;
6074 int ncrtc = 0;
6075 #endif /* SWM_XRR_HAS_CRTC */
6076 struct swm_region *r;
6077
6078
6079 if (i >= ScreenCount(display))
6080 errx(1, "invalid screen");
6081
6082 /* remove any old regions */
6083 while ((r = TAILQ_FIRST(&screens[i].rl)) != NULL) {
6084 r->ws->old_r = r->ws->r = NULL;
6085 XDestroyWindow(display, r->bar_window);
6086 TAILQ_REMOVE(&screens[i].rl, r, entry);
6087 TAILQ_INSERT_TAIL(&screens[i].orl, r, entry);
6088 }
6089 outputs = 0;
6090
6091 /* map virtual screens onto physical screens */
6092 #ifdef SWM_XRR_HAS_CRTC
6093 if (xrandr_support) {
6094 sr = XRRGetScreenResources(display, screens[i].root);
6095 if (sr == NULL)
6096 new_region(&screens[i], 0, 0,
6097 DisplayWidth(display, i),
6098 DisplayHeight(display, i));
6099 else
6100 ncrtc = sr->ncrtc;
6101
6102 for (c = 0, ci = NULL; c < ncrtc; c++) {
6103 ci = XRRGetCrtcInfo(display, sr, sr->crtcs[c]);
6104 if (ci->noutput == 0)
6105 continue;
6106
6107 if (ci != NULL && ci->mode == None)
6108 new_region(&screens[i], 0, 0,
6109 DisplayWidth(display, i),
6110 DisplayHeight(display, i));
6111 else
6112 new_region(&screens[i],
6113 ci->x, ci->y, ci->width, ci->height);
6114 }
6115 if (ci)
6116 XRRFreeCrtcInfo(ci);
6117 XRRFreeScreenResources(sr);
6118 } else
6119 #endif /* SWM_XRR_HAS_CRTC */
6120 {
6121 new_region(&screens[i], 0, 0, DisplayWidth(display, i),
6122 DisplayHeight(display, i));
6123 }
6124 }
6125
6126 void
6127 screenchange(XEvent *e) {
6128 XRRScreenChangeNotifyEvent *xe = (XRRScreenChangeNotifyEvent *)e;
6129 struct swm_region *r;
6130 int i;
6131
6132 DNPRINTF(SWM_D_EVENT, "screenchange: %lu\n", xe->root);
6133
6134 if (!XRRUpdateConfiguration(e))
6135 return;
6136
6137 /* silly event doesn't include the screen index */
6138 for (i = 0; i < ScreenCount(display); i++)
6139 if (screens[i].root == xe->root)
6140 break;
6141 if (i >= ScreenCount(display))
6142 errx(1, "screenchange: screen not found\n");
6143
6144 /* brute force for now, just re-enumerate the regions */
6145 scan_xrandr(i);
6146
6147 /* add bars to all regions */
6148 for (i = 0; i < ScreenCount(display); i++)
6149 TAILQ_FOREACH(r, &screens[i].rl, entry)
6150 bar_setup(r);
6151 stack();
6152 if (focus_mode == SWM_FOCUS_DEFAULT)
6153 drain_enter_notify();
6154 }
6155
6156 void
6157 grab_windows(void)
6158 {
6159 Window d1, d2, *wins = NULL;
6160 XWindowAttributes wa;
6161 unsigned int no;
6162 int i, j;
6163 long state, manage;
6164
6165 for (i = 0; i < ScreenCount(display); i++) {
6166 if (!XQueryTree(display, screens[i].root, &d1, &d2, &wins, &no))
6167 continue;
6168
6169 /* attach windows to a region */
6170 /* normal windows */
6171 for (j = 0; j < no; j++) {
6172 if (!XGetWindowAttributes(display, wins[j], &wa) ||
6173 wa.override_redirect ||
6174 XGetTransientForHint(display, wins[j], &d1))
6175 continue;
6176
6177 state = getstate(wins[j]);
6178 manage = state == IconicState;
6179 if (wa.map_state == IsViewable || manage)
6180 manage_window(wins[j]);
6181 }
6182 /* transient windows */
6183 for (j = 0; j < no; j++) {
6184 if (!XGetWindowAttributes(display, wins[j], &wa) ||
6185 wa.override_redirect)
6186 continue;
6187
6188 state = getstate(wins[j]);
6189 manage = state == IconicState;
6190 if (XGetTransientForHint(display, wins[j], &d1) &&
6191 manage)
6192 manage_window(wins[j]);
6193 }
6194 if (wins) {
6195 XFree(wins);
6196 wins = NULL;
6197 }
6198 }
6199 }
6200
6201 void
6202 setup_screens(void)
6203 {
6204 int i, j, k;
6205 int errorbase, major, minor;
6206 struct workspace *ws;
6207
6208 if ((screens = calloc(ScreenCount(display),
6209 sizeof(struct swm_screen))) == NULL)
6210 errx(1, "calloc: screens");
6211
6212 /* initial Xrandr setup */
6213 xrandr_support = XRRQueryExtension(display,
6214 &xrandr_eventbase, &errorbase);
6215 if (xrandr_support)
6216 if (XRRQueryVersion(display, &major, &minor) && major < 1)
6217 xrandr_support = 0;
6218
6219 /* map physical screens */
6220 for (i = 0; i < ScreenCount(display); i++) {
6221 DNPRINTF(SWM_D_WS, "setup_screens: init screen %d\n", i);
6222 screens[i].idx = i;
6223 TAILQ_INIT(&screens[i].rl);
6224 TAILQ_INIT(&screens[i].orl);
6225 screens[i].root = RootWindow(display, i);
6226
6227 /* set default colors */
6228 setscreencolor("red", i + 1, SWM_S_COLOR_FOCUS);
6229 setscreencolor("rgb:88/88/88", i + 1, SWM_S_COLOR_UNFOCUS);
6230 setscreencolor("rgb:00/80/80", i + 1, SWM_S_COLOR_BAR_BORDER);
6231 setscreencolor("black", i + 1, SWM_S_COLOR_BAR);
6232 setscreencolor("rgb:a0/a0/a0", i + 1, SWM_S_COLOR_BAR_FONT);
6233
6234 /* set default cursor */
6235 XDefineCursor(display, screens[i].root,
6236 XCreateFontCursor(display, XC_left_ptr));
6237
6238 /* init all workspaces */
6239 /* XXX these should be dynamically allocated too */
6240 for (j = 0; j < SWM_WS_MAX; j++) {
6241 ws = &screens[i].ws[j];
6242 ws->idx = j;
6243 ws->focus = NULL;
6244 ws->r = NULL;
6245 ws->old_r = NULL;
6246 TAILQ_INIT(&ws->winlist);
6247 TAILQ_INIT(&ws->unmanagedlist);
6248
6249 for (k = 0; layouts[k].l_stack != NULL; k++)
6250 if (layouts[k].l_config != NULL)
6251 layouts[k].l_config(ws,
6252 SWM_ARG_ID_STACKINIT);
6253 ws->cur_layout = &layouts[0];
6254 ws->cur_layout->l_string(ws);
6255 }
6256
6257 scan_xrandr(i);
6258
6259 if (xrandr_support)
6260 XRRSelectInput(display, screens[i].root,
6261 RRScreenChangeNotifyMask);
6262 }
6263 }
6264
6265 void
6266 setup_globals(void)
6267 {
6268 if ((bar_fonts[0] = strdup("-*-terminus-medium-*-*-*-*-*-*-*-*-*-*-*"))
6269 == NULL)
6270 err(1, "setup_globals: strdup");
6271 if ((bar_fonts[1] = strdup("-*-times-medium-r-*-*-*-*-*-*-*-*-*-*"))
6272 == NULL)
6273 err(1, "setup_globals: strdup");
6274 if ((bar_fonts[2] = strdup("-misc-fixed-medium-r-*-*-*-*-*-*-*-*-*-*"))
6275 == NULL)
6276 err(1, "setup_globals: strdup");
6277 if ((spawn_term[0] = strdup("xterm")) == NULL)
6278 err(1, "setup_globals: strdup");
6279 if ((clock_format = strdup("%a %b %d %R %Z %Y")) == NULL)
6280 errx(1, "strdup");
6281 }
6282
6283 void
6284 workaround(void)
6285 {
6286 int i;
6287 Atom netwmcheck, netwmname, utf8_string;
6288 Window root, win;
6289
6290 /* work around sun jdk bugs, code from wmname */
6291 netwmcheck = XInternAtom(display, "_NET_SUPPORTING_WM_CHECK", False);
6292 netwmname = XInternAtom(display, "_NET_WM_NAME", False);
6293 utf8_string = XInternAtom(display, "UTF8_STRING", False);
6294 for (i = 0; i < ScreenCount(display); i++) {
6295 root = screens[i].root;
6296 win = XCreateSimpleWindow(display,root, 0, 0, 1, 1, 0,
6297 screens[i].c[SWM_S_COLOR_UNFOCUS].color,
6298 screens[i].c[SWM_S_COLOR_UNFOCUS].color);
6299
6300 XChangeProperty(display, root, netwmcheck, XA_WINDOW, 32,
6301 PropModeReplace, (unsigned char *)&win,1);
6302 XChangeProperty(display, win, netwmcheck, XA_WINDOW, 32,
6303 PropModeReplace, (unsigned char *)&win,1);
6304 XChangeProperty(display, win, netwmname, utf8_string, 8,
6305 PropModeReplace, (unsigned char*)"LG3D", strlen("LG3D"));
6306 }
6307 }
6308
6309 int
6310 main(int argc, char *argv[])
6311 {
6312 struct swm_region *r, *rr;
6313 struct ws_win *winfocus = NULL;
6314 struct timeval tv;
6315 union arg a;
6316 char conf[PATH_MAX], *cfile = NULL;
6317 struct stat sb;
6318 XEvent e;
6319 int xfd, i;
6320 fd_set rd;
6321 struct sigaction sact;
6322
6323 start_argv = argv;
6324 fprintf(stderr, "Welcome to scrotwm V%s cvs tag: %s\n",
6325 SWM_VERSION, cvstag);
6326 if (!setlocale(LC_CTYPE, "") || !XSupportsLocale())
6327 warnx("no locale support");
6328
6329 if (!(display = XOpenDisplay(0)))
6330 errx(1, "can not open display");
6331
6332 if (active_wm())
6333 errx(1, "other wm running");
6334
6335 /* handle some signals */
6336 bzero(&sact, sizeof(sact));
6337 sigemptyset(&sact.sa_mask);
6338 sact.sa_flags = 0;
6339 sact.sa_handler = sighdlr;
6340 sigaction(SIGINT, &sact, NULL);
6341 sigaction(SIGQUIT, &sact, NULL);
6342 sigaction(SIGTERM, &sact, NULL);
6343 sigaction(SIGHUP, &sact, NULL);
6344
6345 sact.sa_handler = sighdlr;
6346 sact.sa_flags = SA_NOCLDSTOP;
6347 sigaction(SIGCHLD, &sact, NULL);
6348
6349 astate = XInternAtom(display, "WM_STATE", False);
6350 aprot = XInternAtom(display, "WM_PROTOCOLS", False);
6351 adelete = XInternAtom(display, "WM_DELETE_WINDOW", False);
6352 takefocus = XInternAtom(display, "WM_TAKE_FOCUS", False);
6353 a_wmname = XInternAtom(display, "WM_NAME", False);
6354 a_utf8_string = XInternAtom(display, "UTF8_STRING", False);
6355 a_string = XInternAtom(display, "STRING", False);
6356 a_swm_iconic = XInternAtom(display, "_SWM_ICONIC", False);
6357
6358 /* look for local and global conf file */
6359 pwd = getpwuid(getuid());
6360 if (pwd == NULL)
6361 errx(1, "invalid user %d", getuid());
6362
6363 setup_screens();
6364 setup_globals();
6365 setup_keys();
6366 setup_quirks();
6367 setup_spawn();
6368
6369 /* load config */
6370 snprintf(conf, sizeof conf, "%s/.%s", pwd->pw_dir, SWM_CONF_FILE);
6371 if (stat(conf, &sb) != -1) {
6372 if (S_ISREG(sb.st_mode))
6373 cfile = conf;
6374 } else {
6375 /* try global conf file */
6376 snprintf(conf, sizeof conf, "/etc/%s", SWM_CONF_FILE);
6377 if (!stat(conf, &sb))
6378 if (S_ISREG(sb.st_mode))
6379 cfile = conf;
6380 }
6381 if (cfile)
6382 conf_load(cfile, SWM_CONF_DEFAULT);
6383
6384 setup_ewmh();
6385 /* set some values to work around bad programs */
6386 workaround();
6387 /* grab existing windows (before we build the bars) */
6388 grab_windows();
6389
6390 if (getenv("SWM_STARTED") == NULL)
6391 setenv("SWM_STARTED", "YES", 1);
6392
6393 /* setup all bars */
6394 for (i = 0; i < ScreenCount(display); i++)
6395 TAILQ_FOREACH(r, &screens[i].rl, entry) {
6396 if (winfocus == NULL)
6397 winfocus = TAILQ_FIRST(&r->ws->winlist);
6398 bar_setup(r);
6399 }
6400
6401 unfocus_all();
6402
6403 grabkeys();
6404 stack();
6405 if (focus_mode == SWM_FOCUS_DEFAULT)
6406 drain_enter_notify();
6407
6408 xfd = ConnectionNumber(display);
6409 while (running) {
6410 while (XPending(display)) {
6411 XNextEvent(display, &e);
6412 if (running == 0)
6413 goto done;
6414 if (e.type < LASTEvent) {
6415 dumpevent(&e);
6416 if (handler[e.type])
6417 handler[e.type](&e);
6418 else
6419 DNPRINTF(SWM_D_EVENT,
6420 "win: %lu unknown event: %d\n",
6421 e.xany.window, e.type);
6422 } else {
6423 switch (e.type - xrandr_eventbase) {
6424 case RRScreenChangeNotify:
6425 screenchange(&e);
6426 break;
6427 default:
6428 DNPRINTF(SWM_D_EVENT,
6429 "win: %lu unknown xrandr event: "
6430 "%d\n", e.xany.window, e.type);
6431 break;
6432 }
6433 }
6434 }
6435
6436 /* if we are being restarted go focus on first window */
6437 if (winfocus) {
6438 rr = winfocus->ws->r;
6439 if (rr == NULL) {
6440 /* not a visible window */
6441 winfocus = NULL;
6442 continue;
6443 }
6444 /* move pointer to first screen if multi screen */
6445 if (ScreenCount(display) > 1 || outputs > 1)
6446 XWarpPointer(display, None, rr->s[0].root,
6447 0, 0, 0, 0, rr->g.x,
6448 rr->g.y + (bar_enabled ? bar_height : 0));
6449
6450 a.id = SWM_ARG_ID_FOCUSCUR;
6451 focus(rr, &a);
6452 winfocus = NULL;
6453 continue;
6454 }
6455
6456 FD_ZERO(&rd);
6457 FD_SET(xfd, &rd);
6458 tv.tv_sec = 1;
6459 tv.tv_usec = 0;
6460 if (select(xfd + 1, &rd, NULL, NULL, &tv) == -1)
6461 if (errno != EINTR)
6462 DNPRINTF(SWM_D_MISC, "select failed");
6463 if (restart_wm == 1)
6464 restart(NULL, NULL);
6465 if (search_resp == 1)
6466 search_do_resp();
6467 if (running == 0)
6468 goto done;
6469 if (bar_alarm) {
6470 bar_alarm = 0;
6471 bar_update();
6472 }
6473 }
6474 done:
6475 teardown_ewmh();
6476 bar_extra_stop();
6477 XFreeGC(display, bar_gc);
6478 XCloseDisplay(display);
6479
6480 return (0);
6481 }