aewl

view aewl.c @ 778:7ea91d4d0882

rename eprint() -> die()
author meillo@marmaro.de
date Mon, 16 Feb 2009 16:18:25 +0100
parents 3835ddb75a55
children f1e806b34697
line source
1 /* (C)opyright MMVI-MMVII Anselm R. Garbe <garbeam at gmail dot com>
2 * (C)opyright MMVIII markus schnalke <meillo at marmaro dot de>
3 * See LICENSE file for license details.
4 *
5 * dynamic window manager is designed like any other X client as well. It is
6 * driven through handling X events. In contrast to other X clients, a window
7 * manager selects for SubstructureRedirectMask on the root window, to receive
8 * events about window (dis-)appearance. Only one X connection at a time is
9 * allowed to select for this event mask.
10 *
11 * The event handlers of dwm are organized in an array which is accessed
12 * whenever a new event has been fetched. This allows event dispatching in O(1)
13 * time.
14 *
15 * Each child of the root window is called a client, except windows which have
16 * set the override_redirect flag. Clients are organized in a global
17 * doubly-linked client list, the focus history is remembered through a global
18 * stack list. [...]
19 *
20 * Keys and tagging rules are organized as arrays and defined in the config.h
21 * file. [...] The current mode is represented by the arrange() function
22 * pointer, which wether points to [domax()] or dotile().
23 *
24 * To understand everything else, start reading main.c:main().
25 *
26 * -- and now about aewl --
27 *
28 * aewl is a stripped down dwm. It stated as a patchset, but finally forked off
29 * completely. The reason for this was the increasing gap between my wish to
30 * stay where dwm was, and dwm direction to go further. Further more did I
31 * always use only a small subset of dwm's features, so condencing dwm had been
32 * my wish for a long time.
33 *
34 * In aewl clients are either tagged or not (only one tag). Visible are either
35 * all tagged clients or all without the tag.
36 */
39 #include <errno.h>
40 #include <locale.h>
41 #include <stdio.h>
42 #include <stdarg.h>
43 #include <stdlib.h>
44 #include <string.h>
45 #include <unistd.h>
46 #include <sys/signal.h>
47 #include <sys/types.h>
48 #include <sys/wait.h>
49 #include <X11/cursorfont.h>
50 #include <X11/keysym.h>
51 #include <X11/Xatom.h>
52 #include <X11/Xlib.h>
53 #include <X11/Xproto.h>
54 #include <X11/Xutil.h>
56 #include "config.h"
59 /* mask shorthands, used in event.c and client.c */
60 #define BUTTONMASK (ButtonPressMask | ButtonReleaseMask)
62 enum { NetSupported, NetWMName, NetLast }; /* EWMH atoms */
63 enum { WMProtocols, WMDelete, WMState, WMLast }; /* default atoms */
64 enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */
65 enum { ColFG, ColBG, ColLast }; /* color */
67 typedef struct {
68 int ascent;
69 int descent;
70 int height;
71 XFontSet set;
72 XFontStruct *xfont;
73 } Fnt;
75 typedef struct {
76 int x, y, w, h;
77 unsigned long norm[ColLast];
78 unsigned long sel[ColLast];
79 Drawable drawable;
80 Fnt font;
81 GC gc;
82 } DC; /* draw context */
84 typedef struct Client Client;
85 struct Client {
86 char name[256];
87 int x, y, w, h;
88 int rx, ry, rw, rh; /* revert geometry */
89 int basew, baseh, incw, inch, maxw, maxh, minw, minh;
90 int minax, minay, maxax, maxay;
91 long flags;
92 unsigned int border;
93 Bool isfixed, isfloat, ismax;
94 Bool tag;
95 Client *next;
96 Client *prev;
97 Client *snext;
98 Window win;
99 };
101 typedef struct {
102 const char* class;
103 const char* instance;
104 const char* title;
105 int tag;
106 Bool isfloat;
107 } Rule;
110 typedef struct {
111 unsigned long mod;
112 KeySym keysym;
113 void (*func)(const char* cmd);
114 const char* cmd;
115 } Key;
118 #define CLEANMASK(mask) (mask & ~(numlockmask | LockMask))
119 #define MOUSEMASK (BUTTONMASK | PointerMotionMask)
123 char stext[256]; /* status text */
124 int bh; /* bar height */
125 int screen, sx, sy, sw, sh; /* screen geometry */
126 int wax, way, wah, waw; /* windowarea geometry */
127 unsigned int nmaster; /* number of master clients */
128 unsigned int numlockmask; /* dynamic lock mask */
129 void (*handler[LASTEvent])(XEvent *); /* event handler */
130 void (*arrange)(void); /* arrange function, indicates mode */
131 Atom wmatom[WMLast], netatom[NetLast];
132 Bool running = True;
133 Bool selscreen = True;
134 Bool seltag;
135 Client* clients = NULL; /* global client list */
136 Client* stack = NULL; /* global client stack */
137 Client* sel = NULL; /* selected client */
138 Cursor cursor[CurLast];
139 DC dc = {0}; /* global draw context */
140 Display *dpy;
141 Window root, barwin;
143 static int (*xerrorxlib)(Display *, XErrorEvent *);
144 static Bool otherwm;
145 static unsigned int len = 0;
149 void configure(Client *c); /* send synthetic configure event */
150 void focus(Client *c); /* focus c, c may be NULL */
151 Client *getclient(Window w); /* return client of w */
152 Bool isprotodel(Client *c); /* returns True if c->win supports wmatom[WMDelete] */
153 void manage(Window w, XWindowAttributes *wa); /* manage new client */
154 void resize(Client *c, Bool sizehints); /* resize c*/
155 void updatesizehints(Client *c); /* update the size hint variables of c */
156 void updatetitle(Client *c); /* update the name of c */
157 void unmanage(Client *c); /* destroy c */
159 void drawbar(void); /* draw the bar */
160 unsigned long getcolor(const char *colstr); /* return color of colstr */
161 void setfont(const char *fontstr); /* set the font for DC */
162 unsigned int textw(const char *text); /* return the width of text in px*/
164 void grabkeys(void); /* grab all keys defined in config.h */
166 void sendevent(Window w, Atom a, long value); /* send synthetic event to w */
167 int xerror(Display *dsply, XErrorEvent *ee); /* X error handler */
169 Client *getnext(Client *c); /* returns next visible client */
170 void settag(Client *c, Client *trans); /* sets tag of c */
172 void *emallocz(unsigned int size); /* allocates zero-initialized memory, exits on error */
173 void die(const char *errstr, ...); /* prints errstr and exits with 1 */
175 void detach(Client *c); /* detaches c from global client list */
176 void dotile(void); /* arranges all windows tiled */
177 void domax(void); /* arranges all windows fullscreen */
178 Bool isvisible(Client *c); /* returns True if client is visible */
179 void restack(void); /* restores z layers of all clients */
181 void toggleview(void); /* toggle the view */
182 void focusnext(void); /* focuses next visible client */
183 void zoom(void); /* zooms the focused client to master area */
184 void killclient(void); /* kill c nicely */
185 void quit(void); /* quit nicely */
186 void togglemode(void); /* toggles global arrange function (dotile/domax) */
187 void togglefloat(void); /* toggles focusesd client between floating/non-floating state */
188 void incnmaster(void); /* increments nmaster */
189 void decnmaster(void); /* decrements nmaster */
190 void toggletag(void); /* toggles tag of c */
191 void spawn(const char* cmd); /* forks a new subprocess with cmd */
193 void updatestatus(void); /* update the status text */
196 RULES
197 KEYS
207 /* from view.c */
209 Client *
210 nexttiled(Client *c) {
211 for(c = getnext(c); c && c->isfloat; c = getnext(c->next));
212 return c;
213 }
215 void
216 togglemax(Client *c) {
217 XEvent ev;
219 if(c->isfixed)
220 return;
222 if((c->ismax = !c->ismax)) {
223 c->rx = c->x;
224 c->ry = c->y;
225 c->rw = c->w;
226 c->rh = c->h;
227 c->x = wax;
228 c->y = way;
229 c->w = waw - 2 * BORDERPX;
230 c->h = wah - 2 * BORDERPX;
231 } else {
232 c->x = c->rx;
233 c->y = c->ry;
234 c->w = c->rw;
235 c->h = c->rh;
236 }
237 resize(c, False);
238 while(XCheckMaskEvent(dpy, EnterWindowMask, &ev));
239 }
244 void
245 detach(Client *c) {
246 if(c->prev)
247 c->prev->next = c->next;
248 if(c->next)
249 c->next->prev = c->prev;
250 if(c == clients)
251 clients = c->next;
252 c->next = c->prev = NULL;
253 }
255 void
256 dotile(void) {
257 unsigned int i, n, mw, mh, tw, th;
258 Client *c;
260 for(n = 0, c = nexttiled(clients); c; c = nexttiled(c->next))
261 n++;
262 /* window geoms */
263 mh = (n > nmaster) ? wah / nmaster : wah / (n > 0 ? n : 1);
264 mw = (n > nmaster) ? waw / 2 : waw;
265 th = (n > nmaster) ? wah / (n - nmaster) : 0;
266 tw = waw - mw;
268 for(i = 0, c = clients; c; c = c->next)
269 if(isvisible(c)) {
270 if(c->isfloat) {
271 resize(c, True);
272 continue;
273 }
274 c->ismax = False;
275 c->x = wax;
276 c->y = way;
277 if(i < nmaster) {
278 c->y += i * mh;
279 c->w = mw - 2 * BORDERPX;
280 c->h = mh - 2 * BORDERPX;
281 }
282 else { /* tile window */
283 c->x += mw;
284 c->w = tw - 2 * BORDERPX;
285 if(th > 2 * BORDERPX) {
286 c->y += (i - nmaster) * th;
287 c->h = th - 2 * BORDERPX;
288 }
289 else /* fallback if th <= 2 * BORDERPX */
290 c->h = wah - 2 * BORDERPX;
291 }
292 resize(c, False);
293 i++;
294 }
295 else
296 XMoveWindow(dpy, c->win, c->x + 2 * sw, c->y);
297 if(!sel || !isvisible(sel)) {
298 for(c = stack; c && !isvisible(c); c = c->snext);
299 focus(c);
300 }
301 restack();
302 }
304 void
305 domax(void) {
306 Client *c;
308 for(c = clients; c; c = c->next) {
309 if(isvisible(c)) {
310 if(c->isfloat) {
311 resize(c, True);
312 continue;
313 }
314 c->ismax = True;
315 c->x = wax;
316 c->y = way;
317 c->w = waw - 2 * BORDERPX;
318 c->h = wah - 2 * BORDERPX;
319 resize(c, False);
320 } else {
321 XMoveWindow(dpy, c->win, c->x + 2 * sw, c->y);
322 }
323 }
324 if(!sel || !isvisible(sel)) {
325 for(c = stack; c && !isvisible(c); c = c->snext);
326 focus(c);
327 }
328 restack();
329 }
331 void
332 focusnext() {
333 Client *c;
335 if(!sel)
336 return;
337 if(!(c = getnext(sel->next)))
338 c = getnext(clients);
339 if(c) {
340 focus(c);
341 restack();
342 }
343 }
345 void
346 incnmaster() {
347 if(wah / (nmaster + 1) <= 2 * BORDERPX)
348 return;
349 nmaster++;
350 if(sel)
351 arrange();
352 }
354 void
355 decnmaster() {
356 if(nmaster <= 1)
357 return;
358 nmaster--;
359 if(sel)
360 arrange();
361 }
363 Bool
364 isvisible(Client *c) {
365 return (c->tag == seltag);
366 }
368 void
369 restack(void) {
370 Client *c;
371 XEvent ev;
373 drawbar();
374 if(!sel)
375 return;
376 if(sel->isfloat)
377 XRaiseWindow(dpy, sel->win);
379 if(!sel->isfloat)
380 XLowerWindow(dpy, sel->win);
381 for(c = nexttiled(clients); c; c = nexttiled(c->next)) {
382 if(c == sel)
383 continue;
384 XLowerWindow(dpy, c->win);
385 }
387 XSync(dpy, False);
388 while(XCheckMaskEvent(dpy, EnterWindowMask, &ev));
389 }
391 void
392 togglefloat() {
393 if (!sel)
394 return;
395 sel->isfloat = !sel->isfloat;
396 arrange();
397 }
399 void
400 togglemode() {
401 /* only toggle between tile and max - float is just available through togglefloat */
402 arrange = (arrange == dotile) ? domax : dotile;
403 if(sel)
404 arrange();
405 zoom();
406 }
408 void
409 toggleview() {
410 seltag = !seltag;
411 arrange();
412 }
414 void
415 zoom() {
416 unsigned int n;
417 Client *c;
419 if(!sel)
420 return;
421 if(sel->isfloat) {
422 togglemax(sel);
423 return;
424 }
425 for(n = 0, c = nexttiled(clients); c; c = nexttiled(c->next))
426 n++;
428 if((c = sel) == nexttiled(clients))
429 if(!(c = nexttiled(c->next)))
430 return;
431 detach(c);
432 if(clients)
433 clients->prev = c;
434 c->next = clients;
435 clients = c;
436 focus(c);
437 arrange();
438 }
440 /* from util.c */
442 void *
443 emallocz(unsigned int size) {
444 void *res = calloc(1, size);
446 if(!res)
447 die("fatal: could not malloc() %u bytes\n", size);
448 return res;
449 }
451 void
452 die(const char *errstr, ...) {
453 va_list ap;
455 va_start(ap, errstr);
456 vfprintf(stderr, errstr, ap);
457 va_end(ap);
458 exit(EXIT_FAILURE);
459 }
461 void
462 spawn(const char* cmd) {
463 static char *shell = NULL;
465 if(!cmd)
466 return;
467 if(!(shell = getenv("SHELL")))
468 shell = "/bin/sh";
469 /* The double-fork construct avoids zombie processes and keeps the code
470 * clean from stupid signal handlers. */
471 if(fork() == 0) {
472 if(fork() == 0) {
473 if(dpy)
474 close(ConnectionNumber(dpy));
475 setsid();
476 execl(shell, shell, "-c", cmd, (char *)NULL);
477 fprintf(stderr, "aewl: execl '%s -c %s'", shell, cmd);
478 perror(" failed");
479 }
480 exit(0);
481 }
482 wait(0);
483 }
485 /* from tag.c */
487 Client *
488 getnext(Client *c) {
489 while(c && !isvisible(c)) {
490 c = c->next;
491 }
492 return c;
493 }
495 void
496 settag(Client *c, Client *trans) {
497 unsigned int i;
498 XClassHint ch = { 0 };
500 if(trans) {
501 c->tag = trans->tag;
502 return;
503 }
504 c->tag = seltag; /* default */
505 XGetClassHint(dpy, c->win, &ch);
506 len = sizeof rule / sizeof rule[0];
507 for(i = 0; i < len; i++) {
508 if((rule[i].title && strstr(c->name, rule[i].title))
509 || (ch.res_class && rule[i].class && strstr(ch.res_class, rule[i].class))
510 || (ch.res_name && rule[i].instance && strstr(ch.res_name, rule[i].instance))) {
511 c->isfloat = rule[i].isfloat;
512 if (rule[i].tag < 0) {
513 c->tag = seltag;
514 } else if (rule[i].tag) {
515 c->tag = True;
516 } else {
517 c->tag = False;
518 }
519 break;
520 }
521 }
522 if(ch.res_class)
523 XFree(ch.res_class);
524 if(ch.res_name)
525 XFree(ch.res_name);
526 }
528 void
529 toggletag() {
530 if(!sel)
531 return;
532 sel->tag = !sel->tag;
533 toggleview();
534 }
536 /* from event.c */
538 void
539 movemouse(Client *c) {
540 int x1, y1, ocx, ocy, di;
541 unsigned int dui;
542 Window dummy;
543 XEvent ev;
545 ocx = c->x;
546 ocy = c->y;
547 if(XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync,
548 None, cursor[CurMove], CurrentTime) != GrabSuccess)
549 return;
550 c->ismax = False;
551 XQueryPointer(dpy, root, &dummy, &dummy, &x1, &y1, &di, &di, &dui);
552 for(;;) {
553 XMaskEvent(dpy, MOUSEMASK | ExposureMask | SubstructureRedirectMask, &ev);
554 switch (ev.type) {
555 case ButtonRelease:
556 resize(c, True);
557 XUngrabPointer(dpy, CurrentTime);
558 return;
559 case ConfigureRequest:
560 case Expose:
561 case MapRequest:
562 handler[ev.type](&ev);
563 break;
564 case MotionNotify:
565 XSync(dpy, False);
566 c->x = ocx + (ev.xmotion.x - x1);
567 c->y = ocy + (ev.xmotion.y - y1);
568 if(abs(wax + c->x) < SNAP)
569 c->x = wax;
570 else if(abs((wax + waw) - (c->x + c->w + 2 * c->border)) < SNAP)
571 c->x = wax + waw - c->w - 2 * c->border;
572 if(abs(way - c->y) < SNAP)
573 c->y = way;
574 else if(abs((way + wah) - (c->y + c->h + 2 * c->border)) < SNAP)
575 c->y = way + wah - c->h - 2 * c->border;
576 resize(c, False);
577 break;
578 }
579 }
580 }
582 void
583 resizemouse(Client *c) {
584 int ocx, ocy;
585 int nw, nh;
586 XEvent ev;
588 ocx = c->x;
589 ocy = c->y;
590 if(XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync,
591 None, cursor[CurResize], CurrentTime) != GrabSuccess)
592 return;
593 c->ismax = False;
594 XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->border - 1, c->h + c->border - 1);
595 for(;;) {
596 XMaskEvent(dpy, MOUSEMASK | ExposureMask | SubstructureRedirectMask , &ev);
597 switch(ev.type) {
598 case ButtonRelease:
599 resize(c, True);
600 XUngrabPointer(dpy, CurrentTime);
601 return;
602 case ConfigureRequest:
603 case Expose:
604 case MapRequest:
605 handler[ev.type](&ev);
606 break;
607 case MotionNotify:
608 XSync(dpy, False);
609 nw = ev.xmotion.x - ocx - 2 * c->border + 1;
610 c->w = nw > 0 ? nw : 1;
611 nh = ev.xmotion.y - ocy - 2 * c->border + 1;
612 c->h = nh > 0 ? nh : 1;
613 resize(c, True);
614 break;
615 }
616 }
617 }
619 void
620 buttonpress(XEvent *e) {
621 Client *c;
622 XButtonPressedEvent *ev = &e->xbutton;
624 if(barwin == ev->window) {
625 return;
626 }
627 if((c = getclient(ev->window))) {
628 focus(c);
629 if(CLEANMASK(ev->state) != MODKEY)
630 return;
631 if(ev->button == Button1 && c->isfloat) {
632 restack();
633 movemouse(c);
634 } else if(ev->button == Button3 && c->isfloat && !c->isfixed) {
635 restack();
636 resizemouse(c);
637 }
638 }
639 }
641 void
642 configurerequest(XEvent *e) {
643 unsigned long newmask;
644 Client *c;
645 XConfigureRequestEvent *ev = &e->xconfigurerequest;
646 XWindowChanges wc;
648 if((c = getclient(ev->window))) {
649 c->ismax = False;
650 if(ev->value_mask & CWX)
651 c->x = ev->x;
652 if(ev->value_mask & CWY)
653 c->y = ev->y;
654 if(ev->value_mask & CWWidth)
655 c->w = ev->width;
656 if(ev->value_mask & CWHeight)
657 c->h = ev->height;
658 if(ev->value_mask & CWBorderWidth)
659 c->border = ev->border_width;
660 wc.x = c->x;
661 wc.y = c->y;
662 wc.width = c->w;
663 wc.height = c->h;
664 newmask = ev->value_mask & (~(CWSibling | CWStackMode | CWBorderWidth));
665 if(newmask)
666 XConfigureWindow(dpy, c->win, newmask, &wc);
667 else
668 configure(c);
669 XSync(dpy, False);
670 if(c->isfloat) {
671 resize(c, False);
672 if(!isvisible(c))
673 XMoveWindow(dpy, c->win, c->x + 2 * sw, c->y);
674 }
675 else
676 arrange();
677 } else {
678 wc.x = ev->x;
679 wc.y = ev->y;
680 wc.width = ev->width;
681 wc.height = ev->height;
682 wc.border_width = ev->border_width;
683 wc.sibling = ev->above;
684 wc.stack_mode = ev->detail;
685 XConfigureWindow(dpy, ev->window, ev->value_mask, &wc);
686 XSync(dpy, False);
687 }
688 }
690 void
691 destroynotify(XEvent *e) {
692 Client *c;
693 XDestroyWindowEvent *ev = &e->xdestroywindow;
695 if((c = getclient(ev->window)))
696 unmanage(c);
697 }
699 void
700 enternotify(XEvent *e) {
701 Client *c;
702 XCrossingEvent *ev = &e->xcrossing;
704 if(ev->mode != NotifyNormal || ev->detail == NotifyInferior)
705 return;
706 if((c = getclient(ev->window)) && isvisible(c))
707 focus(c);
708 else if(ev->window == root) {
709 selscreen = True;
710 for(c = stack; c && !isvisible(c); c = c->snext);
711 focus(c);
712 }
713 }
715 void
716 expose(XEvent *e) {
717 XExposeEvent *ev = &e->xexpose;
719 if(ev->count == 0) {
720 if(barwin == ev->window)
721 drawbar();
722 }
723 }
725 void
726 keypress(XEvent *e) {
727 static unsigned int len = sizeof key / sizeof key[0];
728 unsigned int i;
729 KeySym keysym;
730 XKeyEvent *ev = &e->xkey;
732 keysym = XKeycodeToKeysym(dpy, (KeyCode)ev->keycode, 0);
733 for(i = 0; i < len; i++) {
734 if(keysym == key[i].keysym && CLEANMASK(key[i].mod) == CLEANMASK(ev->state)) {
735 if(key[i].func)
736 key[i].func(key[i].cmd);
737 }
738 }
739 }
741 void
742 leavenotify(XEvent *e) {
743 XCrossingEvent *ev = &e->xcrossing;
745 if((ev->window == root) && !ev->same_screen) {
746 selscreen = False;
747 focus(NULL);
748 }
749 }
751 void
752 mappingnotify(XEvent *e) {
753 XMappingEvent *ev = &e->xmapping;
755 XRefreshKeyboardMapping(ev);
756 if(ev->request == MappingKeyboard)
757 grabkeys();
758 }
760 void
761 maprequest(XEvent *e) {
762 static XWindowAttributes wa;
763 XMapRequestEvent *ev = &e->xmaprequest;
765 if(!XGetWindowAttributes(dpy, ev->window, &wa))
766 return;
767 if(wa.override_redirect) {
768 XSelectInput(dpy, ev->window,
769 (StructureNotifyMask | PropertyChangeMask));
770 return;
771 }
772 if(!getclient(ev->window))
773 manage(ev->window, &wa);
774 }
776 void
777 propertynotify(XEvent *e) {
778 Client *c;
779 Window trans;
780 XPropertyEvent *ev = &e->xproperty;
782 if((ev->window == root) && (ev->atom = XA_WM_NAME))
783 updatestatus();
784 else if(ev->state == PropertyDelete)
785 return; /* ignore */
786 else if((c = getclient(ev->window))) {
787 switch (ev->atom) {
788 default: break;
789 case XA_WM_TRANSIENT_FOR:
790 XGetTransientForHint(dpy, c->win, &trans);
791 if(!c->isfloat && (c->isfloat = (trans != 0)))
792 arrange();
793 break;
794 case XA_WM_NORMAL_HINTS:
795 updatesizehints(c);
796 break;
797 }
798 if(ev->atom == XA_WM_NAME || ev->atom == netatom[NetWMName]) {
799 updatetitle(c);
800 if(c == sel)
801 drawbar();
802 }
803 }
804 }
806 void
807 unmapnotify(XEvent *e) {
808 Client *c;
809 XUnmapEvent *ev = &e->xunmap;
811 if((c = getclient(ev->window)))
812 unmanage(c);
813 }
817 void (*handler[LASTEvent]) (XEvent *) = {
818 [ButtonPress] = buttonpress,
819 [ConfigureRequest] = configurerequest,
820 [DestroyNotify] = destroynotify,
821 [EnterNotify] = enternotify,
822 [LeaveNotify] = leavenotify,
823 [Expose] = expose,
824 [KeyPress] = keypress,
825 [MappingNotify] = mappingnotify,
826 [MapRequest] = maprequest,
827 [PropertyNotify] = propertynotify,
828 [UnmapNotify] = unmapnotify
829 };
831 void
832 grabkeys(void) {
833 static unsigned int len = sizeof key / sizeof key[0];
834 unsigned int i;
835 KeyCode code;
837 XUngrabKey(dpy, AnyKey, AnyModifier, root);
838 for(i = 0; i < len; i++) {
839 code = XKeysymToKeycode(dpy, key[i].keysym);
840 XGrabKey(dpy, code, key[i].mod, root, True,
841 GrabModeAsync, GrabModeAsync);
842 XGrabKey(dpy, code, key[i].mod | LockMask, root, True,
843 GrabModeAsync, GrabModeAsync);
844 XGrabKey(dpy, code, key[i].mod | numlockmask, root, True,
845 GrabModeAsync, GrabModeAsync);
846 XGrabKey(dpy, code, key[i].mod | numlockmask | LockMask, root, True,
847 GrabModeAsync, GrabModeAsync);
848 }
849 }
851 /* from draw.c */
853 unsigned int
854 textnw(const char *text, unsigned int len) {
855 XRectangle r;
857 if(dc.font.set) {
858 XmbTextExtents(dc.font.set, text, len, NULL, &r);
859 return r.width;
860 }
861 return XTextWidth(dc.font.xfont, text, len);
862 }
864 void
865 drawtext(const char *text, unsigned long col[ColLast]) {
866 int x, y, w, h;
867 static char buf[256];
868 unsigned int len, olen;
869 XGCValues gcv;
870 XRectangle r = { dc.x, dc.y, dc.w, dc.h };
872 XSetForeground(dpy, dc.gc, col[ColBG]);
873 XFillRectangles(dpy, dc.drawable, dc.gc, &r, 1);
874 if(!text)
875 return;
876 w = 0;
877 olen = len = strlen(text);
878 if(len >= sizeof buf)
879 len = sizeof buf - 1;
880 memcpy(buf, text, len);
881 buf[len] = 0;
882 h = dc.font.ascent + dc.font.descent;
883 y = dc.y + (dc.h / 2) - (h / 2) + dc.font.ascent;
884 x = dc.x + (h / 2);
885 /* shorten text if necessary */
886 while(len && (w = textnw(buf, len)) > dc.w - h)
887 buf[--len] = 0;
888 if(len < olen) {
889 if(len > 1)
890 buf[len - 1] = '.';
891 if(len > 2)
892 buf[len - 2] = '.';
893 if(len > 3)
894 buf[len - 3] = '.';
895 }
896 if(w > dc.w)
897 return; /* too long */
898 gcv.foreground = col[ColFG];
899 if(dc.font.set) {
900 XChangeGC(dpy, dc.gc, GCForeground, &gcv);
901 XmbDrawString(dpy, dc.drawable, dc.font.set, dc.gc, x, y, buf, len);
902 } else {
903 gcv.font = dc.font.xfont->fid;
904 XChangeGC(dpy, dc.gc, GCForeground | GCFont, &gcv);
905 XDrawString(dpy, dc.drawable, dc.gc, x, y, buf, len);
906 }
907 }
909 void
910 drawbar(void) {
911 int x;
912 unsigned long black[ColLast];
913 black[ColBG] = getcolor("#000000");
914 black[ColFG] = getcolor("#ffffff");
916 /* views */
917 dc.x = dc.y = 0;
918 dc.w = textw(NAMETAGGED);
919 drawtext(NAMETAGGED, ( seltag ? dc.sel : dc.norm ));
921 dc.x += dc.w;
922 dc.w = BORDERPX;
923 drawtext(NULL, black);
925 dc.x += dc.w;
926 dc.w = textw(NAMEUNTAGGED);
927 drawtext(NAMEUNTAGGED, ( seltag ? dc.norm : dc.sel ));
929 dc.x += dc.w;
930 dc.w = BORDERPX;
931 drawtext(NULL, black);
933 /* status text */
934 x = dc.x + dc.w;
935 dc.w = textw(stext);
936 dc.x = sw - dc.w;
937 if(dc.x < x) {
938 dc.x = x;
939 dc.w = sw - x;
940 }
941 drawtext(stext, dc.norm);
943 /* client title */
944 if((dc.w = dc.x - x) > bh) {
945 dc.x = x;
946 drawtext(sel ? sel->name : NULL, dc.norm);
948 dc.x += dc.w;
949 dc.w = BORDERPX;
950 drawtext(NULL, black);
951 }
953 XCopyArea(dpy, dc.drawable, barwin, dc.gc, 0, 0, sw, bh, 0, 0);
954 XSync(dpy, False);
955 }
957 unsigned long
958 getcolor(const char *colstr) {
959 Colormap cmap = DefaultColormap(dpy, screen);
960 XColor color;
962 if(!XAllocNamedColor(dpy, cmap, colstr, &color, &color))
963 die("error, cannot allocate color '%s'\n", colstr);
964 return color.pixel;
965 }
967 void
968 setfont(const char *fontstr) {
969 char *def, **missing;
970 int i, n;
972 missing = NULL;
973 if(dc.font.set)
974 XFreeFontSet(dpy, dc.font.set);
975 dc.font.set = XCreateFontSet(dpy, fontstr, &missing, &n, &def);
976 if(missing) {
977 while(n--)
978 fprintf(stderr, "missing fontset: %s\n", missing[n]);
979 XFreeStringList(missing);
980 }
981 if(dc.font.set) {
982 XFontSetExtents *font_extents;
983 XFontStruct **xfonts;
984 char **font_names;
985 dc.font.ascent = dc.font.descent = 0;
986 font_extents = XExtentsOfFontSet(dc.font.set);
987 n = XFontsOfFontSet(dc.font.set, &xfonts, &font_names);
988 for(i = 0, dc.font.ascent = 0, dc.font.descent = 0; i < n; i++) {
989 if(dc.font.ascent < (*xfonts)->ascent)
990 dc.font.ascent = (*xfonts)->ascent;
991 if(dc.font.descent < (*xfonts)->descent)
992 dc.font.descent = (*xfonts)->descent;
993 xfonts++;
994 }
995 } else {
996 if(dc.font.xfont)
997 XFreeFont(dpy, dc.font.xfont);
998 dc.font.xfont = NULL;
999 if(!(dc.font.xfont = XLoadQueryFont(dpy, fontstr)))
1000 die("error, cannot load font: '%s'\n", fontstr);
1001 dc.font.ascent = dc.font.xfont->ascent;
1002 dc.font.descent = dc.font.xfont->descent;
1004 dc.font.height = dc.font.ascent + dc.font.descent;
1007 unsigned int
1008 textw(const char *text) {
1009 return textnw(text, strlen(text)) + dc.font.height;
1012 /* from client.c */
1014 void
1015 detachstack(Client *c) {
1016 Client **tc;
1017 for(tc=&stack; *tc && *tc != c; tc=&(*tc)->snext);
1018 *tc = c->snext;
1021 void
1022 grabbuttons(Client *c, Bool focused) {
1023 XUngrabButton(dpy, AnyButton, AnyModifier, c->win);
1025 if(focused) {
1026 XGrabButton(dpy, Button1, MODKEY, c->win, False, BUTTONMASK,
1027 GrabModeAsync, GrabModeSync, None, None);
1028 XGrabButton(dpy, Button1, MODKEY | LockMask, c->win, False, BUTTONMASK,
1029 GrabModeAsync, GrabModeSync, None, None);
1030 XGrabButton(dpy, Button1, MODKEY | numlockmask, c->win, False, BUTTONMASK,
1031 GrabModeAsync, GrabModeSync, None, None);
1032 XGrabButton(dpy, Button1, MODKEY | numlockmask | LockMask, c->win, False, BUTTONMASK,
1033 GrabModeAsync, GrabModeSync, None, None);
1035 XGrabButton(dpy, Button2, MODKEY, c->win, False, BUTTONMASK,
1036 GrabModeAsync, GrabModeSync, None, None);
1037 XGrabButton(dpy, Button2, MODKEY | LockMask, c->win, False, BUTTONMASK,
1038 GrabModeAsync, GrabModeSync, None, None);
1039 XGrabButton(dpy, Button2, MODKEY | numlockmask, c->win, False, BUTTONMASK,
1040 GrabModeAsync, GrabModeSync, None, None);
1041 XGrabButton(dpy, Button2, MODKEY | numlockmask | LockMask, c->win, False, BUTTONMASK,
1042 GrabModeAsync, GrabModeSync, None, None);
1044 XGrabButton(dpy, Button3, MODKEY, c->win, False, BUTTONMASK,
1045 GrabModeAsync, GrabModeSync, None, None);
1046 XGrabButton(dpy, Button3, MODKEY | LockMask, c->win, False, BUTTONMASK,
1047 GrabModeAsync, GrabModeSync, None, None);
1048 XGrabButton(dpy, Button3, MODKEY | numlockmask, c->win, False, BUTTONMASK,
1049 GrabModeAsync, GrabModeSync, None, None);
1050 XGrabButton(dpy, Button3, MODKEY | numlockmask | LockMask, c->win, False, BUTTONMASK,
1051 GrabModeAsync, GrabModeSync, None, None);
1052 } else {
1053 XGrabButton(dpy, AnyButton, AnyModifier, c->win, False, BUTTONMASK,
1054 GrabModeAsync, GrabModeSync, None, None);
1058 void
1059 setclientstate(Client *c, long state) {
1060 long data[] = {state, None};
1061 XChangeProperty(dpy, c->win, wmatom[WMState], wmatom[WMState], 32,
1062 PropModeReplace, (unsigned char *)data, 2);
1065 int
1066 xerrordummy(Display *dsply, XErrorEvent *ee) {
1067 return 0;
1070 void
1071 configure(Client *c) {
1072 XEvent synev;
1074 synev.type = ConfigureNotify;
1075 synev.xconfigure.display = dpy;
1076 synev.xconfigure.event = c->win;
1077 synev.xconfigure.window = c->win;
1078 synev.xconfigure.x = c->x;
1079 synev.xconfigure.y = c->y;
1080 synev.xconfigure.width = c->w;
1081 synev.xconfigure.height = c->h;
1082 synev.xconfigure.border_width = c->border;
1083 synev.xconfigure.above = None;
1084 XSendEvent(dpy, c->win, True, NoEventMask, &synev);
1087 void
1088 focus(Client *c) {
1089 if(c && !isvisible(c))
1090 return;
1091 if(sel && sel != c) {
1092 grabbuttons(sel, False);
1093 XSetWindowBorder(dpy, sel->win, dc.norm[ColBG]);
1095 if(c) {
1096 detachstack(c);
1097 c->snext = stack;
1098 stack = c;
1099 grabbuttons(c, True);
1101 sel = c;
1102 drawbar();
1103 if(!selscreen)
1104 return;
1105 if(c) {
1106 XSetWindowBorder(dpy, c->win, dc.sel[ColBG]);
1107 XSetInputFocus(dpy, c->win, RevertToPointerRoot, CurrentTime);
1108 } else {
1109 XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime);
1113 Client *
1114 getclient(Window w) {
1115 Client *c;
1117 for(c = clients; c; c = c->next) {
1118 if(c->win == w) {
1119 return c;
1122 return NULL;
1125 Bool
1126 isprotodel(Client *c) {
1127 int i, n;
1128 Atom *protocols;
1129 Bool ret = False;
1131 if(XGetWMProtocols(dpy, c->win, &protocols, &n)) {
1132 for(i = 0; !ret && i < n; i++)
1133 if(protocols[i] == wmatom[WMDelete])
1134 ret = True;
1135 XFree(protocols);
1137 return ret;
1140 void
1141 killclient() {
1142 if(!sel)
1143 return;
1144 if(isprotodel(sel))
1145 sendevent(sel->win, wmatom[WMProtocols], wmatom[WMDelete]);
1146 else
1147 XKillClient(dpy, sel->win);
1150 void
1151 manage(Window w, XWindowAttributes *wa) {
1152 Client *c;
1153 Window trans;
1155 c = emallocz(sizeof(Client));
1156 c->tag = True;
1157 c->win = w;
1158 c->x = wa->x;
1159 c->y = wa->y;
1160 c->w = wa->width;
1161 c->h = wa->height;
1162 if(c->w == sw && c->h == sh) {
1163 c->border = 0;
1164 c->x = sx;
1165 c->y = sy;
1166 } else {
1167 c->border = BORDERPX;
1168 if(c->x + c->w + 2 * c->border > wax + waw)
1169 c->x = wax + waw - c->w - 2 * c->border;
1170 if(c->y + c->h + 2 * c->border > way + wah)
1171 c->y = way + wah - c->h - 2 * c->border;
1172 if(c->x < wax)
1173 c->x = wax;
1174 if(c->y < way)
1175 c->y = way;
1177 updatesizehints(c);
1178 XSelectInput(dpy, c->win,
1179 StructureNotifyMask | PropertyChangeMask | EnterWindowMask);
1180 XGetTransientForHint(dpy, c->win, &trans);
1181 grabbuttons(c, False);
1182 XSetWindowBorder(dpy, c->win, dc.norm[ColBG]);
1183 updatetitle(c);
1184 settag(c, getclient(trans));
1185 if(!c->isfloat)
1186 c->isfloat = trans || c->isfixed;
1187 if(clients)
1188 clients->prev = c;
1189 c->next = clients;
1190 c->snext = stack;
1191 stack = clients = c;
1192 XMoveWindow(dpy, c->win, c->x + 2 * sw, c->y);
1193 XMapWindow(dpy, c->win);
1194 setclientstate(c, NormalState);
1195 if(isvisible(c))
1196 focus(c);
1197 arrange();
1200 void
1201 resize(Client *c, Bool sizehints) {
1202 float actual, dx, dy, max, min;
1203 XWindowChanges wc;
1205 if(c->w <= 0 || c->h <= 0)
1206 return;
1207 if(sizehints) {
1208 if(c->minw && c->w < c->minw)
1209 c->w = c->minw;
1210 if(c->minh && c->h < c->minh)
1211 c->h = c->minh;
1212 if(c->maxw && c->w > c->maxw)
1213 c->w = c->maxw;
1214 if(c->maxh && c->h > c->maxh)
1215 c->h = c->maxh;
1216 /* inspired by algorithm from fluxbox */
1217 if(c->minay > 0 && c->maxay && (c->h - c->baseh) > 0) {
1218 dx = (float)(c->w - c->basew);
1219 dy = (float)(c->h - c->baseh);
1220 min = (float)(c->minax) / (float)(c->minay);
1221 max = (float)(c->maxax) / (float)(c->maxay);
1222 actual = dx / dy;
1223 if(max > 0 && min > 0 && actual > 0) {
1224 if(actual < min) {
1225 dy = (dx * min + dy) / (min * min + 1);
1226 dx = dy * min;
1227 c->w = (int)dx + c->basew;
1228 c->h = (int)dy + c->baseh;
1230 else if(actual > max) {
1231 dy = (dx * min + dy) / (max * max + 1);
1232 dx = dy * min;
1233 c->w = (int)dx + c->basew;
1234 c->h = (int)dy + c->baseh;
1238 if(c->incw)
1239 c->w -= (c->w - c->basew) % c->incw;
1240 if(c->inch)
1241 c->h -= (c->h - c->baseh) % c->inch;
1243 if(c->w == sw && c->h == sh)
1244 c->border = 0;
1245 else
1246 c->border = BORDERPX;
1247 /* offscreen appearance fixes */
1248 if(c->x > sw)
1249 c->x = sw - c->w - 2 * c->border;
1250 if(c->y > sh)
1251 c->y = sh - c->h - 2 * c->border;
1252 if(c->x + c->w + 2 * c->border < sx)
1253 c->x = sx;
1254 if(c->y + c->h + 2 * c->border < sy)
1255 c->y = sy;
1256 wc.x = c->x;
1257 wc.y = c->y;
1258 wc.width = c->w;
1259 wc.height = c->h;
1260 wc.border_width = c->border;
1261 XConfigureWindow(dpy, c->win, CWX | CWY | CWWidth | CWHeight | CWBorderWidth, &wc);
1262 configure(c);
1263 XSync(dpy, False);
1266 void
1267 updatesizehints(Client *c) {
1268 long msize;
1269 XSizeHints size;
1271 if(!XGetWMNormalHints(dpy, c->win, &size, &msize) || !size.flags)
1272 size.flags = PSize;
1273 c->flags = size.flags;
1274 if(c->flags & PBaseSize) {
1275 c->basew = size.base_width;
1276 c->baseh = size.base_height;
1277 } else {
1278 c->basew = c->baseh = 0;
1280 if(c->flags & PResizeInc) {
1281 c->incw = size.width_inc;
1282 c->inch = size.height_inc;
1283 } else {
1284 c->incw = c->inch = 0;
1286 if(c->flags & PMaxSize) {
1287 c->maxw = size.max_width;
1288 c->maxh = size.max_height;
1289 } else {
1290 c->maxw = c->maxh = 0;
1292 if(c->flags & PMinSize) {
1293 c->minw = size.min_width;
1294 c->minh = size.min_height;
1295 } else {
1296 c->minw = c->minh = 0;
1298 if(c->flags & PAspect) {
1299 c->minax = size.min_aspect.x;
1300 c->minay = size.min_aspect.y;
1301 c->maxax = size.max_aspect.x;
1302 c->maxay = size.max_aspect.y;
1303 } else {
1304 c->minax = c->minay = c->maxax = c->maxay = 0;
1306 c->isfixed = (c->maxw && c->minw && c->maxh && c->minh &&
1307 c->maxw == c->minw && c->maxh == c->minh);
1310 void
1311 updatetitle(Client *c) {
1312 if (!gettextprop(c->win, netatom[NetWMName], c->name, sizeof c->name)) {
1313 gettextprop(c->win, XA_WM_NAME, c->name, sizeof c->name);
1318 Bool
1319 gettextprop(Window w, Atom atom, char* text, unsigned int size) {
1320 char **list = NULL;
1321 int n;
1322 XTextProperty name;
1324 if (!text || size == 0) {
1325 return False;
1327 text[0] = '\0';
1328 XGetTextProperty(dpy, w, &name, atom);
1329 if(!name.nitems)
1330 return False;
1331 if(name.encoding == XA_STRING)
1332 strncpy(text, (char *)name.value, size - 1);
1333 else {
1334 if(XmbTextPropertyToTextList(dpy, &name, &list, &n) >= Success && n > 0 && *list) {
1335 strncpy(text, *list, size - 1);
1336 XFreeStringList(list);
1339 text[size - 1] = '\0';
1340 XFree(name.value);
1341 return True;
1344 void
1345 unmanage(Client *c) {
1346 Client *nc;
1348 /* The server grab construct avoids race conditions. */
1349 XGrabServer(dpy);
1350 XSetErrorHandler(xerrordummy);
1351 detach(c);
1352 detachstack(c);
1353 if(sel == c) {
1354 for(nc = stack; nc && !isvisible(nc); nc = nc->snext);
1355 focus(nc);
1357 XUngrabButton(dpy, AnyButton, AnyModifier, c->win);
1358 setclientstate(c, WithdrawnState);
1359 free(c);
1360 XSync(dpy, False);
1361 XSetErrorHandler(xerror);
1362 XUngrabServer(dpy);
1363 arrange();
1366 /* from main.c */
1368 void
1369 cleanup(void) {
1370 while(stack) {
1371 resize(stack, True);
1372 unmanage(stack);
1374 if(dc.font.set)
1375 XFreeFontSet(dpy, dc.font.set);
1376 else
1377 XFreeFont(dpy, dc.font.xfont);
1378 XUngrabKey(dpy, AnyKey, AnyModifier, root);
1379 XFreePixmap(dpy, dc.drawable);
1380 XFreeGC(dpy, dc.gc);
1381 XDestroyWindow(dpy, barwin);
1382 XFreeCursor(dpy, cursor[CurNormal]);
1383 XFreeCursor(dpy, cursor[CurResize]);
1384 XFreeCursor(dpy, cursor[CurMove]);
1385 XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime);
1386 XSync(dpy, False);
1389 void
1390 scan(void) {
1391 unsigned int i, num;
1392 Window *wins, d1, d2;
1393 XWindowAttributes wa;
1395 wins = NULL;
1396 if(XQueryTree(dpy, root, &d1, &d2, &wins, &num)) {
1397 for(i = 0; i < num; i++) {
1398 if(!XGetWindowAttributes(dpy, wins[i], &wa))
1399 continue;
1400 if(wa.override_redirect || XGetTransientForHint(dpy, wins[i], &d1))
1401 continue;
1402 if(wa.map_state == IsViewable)
1403 manage(wins[i], &wa);
1406 if(wins)
1407 XFree(wins);
1410 void
1411 setup(void) {
1412 int i, j;
1413 unsigned int mask;
1414 Window w;
1415 XModifierKeymap *modmap;
1416 XSetWindowAttributes wa;
1418 screen = DefaultScreen(dpy);
1419 root = RootWindow(dpy, screen);
1421 /* init atoms */
1422 wmatom[WMProtocols] = XInternAtom(dpy, "WM_PROTOCOLS", False);
1423 wmatom[WMDelete] = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
1424 wmatom[WMState] = XInternAtom(dpy, "WM_STATE", False);
1425 netatom[NetSupported] = XInternAtom(dpy, "_NET_SUPPORTED", False);
1426 netatom[NetWMName] = XInternAtom(dpy, "_NET_WM_NAME", False);
1427 XChangeProperty(dpy, root, netatom[NetSupported], XA_ATOM, 32,
1428 PropModeReplace, (unsigned char *) netatom, NetLast);
1429 /* init cursors */
1430 cursor[CurNormal] = XCreateFontCursor(dpy, XC_left_ptr);
1431 cursor[CurResize] = XCreateFontCursor(dpy, XC_sizing);
1432 cursor[CurMove] = XCreateFontCursor(dpy, XC_fleur);
1433 /* init modifier map */
1434 numlockmask = 0;
1435 modmap = XGetModifierMapping(dpy);
1436 for (i = 0; i < 8; i++) {
1437 for (j = 0; j < modmap->max_keypermod; j++) {
1438 if(modmap->modifiermap[i * modmap->max_keypermod + j] == XKeysymToKeycode(dpy, XK_Num_Lock))
1439 numlockmask = (1 << i);
1442 XFreeModifiermap(modmap);
1443 /* select for events */
1444 wa.event_mask = SubstructureRedirectMask | SubstructureNotifyMask
1445 | EnterWindowMask | LeaveWindowMask | PropertyChangeMask;
1446 wa.cursor = cursor[CurNormal];
1447 XChangeWindowAttributes(dpy, root, CWEventMask | CWCursor, &wa);
1448 grabkeys();
1449 seltag = True;
1450 /* style */
1451 dc.norm[ColBG] = getcolor(NORMBGCOLOR);
1452 dc.norm[ColFG] = getcolor(NORMFGCOLOR);
1453 dc.sel[ColBG] = getcolor(SELBGCOLOR);
1454 dc.sel[ColFG] = getcolor(SELFGCOLOR);
1455 setfont(FONT);
1456 /* geometry */
1457 sx = sy = 0;
1458 sw = DisplayWidth(dpy, screen);
1459 sh = DisplayHeight(dpy, screen);
1460 nmaster = NMASTER;
1461 arrange = DEFMODE;
1462 /* bar */
1463 dc.h = bh = dc.font.height + 2;
1464 wa.override_redirect = 1;
1465 wa.background_pixmap = ParentRelative;
1466 wa.event_mask = ButtonPressMask | ExposureMask;
1467 barwin = XCreateWindow(dpy, root, sx, sy, sw, bh, 0,
1468 DefaultDepth(dpy, screen), CopyFromParent, DefaultVisual(dpy, screen),
1469 CWOverrideRedirect | CWBackPixmap | CWEventMask, &wa);
1470 XDefineCursor(dpy, barwin, cursor[CurNormal]);
1471 XMapRaised(dpy, barwin);
1472 /* windowarea */
1473 wax = sx;
1474 way = sy + bh;
1475 wah = sh - bh;
1476 waw = sw;
1477 /* pixmap for everything */
1478 dc.drawable = XCreatePixmap(dpy, root, sw, bh, DefaultDepth(dpy, screen));
1479 dc.gc = XCreateGC(dpy, root, 0, 0);
1480 XSetLineAttributes(dpy, dc.gc, 1, LineSolid, CapButt, JoinMiter);
1481 /* multihead support */
1482 selscreen = XQueryPointer(dpy, root, &w, &w, &i, &i, &i, &i, &mask);
1484 updatestatus();
1487 /*
1488 * Startup Error handler to check if another window manager
1489 * is already running.
1490 */
1491 int
1492 xerrorstart(Display *dsply, XErrorEvent *ee) {
1493 otherwm = True;
1494 return -1;
1497 void
1498 sendevent(Window w, Atom a, long value) {
1499 XEvent e;
1501 e.type = ClientMessage;
1502 e.xclient.window = w;
1503 e.xclient.message_type = a;
1504 e.xclient.format = 32;
1505 e.xclient.data.l[0] = value;
1506 e.xclient.data.l[1] = CurrentTime;
1507 XSendEvent(dpy, w, False, NoEventMask, &e);
1508 XSync(dpy, False);
1511 void
1512 quit() {
1513 running = False;
1516 /* There's no way to check accesses to destroyed windows, thus those cases are
1517 * ignored (especially on UnmapNotify's). Other types of errors call Xlibs
1518 * default error handler, which may call exit.
1519 */
1520 int
1521 xerror(Display *dpy, XErrorEvent *ee) {
1522 if(ee->error_code == BadWindow
1523 || (ee->request_code == X_SetInputFocus && ee->error_code == BadMatch)
1524 || (ee->request_code == X_PolyText8 && ee->error_code == BadDrawable)
1525 || (ee->request_code == X_PolyFillRectangle && ee->error_code == BadDrawable)
1526 || (ee->request_code == X_PolySegment && ee->error_code == BadDrawable)
1527 || (ee->request_code == X_ConfigureWindow && ee->error_code == BadMatch)
1528 || (ee->request_code == X_GrabKey && ee->error_code == BadAccess)
1529 || (ee->request_code == X_CopyArea && ee->error_code == BadDrawable))
1530 return 0;
1531 fprintf(stderr, "aewl: fatal error: request code=%d, error code=%d\n",
1532 ee->request_code, ee->error_code);
1533 return xerrorxlib(dpy, ee); /* may call exit */
1536 void
1537 checkotherwm(void) {
1538 otherwm = False;
1539 xerrorxlib = XSetErrorHandler(xerrorstart);
1540 /* this causes an error if some other window manager is running */
1541 XSelectInput(dpy, DefaultRootWindow(dpy), SubstructureRedirectMask);
1542 XSync(dpy, False);
1543 if(otherwm) {
1544 die("aewl: another window manager is already running\n");
1547 XSetErrorHandler(xerror);
1548 XSync(dpy, False);
1551 void
1552 run(void) {
1553 XEvent ev;
1555 XSync(dpy, False);
1556 /* main event loop */
1557 while(running && !XNextEvent(dpy, &ev)) {
1558 if(handler[ev.type]) {
1559 (handler[ev.type])(&ev); /* call handler */
1564 void
1565 updatestatus(void) {
1566 if (!gettextprop(root, XA_WM_NAME, stext, sizeof(stext))) {
1567 strcpy(stext, "aewl-"VERSION);
1569 drawbar();
1572 int
1573 main(int argc, char *argv[]) {
1575 if(argc == 2 && !strncmp("-v", argv[1], 3)) {
1576 fputs("aewl-"VERSION", Copyright 2008 markus schnalke <meillo@marmaro.de>\n", stdout);
1577 fputs("forked off dwm-3.4, (C)opyright MMVI-MMVII Anselm R. Garbe\n", stdout);
1578 exit(EXIT_SUCCESS);
1579 } else if(argc != 1) {
1580 die("usage: aewl [-v]\n");
1582 setlocale(LC_CTYPE, "");
1583 dpy = XOpenDisplay(0);
1584 if(!dpy) {
1585 die("aewl: cannot open display\n");
1588 checkotherwm();
1589 setup();
1590 scan();
1591 run();
1592 cleanup();
1594 XCloseDisplay(dpy);
1595 return 0;