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