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