]> code.delx.au - spectrwm/blob - scrotwm.c
Make floaters work right. This fixes the remaining issues.
[spectrwm] / scrotwm.c
1 /* $scrotwm$ */
2 /*
3 * Copyright (c) 2009 Marco Peereboom <marco@peereboom.us>
4 * Copyright (c) 2009 Ryan McBride <mcbride@countersiege.com>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18 /*
19 * Much code and ideas taken from dwm under the following license:
20 * MIT/X Consortium License
21 *
22 * 2006-2008 Anselm R Garbe <garbeam at gmail dot com>
23 * 2006-2007 Sander van Dijk <a dot h dot vandijk at gmail dot com>
24 * 2006-2007 Jukka Salmi <jukka at salmi dot ch>
25 * 2007 Premysl Hruby <dfenze at gmail dot com>
26 * 2007 Szabolcs Nagy <nszabolcs at gmail dot com>
27 * 2007 Christof Musik <christof at sendfax dot de>
28 * 2007-2008 Enno Gottox Boland <gottox at s01 dot de>
29 * 2007-2008 Peter Hartlich <sgkkr at hartlich dot com>
30 * 2008 Martin Hurton <martin dot hurton at gmail dot com>
31 *
32 * Permission is hereby granted, free of charge, to any person obtaining a
33 * copy of this software and associated documentation files (the "Software"),
34 * to deal in the Software without restriction, including without limitation
35 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
36 * and/or sell copies of the Software, and to permit persons to whom the
37 * Software is furnished to do so, subject to the following conditions:
38 *
39 * The above copyright notice and this permission notice shall be included in
40 * all copies or substantial portions of the Software.
41 *
42 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
43 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
44 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
45 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
46 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
47 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
48 * DEALINGS IN THE SOFTWARE.
49 */
50
51 #define SWM_VERSION "0.5"
52
53 #include <stdio.h>
54 #include <stdlib.h>
55 #include <err.h>
56 #include <locale.h>
57 #include <unistd.h>
58 #include <time.h>
59 #include <signal.h>
60 #include <string.h>
61 #include <util.h>
62 #include <pwd.h>
63
64 #include <sys/types.h>
65 #include <sys/stat.h>
66 #include <sys/wait.h>
67 #include <sys/queue.h>
68 #include <sys/param.h>
69
70 #include <X11/cursorfont.h>
71 #include <X11/keysym.h>
72 #include <X11/Xatom.h>
73 #include <X11/Xlib.h>
74 #include <X11/Xproto.h>
75 #include <X11/Xutil.h>
76
77 /* #define SWM_DEBUG */
78 #ifdef SWM_DEBUG
79 #define DPRINTF(x...) do { if (swm_debug) fprintf(stderr, x); } while(0)
80 #define DNPRINTF(n,x...) do { if (swm_debug & n) fprintf(stderr, x); } while(0)
81 #define SWM_D_MISC 0x0001
82 #define SWM_D_EVENT 0x0002
83 #define SWM_D_WS 0x0004
84 #define SWM_D_FOCUS 0x0008
85 #define SWM_D_MOVE 0x0010
86
87 u_int32_t swm_debug = 0
88 | SWM_D_MISC
89 | SWM_D_EVENT
90 | SWM_D_WS
91 | SWM_D_FOCUS
92 | SWM_D_MOVE
93 ;
94 #else
95 #define DPRINTF(x...)
96 #define DNPRINTF(n,x...)
97 #endif
98
99 #define LENGTH(x) (sizeof x / sizeof x[0])
100 #define MODKEY Mod1Mask
101 #define CLEANMASK(mask) (mask & ~(numlockmask | LockMask))
102
103 #define WIDTH ws[current_ws].g.w
104 #define HEIGHT ws[current_ws].g.h
105
106 char **start_argv;
107 Atom astate;
108 int (*xerrorxlib)(Display *, XErrorEvent *);
109 int other_wm;
110 int screen;
111 int running = 1;
112 int ignore_enter = 0;
113 unsigned int numlockmask = 0;
114 unsigned long color_focus = 0xff0000; /* XXX should this be per ws? */
115 unsigned long color_unfocus = 0x888888;
116 Display *display;
117 Window root;
118
119 /* status bar */
120 int bar_enabled = 1;
121 int bar_height = 0;
122 unsigned long bar_border = 0x008080;
123 unsigned long bar_color = 0x000000;
124 unsigned long bar_font_color = 0xa0a0a0;
125 Window bar_window;
126 GC bar_gc;
127 XGCValues bar_gcv;
128 XFontStruct *bar_fs;
129 char bar_text[128];
130 char *bar_fonts[] = {
131 "-*-terminus-*-*-*-*-*-*-*-*-*-*-*-*",
132 "-*-times-medium-r-*-*-*-*-*-*-*-*-*-*",
133 NULL
134 };
135
136 /* terminal + args */
137 char *spawn_term[] = { "xterm", NULL };
138 char *spawn_menu[] = { "dmenu_run", NULL };
139
140 /* layout manager data */
141 struct swm_geometry {
142 int x;
143 int y;
144 int w;
145 int h;
146 };
147
148
149 struct ws_win {
150 TAILQ_ENTRY(ws_win) entry;
151 Window id;
152 struct swm_geometry g;
153 int floating;
154 int transient;
155 XWindowAttributes wa;
156 };
157 TAILQ_HEAD(ws_win_list, ws_win);
158
159 /* layout handlers */
160 void stack(void);
161 void vertical_init(int);
162 void vertical_resize(int);
163 void vertical_stack(struct swm_geometry *);
164 void horizontal_init(int);
165 void horizontal_resize(int);
166 void horizontal_stack(struct swm_geometry *);
167 void max_init(int);
168 void max_focus(struct ws_win *);
169 void max_stack(struct swm_geometry *);
170
171 struct layout {
172 void (*l_init)(int); /* init/reset */
173 void (*l_stack)(struct swm_geometry *);
174 void (*l_resize)(int);
175 void (*l_focus)(struct ws_win *);
176 } layouts[] = {
177 /* init stack, resize */
178 { vertical_init, vertical_stack, vertical_resize, NULL},
179 { horizontal_init, horizontal_stack, horizontal_resize, NULL},
180 { NULL, max_stack, NULL, max_focus},
181 { NULL, NULL, NULL, NULL},
182 };
183
184
185 /* define work spaces */
186 #define SWM_WS_MAX (10)
187 struct workspace {
188 int visible; /* workspace visible */
189 int restack; /* restack on switch */
190 struct swm_geometry g;
191 struct layout *cur_layout; /* current layout handlers */
192 struct ws_win *focus; /* which win has focus */
193 struct ws_win_list winlist; /* list of windows in ws */
194 } ws[SWM_WS_MAX];
195 int current_ws = 0;
196
197 /* args to functions */
198 union arg {
199 int id;
200 #define SWM_ARG_ID_FOCUSNEXT (0)
201 #define SWM_ARG_ID_FOCUSPREV (1)
202 #define SWM_ARG_ID_FOCUSMAIN (2)
203 #define SWM_ARG_ID_SWAPNEXT (3)
204 #define SWM_ARG_ID_SWAPPREV (4)
205 #define SWM_ARG_ID_SWAPMAIN (5)
206 #define SWM_ARG_ID_MASTERSHRINK (6)
207 #define SWM_ARG_ID_MASTERGROW (7)
208 char **argv;
209 };
210
211 /* conf file stuff */
212 #define SWM_CONF_WS "\n= \t"
213 #define SWM_CONF_FILE "scrotwm.conf"
214 int
215 conf_load(char *filename)
216 {
217 FILE *config;
218 char *line, *cp, *var, *val;
219 size_t len, lineno = 0;
220
221 DNPRINTF(SWM_D_MISC, "conf_load: filename %s\n", filename);
222
223 if (filename == NULL)
224 return (1);
225
226 if ((config = fopen(filename, "r")) == NULL)
227 return (1);
228
229 for (;;) {
230 if ((line = fparseln(config, &len, &lineno, NULL, 0)) == NULL)
231 if (feof(config))
232 break;
233 cp = line;
234 cp += (long)strspn(cp, SWM_CONF_WS);
235 if (cp[0] == '\0') {
236 /* empty line */
237 free(line);
238 continue;
239 }
240 if ((var = strsep(&cp, SWM_CONF_WS)) == NULL || cp == NULL)
241 break;
242 cp += (long)strspn(cp, SWM_CONF_WS);
243 if ((val = strsep(&cp, SWM_CONF_WS)) == NULL)
244 break;
245
246 DNPRINTF(SWM_D_MISC, "conf_load: %s=%s\n",var ,val);
247 switch (var[0]) {
248 case 'b':
249 if (!strncmp(var, "bar_enabled", strlen("bar_enabled")))
250 bar_enabled = atoi(val);
251 else if (!strncmp(var, "bar_border",
252 strlen("bar_border")))
253 bar_border = strtol(val, NULL, 16);
254 else if (!strncmp(var, "bar_color",
255 strlen("bar_color")))
256 bar_color = strtol(val, NULL, 16);
257 else if (!strncmp(var, "bar_font_color",
258 strlen("bar_font_color")))
259 bar_font_color = strtol(val, NULL, 16);
260 else if (!strncmp(var, "bar_font", strlen("bar_font")))
261 asprintf(&bar_fonts[0], "%s", val);
262 else
263 goto bad;
264 break;
265
266 case 'c':
267 if (!strncmp(var, "color_focus", strlen("color_focus")))
268 color_focus = strtol(val, NULL, 16);
269 else if (!strncmp(var, "color_unfocus",
270 strlen("color_unfocus")))
271 color_unfocus = strtol(val, NULL, 16);
272 else
273 goto bad;
274 break;
275
276 case 's':
277 if (!strncmp(var, "spawn_term", strlen("spawn_term")))
278 asprintf(&spawn_term[0], "%s", val); /* XXX args? */
279 break;
280 default:
281 goto bad;
282 }
283 free(line);
284 }
285
286 fclose(config);
287 return (0);
288 bad:
289 errx(1, "invalid conf file entry: %s=%s", var, val);
290 }
291
292 void
293 bar_print(void)
294 {
295 time_t tmt;
296 struct tm tm;
297
298 if (bar_enabled == 0)
299 return;
300
301 /* clear old text */
302 XSetForeground(display, bar_gc, bar_color);
303 XDrawString(display, bar_window, bar_gc, 4, bar_fs->ascent, bar_text,
304 strlen(bar_text));
305
306 /* draw new text */
307 time(&tmt);
308 localtime_r(&tmt, &tm);
309 strftime(bar_text, sizeof bar_text, "%a %b %d %R %Z %Y", &tm);
310 XSetForeground(display, bar_gc, bar_font_color);
311 XDrawString(display, bar_window, bar_gc, 4, bar_fs->ascent, bar_text,
312 strlen(bar_text));
313 XSync(display, False);
314
315 alarm(60);
316 }
317
318 void
319 bar_signal(int sig)
320 {
321 /* XXX yeah yeah byte me */
322 bar_print();
323 }
324
325 void
326 bar_toggle(union arg *args)
327 {
328 int i;
329
330 DNPRINTF(SWM_D_MISC, "bar_toggle\n");
331
332 if (bar_enabled) {
333 bar_enabled = 0;
334 XUnmapWindow(display, bar_window);
335 } else {
336 bar_enabled = 1;
337 XMapRaised(display, bar_window);
338 }
339 XSync(display, False);
340 for (i = 0; i < SWM_WS_MAX; i++)
341 ws[i].restack = 1;
342
343 stack();
344 bar_print(); /* must be after stack */
345 }
346
347 void
348 bar_setup(void)
349 {
350 int i;
351
352 for (i = 0; bar_fonts[i] != NULL; i++) {
353 bar_fs = XLoadQueryFont(display, bar_fonts[i]);
354 if (bar_fs)
355 break;
356 }
357 if (bar_fonts[i] == NULL)
358 errx(1, "couldn't load font");
359 bar_height = bar_fs->ascent + bar_fs->descent + 3;
360
361 bar_window = XCreateSimpleWindow(display, root, 0, 0,
362 WIDTH, bar_height - 2, 1, bar_border, bar_color);
363 bar_gc = XCreateGC(display, bar_window, 0, &bar_gcv);
364 XSetFont(display, bar_gc, bar_fs->fid);
365 XSelectInput(display, bar_window, VisibilityChangeMask);
366 if (bar_enabled) {
367 XMapRaised(display, bar_window);
368 }
369 DNPRINTF(SWM_D_MISC, "bar_setup: bar_window %d\n", (int)bar_window);
370
371 if (signal(SIGALRM, bar_signal) == SIG_ERR)
372 err(1, "could not install bar_signal");
373 bar_print();
374 }
375
376 void
377 config_win(struct ws_win *win)
378 {
379 XConfigureEvent ce;
380
381 DNPRINTF(SWM_D_MISC, "config_win: win %lu x %d y %d w %d h %d\n",
382 win->id, win->g.x, win->g.y, win->g.w, win->g.h);
383 ce.type = ConfigureNotify;
384 ce.display = display;
385 ce.event = win->id;
386 ce.window = win->id;
387 ce.x = win->g.x;
388 ce.y = win->g.y;
389 ce.width = win->g.w;
390 ce.height = win->g.h;
391 ce.border_width = 1; /* XXX store this! */
392 ce.above = None;
393 ce.override_redirect = False;
394 XSendEvent(display, win->id, False, StructureNotifyMask, (XEvent *)&ce);
395 }
396
397 int
398 count_win(int wsid, int count_transient)
399 {
400 struct ws_win *win;
401 int count = 0;
402
403 TAILQ_FOREACH (win, &ws[wsid].winlist, entry) {
404 if (count_transient == 0 && win->floating)
405 continue;
406 if (count_transient == 0 && win->transient)
407 continue;
408 count++;
409 }
410 DNPRINTF(SWM_D_MISC, "count_win: %d\n", count);
411
412 return (count);
413 }
414 void
415 quit(union arg *args)
416 {
417 DNPRINTF(SWM_D_MISC, "quit\n");
418 running = 0;
419 }
420
421 void
422 restart(union arg *args)
423 {
424 DNPRINTF(SWM_D_MISC, "restart: %s\n", start_argv[0]);
425
426 XCloseDisplay(display);
427 execvp(start_argv[0], start_argv);
428 fprintf(stderr, "execvp failed\n");
429 perror(" failed");
430 quit(NULL);
431 }
432
433 void
434 spawn(union arg *args)
435 {
436 DNPRINTF(SWM_D_MISC, "spawn: %s\n", args->argv[0]);
437 /*
438 * The double-fork construct avoids zombie processes and keeps the code
439 * clean from stupid signal handlers.
440 */
441 if (fork() == 0) {
442 if (fork() == 0) {
443 if (display)
444 close(ConnectionNumber(display));
445 setsid();
446 execvp(args->argv[0], args->argv);
447 fprintf(stderr, "execvp failed\n");
448 perror(" failed");
449 }
450 exit(0);
451 }
452 wait(0);
453 }
454
455 void
456 focus_win(struct ws_win *win)
457 {
458 DNPRINTF(SWM_D_FOCUS, "focus_win: id: %lu\n", win->id);
459 XSetWindowBorder(display, win->id, color_focus);
460 XSetInputFocus(display, win->id, RevertToPointerRoot, CurrentTime);
461 ws[current_ws].focus = win;
462 }
463
464 void
465 unfocus_win(struct ws_win *win)
466 {
467 DNPRINTF(SWM_D_FOCUS, "unfocus_win: id: %lu\n", win->id);
468 XSetWindowBorder(display, win->id, color_unfocus);
469 if (ws[current_ws].focus == win)
470 ws[current_ws].focus = NULL;
471 }
472
473 void
474 switchws(union arg *args)
475 {
476 int wsid = args->id;
477 struct ws_win *win;
478
479 DNPRINTF(SWM_D_WS, "switchws: %d\n", wsid + 1);
480
481 if (wsid == current_ws)
482 return;
483
484 /* map new window first to prevent ugly blinking */
485 TAILQ_FOREACH (win, &ws[wsid].winlist, entry)
486 XMapRaised(display, win->id);
487 ws[wsid].visible = 1;
488
489 TAILQ_FOREACH (win, &ws[current_ws].winlist, entry)
490 XUnmapWindow(display, win->id);
491 ws[current_ws].visible = 0;
492
493 current_ws = wsid;
494
495 ignore_enter = 1;
496 if (ws[wsid].restack) {
497 stack();
498 bar_print();
499 } else {
500 if (ws[wsid].focus != NULL)
501 focus_win(ws[wsid].focus);
502 XSync(display, False);
503 }
504 }
505
506 void
507 swapwin(union arg *args)
508 {
509 struct ws_win *target;
510
511 DNPRINTF(SWM_D_MOVE, "swapwin: id %d\n", args->id);
512 if (ws[current_ws].focus == NULL)
513 return;
514
515 switch (args->id) {
516 case SWM_ARG_ID_SWAPPREV:
517 target = TAILQ_PREV(ws[current_ws].focus, ws_win_list, entry);
518 TAILQ_REMOVE(&ws[current_ws].winlist,
519 ws[current_ws].focus, entry);
520 if (target == NULL)
521 TAILQ_INSERT_TAIL(&ws[current_ws].winlist,
522 ws[current_ws].focus, entry);
523 else
524 TAILQ_INSERT_BEFORE(target, ws[current_ws].focus,
525 entry);
526 break;
527 case SWM_ARG_ID_SWAPNEXT:
528 target = TAILQ_NEXT(ws[current_ws].focus, entry);
529 TAILQ_REMOVE(&ws[current_ws].winlist,
530 ws[current_ws].focus, entry);
531 if (target == NULL)
532 TAILQ_INSERT_HEAD(&ws[current_ws].winlist,
533 ws[current_ws].focus, entry);
534 else
535 TAILQ_INSERT_AFTER(&ws[current_ws].winlist, target,
536 ws[current_ws].focus, entry);
537 break;
538 case SWM_ARG_ID_SWAPMAIN:
539 target = TAILQ_FIRST(&ws[current_ws].winlist);
540 if (target == ws[current_ws].focus)
541 return;
542 TAILQ_REMOVE(&ws[current_ws].winlist, target, entry);
543 TAILQ_INSERT_BEFORE(ws[current_ws].focus, target, entry);
544 TAILQ_REMOVE(&ws[current_ws].winlist,
545 ws[current_ws].focus, entry);
546 TAILQ_INSERT_HEAD(&ws[current_ws].winlist,
547 ws[current_ws].focus, entry);
548 break;
549 default:
550 DNPRINTF(SWM_D_MOVE, "invalid id: %d\n", args->id);
551 return;
552 }
553
554 ignore_enter = 2;
555 stack();
556 }
557
558 void
559 focus(union arg *args)
560 {
561 struct ws_win *winfocus, *winlostfocus;
562
563 DNPRINTF(SWM_D_FOCUS, "focus: id %d\n", args->id);
564 if (ws[current_ws].focus == NULL)
565 return;
566
567 winlostfocus = ws[current_ws].focus;
568
569 switch (args->id) {
570 case SWM_ARG_ID_FOCUSPREV:
571 winfocus = TAILQ_PREV(ws[current_ws].focus, ws_win_list, entry);
572 if (winfocus == NULL)
573 winfocus =
574 TAILQ_LAST(&ws[current_ws].winlist, ws_win_list);
575 break;
576
577 case SWM_ARG_ID_FOCUSNEXT:
578 winfocus = TAILQ_NEXT(ws[current_ws].focus, entry);
579 if (winfocus == NULL)
580 winfocus = TAILQ_FIRST(&ws[current_ws].winlist);
581 break;
582
583 case SWM_ARG_ID_FOCUSMAIN:
584 winfocus = TAILQ_FIRST(&ws[current_ws].winlist);
585 break;
586
587 default:
588 return;
589 }
590
591 if (winfocus == winlostfocus)
592 return;
593
594 unfocus_win(winlostfocus);
595 focus_win(winfocus);
596 /* XXX if we hook in focus_win(), we get a nasty cycle */
597 if (ws[current_ws].cur_layout->l_focus != NULL)
598 ws[current_ws].cur_layout->l_focus(winfocus);
599 XSync(display, False);
600 }
601
602 void
603 cycle_layout(union arg *args)
604 {
605 DNPRINTF(SWM_D_EVENT, "cycle_layout: workspace: %d\n", current_ws);
606
607 ws[current_ws].cur_layout++;
608 if (ws[current_ws].cur_layout->l_stack == NULL)
609 ws[current_ws].cur_layout = &layouts[0];
610 ignore_enter = 1;
611
612 stack();
613 }
614
615 void
616 resize_master(union arg *args)
617 {
618 DNPRINTF(SWM_D_EVENT, "resize_master: id %d\n", args->id);
619
620 if (ws[current_ws].cur_layout->l_resize != NULL)
621 ws[current_ws].cur_layout->l_resize(args->id);
622 }
623
624 void
625 stack_reset(union arg *args)
626 {
627 DNPRINTF(SWM_D_EVENT, "stack_reset: ws %d\n", current_ws);
628
629 if (ws[current_ws].cur_layout->l_init != NULL) {
630 ws[current_ws].cur_layout->l_init(current_ws);
631 stack();
632 }
633 }
634
635 void
636 stack(void) {
637 struct swm_geometry g;
638 DNPRINTF(SWM_D_EVENT, "stack: workspace: %d\n", current_ws);
639
640 /* start with workspace geometry, adjust for bar */
641 g = ws[current_ws].g;
642 if (bar_enabled) {
643 g.y += bar_height;
644 g.h -= bar_height;
645 }
646
647 ws[current_ws].restack = 0;
648 ws[current_ws].cur_layout->l_stack(&g);
649 XSync(display, False);
650 }
651
652 void
653 stack_floater(struct ws_win *win)
654 {
655 unsigned int mask;
656 XWindowChanges wc;
657
658 bzero(&wc, sizeof wc);
659 mask = CWX | CWY | CWBorderWidth | CWWidth | CWHeight;
660 wc.border_width = 1;
661 if (win->transient) {
662 /* XXX dialog window, make it bigger but not too big */
663 win->g.w *= 2;
664 win->g.h *= 2;
665 }
666 wc.width = win->g.w;
667 wc.height = win->g.h;
668 wc.x = (WIDTH - win->g.w) / 2;
669 wc.y = (HEIGHT - win->g.h) / 2;
670
671 DNPRINTF(SWM_D_EVENT, "stack_floater: win %d x %d y %d w %d h %d\n",
672 win, wc.x, wc.y, wc.width, wc.height);
673
674 XConfigureWindow(display, win->id, mask, &wc);
675 }
676
677
678 int vertical_msize[SWM_WS_MAX];
679
680 void
681 vertical_init(int ws_idx)
682 {
683 DNPRINTF(SWM_D_MISC, "vertical_init: workspace: %d\n", current_ws);
684
685 vertical_msize[ws_idx] = ws[ws_idx].g.w / 2;
686 }
687
688 void
689 vertical_resize(int id)
690 {
691 DNPRINTF(SWM_D_MISC, "vertical_resize: workspace: %d\n", current_ws);
692
693 switch (id) {
694 case SWM_ARG_ID_MASTERSHRINK:
695 vertical_msize[current_ws] -= WIDTH / 32;
696 if ( vertical_msize[current_ws] < WIDTH / 16)
697 vertical_msize[current_ws] = WIDTH / 16;
698 break;
699 case SWM_ARG_ID_MASTERGROW:
700 vertical_msize[current_ws] += WIDTH / 32;
701 if ( vertical_msize[current_ws] >
702 (WIDTH - (WIDTH / 16)))
703 vertical_msize[current_ws] =
704 WIDTH - WIDTH / 16;
705 break;
706 default:
707 return;
708 }
709 stack();
710 }
711
712 void
713 vertical_stack(struct swm_geometry *g) {
714 XWindowChanges wc;
715 struct swm_geometry gg = *g;
716 struct ws_win wf, *win, *winfocus = &wf;
717 int i, hrh, winno;
718 unsigned int mask;
719
720 DNPRINTF(SWM_D_EVENT, "vertical_stack: workspace: %d\n", current_ws);
721
722 winno = count_win(current_ws, 0);
723 if (winno == 0)
724 return;
725 winfocus->id = root;
726
727 if (winno > 1)
728 gg.w = vertical_msize[current_ws];
729
730 if (winno > 2)
731 hrh = g->h / (winno - 1);
732 else
733 hrh = 0;
734
735 i = 0;
736 TAILQ_FOREACH (win, &ws[current_ws].winlist, entry) {
737 if (i == 1) {
738 gg.x += vertical_msize[current_ws] + 2;
739 gg.w = g->w - (vertical_msize[current_ws] + 2);
740 }
741 if (i != 0 && hrh != 0) {
742 /* correct the last window for lost pixels */
743 if (win == TAILQ_LAST(&ws[current_ws].winlist,
744 ws_win_list)) {
745 gg.h = hrh + (g->h - (i * hrh));
746 gg.y += hrh;
747 } else {
748 gg.h = hrh - 2;
749 /* leave first right hand window at y = 0 */
750 if (i > 1)
751 gg.y += gg.h + 2;
752 }
753 }
754
755 if (win->transient != 0 || win->floating != 0)
756 stack_floater(win);
757 else {
758 bzero(&wc, sizeof wc);
759 wc.border_width = 1;
760 win->g.x = wc.x = gg.x;
761 win->g.y = wc.y = gg.y;
762 win->g.w = wc.width = gg.w;
763 win->g.h = wc.height = gg.h;
764 mask = CWX | CWY | CWWidth | CWHeight | CWBorderWidth;
765 XConfigureWindow(display, win->id, mask, &wc);
766 }
767
768 if (win == ws[current_ws].focus)
769 winfocus = win;
770 else
771 unfocus_win(win);
772 XMapRaised(display, win->id);
773 i++;
774 }
775
776 focus_win(winfocus); /* this has to be done outside of the loop */
777 }
778
779 int horizontal_msize[SWM_WS_MAX];
780
781 void
782 horizontal_init(int ws_idx)
783 {
784 DNPRINTF(SWM_D_MISC, "horizontal_init: workspace: %d\n", current_ws);
785
786 horizontal_msize[ws_idx] = ws[ws_idx].g.h / 2;
787 }
788
789 void
790 horizontal_resize(int id)
791 {
792 DNPRINTF(SWM_D_MISC, "horizontal_resize: workspace: %d\n", current_ws);
793
794 switch (id) {
795 case SWM_ARG_ID_MASTERSHRINK:
796 horizontal_msize[current_ws] -= HEIGHT / 32;
797 if ( horizontal_msize[current_ws] < HEIGHT / 16)
798 horizontal_msize[current_ws] = HEIGHT / 16;
799 break;
800 case SWM_ARG_ID_MASTERGROW:
801 horizontal_msize[current_ws] += HEIGHT / 32;
802 if ( horizontal_msize[current_ws] >
803 (HEIGHT - (HEIGHT / 16)))
804 horizontal_msize[current_ws] =
805 HEIGHT - HEIGHT / 16;
806 break;
807 default:
808 return;
809 }
810 stack();
811 }
812
813 void
814 horizontal_stack(struct swm_geometry *g) {
815 XWindowChanges wc;
816 struct swm_geometry gg = *g;
817 struct ws_win wf, *win, *winfocus = &wf;
818 int i, hrw, winno;
819 unsigned int mask;
820
821 DNPRINTF(SWM_D_EVENT, "horizontal_stack: workspace: %d\n", current_ws);
822
823 winno = count_win(current_ws, 0);
824 if (winno == 0)
825 return;
826 winfocus->id = root;
827
828 if (winno > 1)
829 gg.h = horizontal_msize[current_ws];
830
831 if (winno > 2)
832 hrw = g->w / (winno - 1);
833 else
834 hrw = 0;
835
836 i = 0;
837 TAILQ_FOREACH (win, &ws[current_ws].winlist, entry) {
838 if (i == 1) {
839 gg.y += horizontal_msize[current_ws] + 2;
840 gg.h = g->h - (horizontal_msize[current_ws] + 2);
841 }
842 if (i != 0 && hrw != 0) {
843 /* correct the last window for lost pixels */
844 if (win == TAILQ_LAST(&ws[current_ws].winlist,
845 ws_win_list)) {
846 gg.w = hrw + (g->w - (i * hrw));
847 gg.x += hrw;
848 } else {
849 gg.w = hrw - 2;
850 /* leave first bottom window at x = 0 */
851 if (i > 1)
852 gg.x += gg.w + 2;
853 }
854 }
855
856 if (win->transient != 0 || win->floating != 0)
857 stack_floater(win);
858 else {
859 bzero(&wc, sizeof wc);
860 wc.border_width = 1;
861 win->g.x = wc.x = gg.x;
862 win->g.y = wc.y = gg.y;
863 win->g.w = wc.width = gg.w;
864 win->g.h = wc.height = gg.h;
865 mask = CWX | CWY | CWWidth | CWHeight | CWBorderWidth;
866 XConfigureWindow(display, win->id, mask, &wc);
867 }
868
869 if (win == ws[current_ws].focus)
870 winfocus = win;
871 else
872 unfocus_win(win);
873 XMapRaised(display, win->id);
874 i++;
875 }
876
877 focus_win(winfocus); /* this has to be done outside of the loop */
878 }
879
880 void
881 max_focus(struct ws_win *fwin)
882 {
883 struct ws_win *win;
884
885 TAILQ_FOREACH (win, &ws[current_ws].winlist, entry) {
886 if (win->transient == 0 && win->floating == 0 && win == fwin)
887 XMapRaised(display, win->id);
888 else
889 XUnmapWindow(display, win->id);
890 }
891 }
892
893 /* fullscreen view */
894 void
895 max_stack(struct swm_geometry *g) {
896 XWindowChanges wc;
897 struct swm_geometry gg = *g;
898 struct ws_win *win, *winfocus;
899 unsigned int mask;
900
901 DNPRINTF(SWM_D_EVENT, "max_stack: workspace: %d\n", current_ws);
902
903 if (count_win(current_ws, 0) == 0)
904 return;
905
906 winfocus = ws[current_ws].focus;
907 if (!winfocus)
908 winfocus = TAILQ_FIRST(&ws[current_ws].winlist);
909
910 TAILQ_FOREACH (win, &ws[current_ws].winlist, entry) {
911 if (win->transient != 0 || win->floating != 0) {
912 if (win == winfocus) {
913 stack_floater(win); /* XXX maximize? */
914 XMapRaised(display, win->id);
915 } else
916 XUnmapWindow(display, win->id);
917 } else {
918 bzero(&wc, sizeof wc);
919 wc.border_width = 1;
920 win->g.x = wc.x = gg.x;
921 win->g.y = wc.y = gg.y;
922 win->g.w = wc.width = gg.w;
923 win->g.h = wc.height = gg.h;
924 mask = CWX | CWY | CWWidth | CWHeight | CWBorderWidth;
925 XConfigureWindow(display, win->id, mask, &wc);
926
927 if (winfocus == win)
928 XMapRaised(display, win->id);
929 else
930 XUnmapWindow(display, win->id);
931 }
932 }
933
934 focus_win(winfocus); /* this has to be done outside of the loop */
935 }
936
937 void
938 send_to_ws(union arg *args)
939 {
940 int wsid = args->id;
941 struct ws_win *win = ws[current_ws].focus;
942
943 DNPRINTF(SWM_D_MOVE, "send_to_ws: win: %lu\n", win->id);
944
945 XUnmapWindow(display, win->id);
946
947 /* find a window to focus */
948 ws[current_ws].focus = TAILQ_PREV(win, ws_win_list, entry);
949 if (ws[current_ws].focus == NULL)
950 ws[current_ws].focus = TAILQ_FIRST(&ws[current_ws].winlist);
951 if (ws[current_ws].focus == win)
952 ws[current_ws].focus = NULL;
953
954 TAILQ_REMOVE(&ws[current_ws].winlist, win, entry);
955
956 TAILQ_INSERT_TAIL(&ws[wsid].winlist, win, entry);
957 if (count_win(wsid, 1) == 1)
958 ws[wsid].focus = win;
959 ws[wsid].restack = 1;
960
961 stack();
962 }
963
964 /* key definitions */
965 struct key {
966 unsigned int mod;
967 KeySym keysym;
968 void (*func)(union arg *);
969 union arg args;
970 } keys[] = {
971 /* modifier key function argument */
972 { MODKEY, XK_space, cycle_layout, {0} },
973 { MODKEY | ShiftMask, XK_space, stack_reset, {0} },
974 { MODKEY, XK_h, resize_master, {.id = SWM_ARG_ID_MASTERSHRINK} },
975 { MODKEY, XK_l, resize_master, {.id = SWM_ARG_ID_MASTERGROW} },
976 { MODKEY, XK_Return, swapwin, {.id = SWM_ARG_ID_SWAPMAIN} },
977 { MODKEY, XK_j, focus, {.id = SWM_ARG_ID_FOCUSNEXT} },
978 { MODKEY, XK_k, focus, {.id = SWM_ARG_ID_FOCUSPREV} },
979 { MODKEY | ShiftMask, XK_j, swapwin, {.id = SWM_ARG_ID_SWAPNEXT} },
980 { MODKEY | ShiftMask, XK_k, swapwin, {.id = SWM_ARG_ID_SWAPPREV} },
981 { MODKEY | ShiftMask, XK_Return, spawn, {.argv = spawn_term} },
982 { MODKEY, XK_p, spawn, {.argv = spawn_menu} },
983 { MODKEY | ShiftMask, XK_q, quit, {0} },
984 { MODKEY, XK_q, restart, {0} },
985 { MODKEY, XK_m, focus, {.id = SWM_ARG_ID_FOCUSMAIN} },
986 { MODKEY, XK_1, switchws, {.id = 0} },
987 { MODKEY, XK_2, switchws, {.id = 1} },
988 { MODKEY, XK_3, switchws, {.id = 2} },
989 { MODKEY, XK_4, switchws, {.id = 3} },
990 { MODKEY, XK_5, switchws, {.id = 4} },
991 { MODKEY, XK_6, switchws, {.id = 5} },
992 { MODKEY, XK_7, switchws, {.id = 6} },
993 { MODKEY, XK_8, switchws, {.id = 7} },
994 { MODKEY, XK_9, switchws, {.id = 8} },
995 { MODKEY, XK_0, switchws, {.id = 9} },
996 { MODKEY | ShiftMask, XK_1, send_to_ws, {.id = 0} },
997 { MODKEY | ShiftMask, XK_2, send_to_ws, {.id = 1} },
998 { MODKEY | ShiftMask, XK_3, send_to_ws, {.id = 2} },
999 { MODKEY | ShiftMask, XK_4, send_to_ws, {.id = 3} },
1000 { MODKEY | ShiftMask, XK_5, send_to_ws, {.id = 4} },
1001 { MODKEY | ShiftMask, XK_6, send_to_ws, {.id = 5} },
1002 { MODKEY | ShiftMask, XK_7, send_to_ws, {.id = 6} },
1003 { MODKEY | ShiftMask, XK_8, send_to_ws, {.id = 7} },
1004 { MODKEY | ShiftMask, XK_9, send_to_ws, {.id = 8} },
1005 { MODKEY | ShiftMask, XK_0, send_to_ws, {.id = 9} },
1006 { MODKEY, XK_b, bar_toggle, {0} },
1007 { MODKEY, XK_Tab, focus, {.id = SWM_ARG_ID_FOCUSNEXT} },
1008 { MODKEY | ShiftMask, XK_Tab, focus, {.id = SWM_ARG_ID_FOCUSPREV} },
1009 };
1010
1011 void
1012 updatenumlockmask(void)
1013 {
1014 unsigned int i, j;
1015 XModifierKeymap *modmap;
1016
1017 DNPRINTF(SWM_D_MISC, "updatenumlockmask\n");
1018 numlockmask = 0;
1019 modmap = XGetModifierMapping(display);
1020 for (i = 0; i < 8; i++)
1021 for (j = 0; j < modmap->max_keypermod; j++)
1022 if (modmap->modifiermap[i * modmap->max_keypermod + j]
1023 == XKeysymToKeycode(display, XK_Num_Lock))
1024 numlockmask = (1 << i);
1025
1026 XFreeModifiermap(modmap);
1027 }
1028
1029 void
1030 grabkeys(void)
1031 {
1032 unsigned int i, j;
1033 KeyCode code;
1034 unsigned int modifiers[] =
1035 { 0, LockMask, numlockmask, numlockmask | LockMask };
1036
1037 DNPRINTF(SWM_D_MISC, "grabkeys\n");
1038 updatenumlockmask();
1039
1040 XUngrabKey(display, AnyKey, AnyModifier, root);
1041 for (i = 0; i < LENGTH(keys); i++) {
1042 if ((code = XKeysymToKeycode(display, keys[i].keysym)))
1043 for (j = 0; j < LENGTH(modifiers); j++)
1044 XGrabKey(display, code,
1045 keys[i].mod | modifiers[j], root,
1046 True, GrabModeAsync, GrabModeAsync);
1047 }
1048 }
1049 void
1050 expose(XEvent *e)
1051 {
1052 DNPRINTF(SWM_D_EVENT, "expose: window: %lu\n", e->xexpose.window);
1053 }
1054
1055 void
1056 keypress(XEvent *e)
1057 {
1058 unsigned int i;
1059 KeySym keysym;
1060 XKeyEvent *ev = &e->xkey;
1061
1062 DNPRINTF(SWM_D_EVENT, "keypress: window: %lu\n", ev->window);
1063
1064 keysym = XKeycodeToKeysym(display, (KeyCode)ev->keycode, 0);
1065 for (i = 0; i < LENGTH(keys); i++)
1066 if (keysym == keys[i].keysym
1067 && CLEANMASK(keys[i].mod) == CLEANMASK(ev->state)
1068 && keys[i].func)
1069 keys[i].func(&(keys[i].args));
1070 }
1071
1072 void
1073 buttonpress(XEvent *e)
1074 {
1075 XButtonPressedEvent *ev = &e->xbutton;
1076 #ifdef SWM_CLICKTOFOCUS
1077 struct ws_win *win;
1078 #endif
1079
1080
1081 DNPRINTF(SWM_D_EVENT, "buttonpress: window: %lu\n", ev->window);
1082
1083 if (ev->window == root)
1084 return;
1085 if (ev->window == ws[current_ws].focus->id)
1086 return;
1087 #ifdef SWM_CLICKTOFOCUS
1088 TAILQ_FOREACH(win, &ws[current_ws].winlist, entry)
1089 if (win->id == ev->window) {
1090 /* focus in the clicked window */
1091 XSetWindowBorder(display, ev->window, 0xff0000);
1092 XSetWindowBorder(display,
1093 ws[current_ws].focus->id, 0x888888);
1094 XSetInputFocus(display, ev->window, RevertToPointerRoot,
1095 CurrentTime);
1096 ws[current_ws].focus = win;
1097 XSync(display, False);
1098 break;
1099 }
1100 #endif
1101 }
1102
1103 void
1104 set_win_state(struct ws_win *win, long state)
1105 {
1106 long data[] = {state, None};
1107
1108 DNPRINTF(SWM_D_EVENT, "set_win_state: window: %lu\n", win->id);
1109
1110 XChangeProperty(display, win->id, astate, astate, 32, PropModeReplace,
1111 (unsigned char *)data, 2);
1112 }
1113
1114 struct ws_win *
1115 manage_window(Window id)
1116 {
1117 Window trans;
1118 struct ws_win *win;
1119 XClassHint ch;
1120
1121 TAILQ_FOREACH (win, &ws[current_ws].winlist, entry) {
1122 if (win->id == id)
1123 return (win); /* already being managed */
1124 }
1125
1126 if ((win = calloc(1, sizeof(struct ws_win))) == NULL)
1127 errx(1, "calloc: failed to allocate memory for new window");
1128
1129 win->id = id;
1130 TAILQ_INSERT_TAIL(&ws[current_ws].winlist, win, entry);
1131 ws[current_ws].focus = win; /* make new win focused */
1132
1133 XGetTransientForHint(display, win->id, &trans);
1134 if (trans) {
1135 win->transient = trans;
1136 DNPRINTF(SWM_D_MISC, "manage_window: win %u transient %u\n",
1137 (unsigned)win->id, win->transient);
1138 }
1139 XGetWindowAttributes(display, win->id, &win->wa);
1140 win->g.w = win->wa.width;
1141 win->g.h = win->wa.height;
1142 win->g.x = win->wa.x;
1143 win->g.y = win->wa.y;
1144
1145 /* XXX make this a table */
1146 bzero(&ch, sizeof ch);
1147 if (XGetClassHint(display, win->id, &ch)) {
1148 /*fprintf(stderr, "class: %s name: %s\n", ch.res_class, ch.res_name); */
1149 if (!strcmp(ch.res_class, "MPlayer") && !strcmp(ch.res_name, "xv")) {
1150 win->floating = 1;
1151 }
1152 if (ch.res_class)
1153 XFree(ch.res_class);
1154 if (ch.res_name)
1155 XFree(ch.res_name);
1156 }
1157
1158 XSelectInput(display, id, EnterWindowMask | FocusChangeMask |
1159 PropertyChangeMask | StructureNotifyMask);
1160
1161 set_win_state(win, NormalState);
1162
1163 return (win);
1164 }
1165
1166 void
1167 configurerequest(XEvent *e)
1168 {
1169 XConfigureRequestEvent *ev = &e->xconfigurerequest;
1170 struct ws_win *win;
1171 int new = 1;
1172 XWindowChanges wc;
1173
1174 TAILQ_FOREACH (win, &ws[current_ws].winlist, entry) {
1175 if (win->id == ev->window) {
1176 new = 0;
1177 break;
1178 }
1179 }
1180
1181 if (new) {
1182 DNPRINTF(SWM_D_EVENT, "configurerequest: new window: %lu\n",
1183 ev->window);
1184 bzero(&wc, sizeof wc);
1185 wc.x = ev->x;
1186 wc.y = ev->y;
1187 wc.width = ev->width;
1188 wc.height = ev->height;
1189 wc.border_width = ev->border_width;
1190 wc.sibling = ev->above;
1191 wc.stack_mode = ev->detail;
1192 XConfigureWindow(display, ev->window, ev->value_mask, &wc);
1193 } else {
1194 DNPRINTF(SWM_D_EVENT, "configurerequest: change window: %lu\n",
1195 ev->window);
1196 if(win->floating) {
1197 if(ev->value_mask & CWX)
1198 win->g.x = ev->x;
1199 if(ev->value_mask & CWY)
1200 win->g.y = ev->y;
1201 if(ev->value_mask & CWWidth)
1202 win->g.w = ev->width;
1203 if(ev->value_mask & CWHeight)
1204 win->g.h = ev->height;
1205 /* this seems to be full screen */
1206 if (win->g.w > WIDTH) {
1207 /* kill border */
1208 win->g.x -= 1;
1209 win->g.w += 1;
1210 }
1211 if (win->g.h > HEIGHT) {
1212 /* kill border */
1213 win->g.y -= 1;
1214 win->g.h += 1;
1215 }
1216 if((ev->value_mask & (CWX|CWY)) && !(ev->value_mask & (CWWidth|CWHeight)))
1217 config_win(win);
1218 XMoveResizeWindow(display, win->id, win->g.x, win->g.y, win->g.w, win->g.h);
1219 } else
1220 config_win(win);
1221 }
1222 }
1223
1224 void
1225 configurenotify(XEvent *e)
1226 {
1227 DNPRINTF(SWM_D_EVENT, "configurenotify: window: %lu\n",
1228 e->xconfigure.window);
1229 }
1230
1231 void
1232 destroynotify(XEvent *e)
1233 {
1234 struct ws_win *win;
1235 XDestroyWindowEvent *ev = &e->xdestroywindow;
1236
1237 DNPRINTF(SWM_D_EVENT, "destroynotify: window %lu\n", ev->window);
1238
1239 TAILQ_FOREACH (win, &ws[current_ws].winlist, entry) {
1240 if (ev->window == win->id) {
1241 /* find a window to focus */
1242 ws[current_ws].focus = TAILQ_PREV(win,
1243 ws_win_list, entry);
1244 if (ws[current_ws].focus == NULL)
1245 ws[current_ws].focus =
1246 TAILQ_FIRST(&ws[current_ws].winlist);
1247 if (win == ws[current_ws].focus)
1248 ws[current_ws].focus = NULL;
1249
1250 TAILQ_REMOVE(&ws[current_ws].winlist, win, entry);
1251 set_win_state(win, WithdrawnState);
1252 free(win);
1253 break;
1254 }
1255 }
1256
1257 stack();
1258 }
1259
1260 void
1261 enternotify(XEvent *e)
1262 {
1263 XCrossingEvent *ev = &e->xcrossing;
1264 struct ws_win *win;
1265
1266 DNPRINTF(SWM_D_EVENT, "enternotify: window: %lu\n", ev->window);
1267
1268 if ((ev->mode != NotifyNormal || ev->detail == NotifyInferior) &&
1269 ev->window != root)
1270 return;
1271 if (ignore_enter) {
1272 /* eat event(s) to prevent autofocus */
1273 ignore_enter--;
1274 return;
1275 }
1276 TAILQ_FOREACH (win, &ws[current_ws].winlist, entry) {
1277 if (win->id == ev->window)
1278 focus_win(win);
1279 else
1280 unfocus_win(win);
1281 }
1282 }
1283
1284 void
1285 focusin(XEvent *e)
1286 {
1287 XFocusChangeEvent *ev = &e->xfocus;
1288
1289 DNPRINTF(SWM_D_EVENT, "focusin: window: %lu\n", ev->window);
1290
1291 if (ev->window == root)
1292 return;
1293 /*
1294 * kill grab for now so that we can cut and paste , this screws up
1295 * click to focus
1296 */
1297 /*
1298 DNPRINTF(SWM_D_EVENT, "focusin: window: %lu grabbing\n", ev->window);
1299 XGrabButton(display, Button1, AnyModifier, ev->window, False,
1300 ButtonPress, GrabModeAsync, GrabModeSync, None, None);
1301 */
1302 }
1303
1304 void
1305 mappingnotify(XEvent *e)
1306 {
1307 XMappingEvent *ev = &e->xmapping;
1308
1309 DNPRINTF(SWM_D_EVENT, "mappingnotify: window: %lu\n", ev->window);
1310
1311 XRefreshKeyboardMapping(ev);
1312 if (ev->request == MappingKeyboard)
1313 grabkeys();
1314 }
1315
1316 void
1317 maprequest(XEvent *e)
1318 {
1319 XMapRequestEvent *ev = &e->xmaprequest;
1320 XWindowAttributes wa;
1321
1322 DNPRINTF(SWM_D_EVENT, "maprequest: window: %lu\n",
1323 e->xmaprequest.window);
1324
1325 if (!XGetWindowAttributes(display, ev->window, &wa))
1326 return;
1327 if (wa.override_redirect)
1328 return;
1329 manage_window(e->xmaprequest.window);
1330 stack();
1331 }
1332
1333 void
1334 propertynotify(XEvent *e)
1335 {
1336 DNPRINTF(SWM_D_EVENT, "propertynotify: window: %lu\n",
1337 e->xproperty.window);
1338 }
1339
1340 void
1341 unmapnotify(XEvent *e)
1342 {
1343 DNPRINTF(SWM_D_EVENT, "unmapnotify: window: %lu\n", e->xunmap.window);
1344 }
1345
1346 void
1347 visibilitynotify(XEvent *e)
1348 {
1349 DNPRINTF(SWM_D_EVENT, "visibilitynotify: window: %lu\n", e->xvisibility.window);
1350
1351 if (e->xvisibility.window == bar_window &&
1352 e->xvisibility.state == VisibilityUnobscured)
1353 bar_print();
1354 }
1355
1356 void (*handler[LASTEvent])(XEvent *) = {
1357 [Expose] = expose,
1358 [KeyPress] = keypress,
1359 [ButtonPress] = buttonpress,
1360 [ConfigureRequest] = configurerequest,
1361 [ConfigureNotify] = configurenotify,
1362 [DestroyNotify] = destroynotify,
1363 [EnterNotify] = enternotify,
1364 [FocusIn] = focusin,
1365 [MappingNotify] = mappingnotify,
1366 [MapRequest] = maprequest,
1367 [PropertyNotify] = propertynotify,
1368 [UnmapNotify] = unmapnotify,
1369 [VisibilityNotify] = visibilitynotify,
1370 };
1371
1372 int
1373 xerror_start(Display *d, XErrorEvent *ee)
1374 {
1375 other_wm = 1;
1376 return (-1);
1377 }
1378
1379 int
1380 xerror(Display *d, XErrorEvent *ee)
1381 {
1382 /* fprintf(stderr, "error: %p %p\n", display, ee); */
1383 return (-1);
1384 }
1385
1386 int
1387 active_wm(void)
1388 {
1389 other_wm = 0;
1390 xerrorxlib = XSetErrorHandler(xerror_start);
1391
1392 /* this causes an error if some other window manager is running */
1393 XSelectInput(display, DefaultRootWindow(display),
1394 SubstructureRedirectMask);
1395 XSync(display, False);
1396 if (other_wm)
1397 return (1);
1398
1399 XSetErrorHandler(xerror);
1400 XSync(display, False);
1401 return (0);
1402 }
1403
1404 long
1405 getstate(Window w)
1406 {
1407 int format, status;
1408 long result = -1;
1409 unsigned char *p = NULL;
1410 unsigned long n, extra;
1411 Atom real;
1412
1413 astate = XInternAtom(display, "WM_STATE", False);
1414 status = XGetWindowProperty(display, w, astate, 0L, 2L, False, astate,
1415 &real, &format, &n, &extra, (unsigned char **)&p);
1416 if (status != Success)
1417 return (-1);
1418 if (n != 0)
1419 result = *p;
1420 XFree(p);
1421 return (result);
1422 }
1423
1424 int
1425 main(int argc, char *argv[])
1426 {
1427 struct passwd *pwd;
1428 char conf[PATH_MAX], *cfile = NULL;
1429 struct stat sb;
1430 XEvent e;
1431 unsigned int i, j, num;
1432 Window d1, d2, *wins = NULL;
1433 XWindowAttributes wa;
1434
1435 start_argv = argv;
1436 fprintf(stderr, "Welcome to scrotwm V%s\n", SWM_VERSION);
1437 if (!setlocale(LC_CTYPE, "") || !XSupportsLocale())
1438 warnx("no locale support");
1439
1440 if (!(display = XOpenDisplay(0)))
1441 errx(1, "can not open display");
1442
1443 if (active_wm())
1444 errx(1, "other wm running");
1445
1446 screen = DefaultScreen(display);
1447 root = RootWindow(display, screen);
1448 astate = XInternAtom(display, "WM_STATE", False);
1449
1450 /* look for local and global conf file */
1451 pwd = getpwuid(getuid());
1452 if (pwd == NULL)
1453 errx(1, "invalid user %d", getuid());
1454
1455 snprintf(conf, sizeof conf, "%s/.%s", pwd->pw_dir, SWM_CONF_FILE);
1456 if (stat(conf, &sb) != -1) {
1457 if (S_ISREG(sb.st_mode))
1458 cfile = conf;
1459 } else {
1460 /* try global conf file */
1461 snprintf(conf, sizeof conf, "/etc/%s", SWM_CONF_FILE);
1462 if (!stat(conf, &sb))
1463 if (S_ISREG(sb.st_mode))
1464 cfile = conf;
1465 }
1466 if (cfile)
1467 conf_load(cfile);
1468
1469 /* init all workspaces */
1470 for (i = 0; i < SWM_WS_MAX; i++) {
1471 ws[i].visible = 0;
1472 ws[i].restack = 1;
1473 ws[i].focus = NULL;
1474 ws[i].g.x = 0;
1475 ws[i].g.y = 0;
1476 ws[i].g.w = DisplayWidth(display, screen) - 2;
1477 ws[i].g.h = DisplayHeight(display, screen) - 2;
1478 TAILQ_INIT(&ws[i].winlist);
1479
1480 for (j = 0; layouts[j].l_stack != NULL; j++) {
1481 if (layouts[j].l_init != NULL)
1482 layouts[j].l_init(i);
1483 }
1484 ws[i].cur_layout = &layouts[0];
1485 }
1486 /* make work space 1 active */
1487 ws[0].visible = 1;
1488
1489 /* grab existing windows */
1490 if (XQueryTree(display, root, &d1, &d2, &wins, &num)) {
1491 /* normal windows */
1492 for (i = 0; i < num; i++) {
1493 XGetWindowAttributes(display, wins[i], &wa);
1494 if (!XGetWindowAttributes(display, wins[i], &wa) ||
1495 wa.override_redirect || XGetTransientForHint(display, wins[i], &d1))
1496 continue;
1497 if (wa.map_state == IsViewable || getstate(wins[i]) == NormalState)
1498 manage_window(wins[i]);
1499 }
1500 /* transient windows */
1501 for (i = 0; i < num; i++) {
1502 if (!XGetWindowAttributes(display, wins[i], &wa))
1503 continue;
1504 if (XGetTransientForHint(display, wins[i], &d1) &&
1505 (wa.map_state == IsViewable || getstate(wins[i]) ==
1506 NormalState))
1507 manage_window(wins[i]);
1508 }
1509 if (wins)
1510 XFree(wins);
1511 }
1512 ws[0].focus = TAILQ_FIRST(&ws[0].winlist);
1513
1514 /* setup status bar */
1515 bar_setup();
1516
1517 XSelectInput(display, root, SubstructureRedirectMask |
1518 SubstructureNotifyMask | ButtonPressMask | KeyPressMask |
1519 EnterWindowMask | LeaveWindowMask | StructureNotifyMask |
1520 FocusChangeMask | PropertyChangeMask | ExposureMask);
1521
1522 grabkeys();
1523 stack();
1524
1525 while (running) {
1526 XNextEvent(display, &e);
1527 if (handler[e.type])
1528 handler[e.type](&e);
1529 }
1530
1531 XCloseDisplay(display);
1532
1533 return (0);
1534 }