Mercurial > aewl
diff dwm.c @ 754:4c12dccc288d
this is the (second) begin of aewl - my personal stipped down dwm
author | meillo@marmaro.de |
---|---|
date | Thu, 29 May 2008 23:12:30 +0200 |
parents | main.c@628c5bac7f3b |
children | cdd895c163bd |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwm.c Thu May 29 23:12:30 2008 +0200 @@ -0,0 +1,1846 @@ +/* (C)opyright MMVI-MMVII Anselm R. Garbe <garbeam at gmail dot com> + * See LICENSE file for license details. + * + * dynamic window manager is designed like any other X client as well. It is + * driven through handling X events. In contrast to other X clients, a window + * manager selects for SubstructureRedirectMask on the root window, to receive + * events about window (dis-)appearance. Only one X connection at a time is + * allowed to select for this event mask. + * + * Calls to fetch an X event from the event queue are blocking. Due reading + * status text from standard input, a select()-driven main loop has been + * implemented which selects for reads on the X connection and STDIN_FILENO to + * handle all data smoothly. The event handlers of dwm are organized in an + * array which is accessed whenever a new event has been fetched. This allows + * event dispatching in O(1) time. + * + * Each child of the root window is called a client, except windows which have + * set the override_redirect flag. Clients are organized in a global + * doubly-linked client list, the focus history is remembered through a global + * stack list. Each client contains an array of Bools of the same size as the + * global tags array to indicate the tags of a client. For each client dwm + * creates a small title window, which is resized whenever the (_NET_)WM_NAME + * properties are updated or the client is moved/resized. + * + * Keys and tagging rules are organized as arrays and defined in the config.h + * file. These arrays are kept static in event.o and tag.o respectively, + * because no other part of dwm needs access to them. The current mode is + * represented by the arrange() function pointer, which wether points to + * dofloat() or dotile(). + * + * To understand everything else, start reading main.c:main(). + */ + +#include "config.h" +#include <errno.h> +#include <locale.h> +#include <regex.h> +#include <stdio.h> +#include <stdarg.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/select.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <X11/cursorfont.h> +#include <X11/keysym.h> +#include <X11/Xatom.h> +#include <X11/Xlib.h> +#include <X11/Xproto.h> +#include <X11/Xutil.h> + +/* mask shorthands, used in event.c and client.c */ +#define BUTTONMASK (ButtonPressMask | ButtonReleaseMask) + +enum { NetSupported, NetWMName, NetLast }; /* EWMH atoms */ +enum { WMProtocols, WMDelete, WMState, WMLast }; /* default atoms */ +enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */ +enum { ColBorder, ColFG, ColBG, ColLast }; /* color */ + +typedef union { + const char *cmd; + int i; +} Arg; /* argument type */ + +typedef struct { + int ascent; + int descent; + int height; + XFontSet set; + XFontStruct *xfont; +} Fnt; + +typedef struct { + int x, y, w, h; + unsigned long norm[ColLast]; + unsigned long sel[ColLast]; + Drawable drawable; + Fnt font; + GC gc; +} DC; /* draw context */ + +typedef struct Client Client; +struct Client { + char name[256]; + int x, y, w, h; + int rx, ry, rw, rh; /* revert geometry */ + int basew, baseh, incw, inch, maxw, maxh, minw, minh; + int minax, minay, maxax, maxay; + long flags; + unsigned int border; + Bool isfixed, isfloat, ismax; + Bool *tags; + Client *next; + Client *prev; + Client *snext; + Window win; +}; + +typedef struct { + const char *clpattern; + const char *tpattern; + Bool isfloat; +} Rule; + +typedef struct { + regex_t *clregex; + regex_t *tregex; +} RReg; + + +typedef struct { + unsigned long mod; + KeySym keysym; + void (*func)(Arg *arg); + Arg arg; +} Key; + + +#define CLEANMASK(mask) (mask & ~(numlockmask | LockMask)) +#define MOUSEMASK (BUTTONMASK | PointerMotionMask) + + + +const char *tags[]; /* all tags */ +char stext[256]; /* status text */ +int bh, bmw; /* bar height, bar mode label width */ +int screen, sx, sy, sw, sh; /* screen geometry */ +int wax, way, wah, waw; /* windowarea geometry */ +unsigned int nmaster; /* number of master clients */ +unsigned int ntags, numlockmask; /* number of tags, dynamic lock mask */ +void (*handler[LASTEvent])(XEvent *); /* event handler */ +void (*arrange)(void); /* arrange function, indicates mode */ +Atom wmatom[WMLast], netatom[NetLast]; +Bool running, selscreen, *seltag; /* seltag is array of Bool */ +Client *clients, *sel, *stack; /* global client list and stack */ +Cursor cursor[CurLast]; +DC dc; /* global draw context */ +Display *dpy; +Window root, barwin; + +Bool running = True; +Bool selscreen = True; +Client *clients = NULL; +Client *sel = NULL; +Client *stack = NULL; +DC dc = {0}; + +static int (*xerrorxlib)(Display *, XErrorEvent *); +static Bool otherwm, readin; +static RReg *rreg = NULL; +static unsigned int len = 0; + + +TAGS +RULES + + +/* client.c */ +void configure(Client *c); /* send synthetic configure event */ +void focus(Client *c); /* focus c, c may be NULL */ +Client *getclient(Window w); /* return client of w */ +Bool isprotodel(Client *c); /* returns True if c->win supports wmatom[WMDelete] */ +void killclient(Arg *arg); /* kill c nicely */ +void manage(Window w, XWindowAttributes *wa); /* manage new client */ +void resize(Client *c, Bool sizehints); /* resize c*/ +void updatesizehints(Client *c); /* update the size hint variables of c */ +void updatetitle(Client *c); /* update the name of c */ +void unmanage(Client *c); /* destroy c */ + +/* draw.c */ +void drawstatus(void); /* draw the bar */ +unsigned long getcolor(const char *colstr); /* return color of colstr */ +void setfont(const char *fontstr); /* set the font for DC */ +unsigned int textw(const char *text); /* return the width of text in px*/ + +/* event.c */ +void grabkeys(void); /* grab all keys defined in config.h */ +void procevent(void); /* process pending X events */ + +/* main.c */ +void quit(Arg *arg); /* quit dwm nicely */ +void sendevent(Window w, Atom a, long value); /* send synthetic event to w */ +int xerror(Display *dsply, XErrorEvent *ee); /* dwm's X error handler */ + +/* tag.c */ +void initrregs(void); /* initialize regexps of rules defined in config.h */ +Client *getnext(Client *c); /* returns next visible client */ +Client *getprev(Client *c); /* returns previous visible client */ +void settags(Client *c, Client *trans); /* sets tags of c */ +void tag(Arg *arg); /* tags c with arg's index */ +void toggletag(Arg *arg); /* toggles c tags with arg's index */ +void viewnext(Arg *arg); /* view next tag(s) */ + +/* util.c */ +void *emallocz(unsigned int size); /* allocates zero-initialized memory, exits on error */ +void eprint(const char *errstr, ...); /* prints errstr and exits with 1 */ +void spawn(Arg *arg); /* forks a new subprocess with to arg's cmd */ + +/* view.c */ +void detach(Client *c); /* detaches c from global client list */ +void dofloat(void); /* arranges all windows floating */ +void dotile(void); /* arranges all windows tiled */ +void domax(void); /* arranges all windows fullscreen */ +void focusnext(Arg *arg); /* focuses next visible client, arg is ignored */ +void incnmaster(Arg *arg); /* increments nmaster with arg's index value */ +Bool isvisible(Client *c); /* returns True if client is visible */ +void restack(void); /* restores z layers of all clients */ +void togglefloat(Arg *arg); /* toggles focusesd client between floating/non-floating state */ +void togglemode(Arg *arg); /* toggles global arrange function (dotile/dofloat) */ +void view(Arg *arg); /* views the tag with arg's index */ +void zoom(Arg *arg); /* zooms the focused client to master area, arg is ignored */ + + + + + + + + + + +/* from view.c */ +/* static */ + +static Client * +nexttiled(Client *c) { + for(c = getnext(c); c && c->isfloat; c = getnext(c->next)); + return c; +} + +static void +togglemax(Client *c) { + XEvent ev; + + if(c->isfixed) + return; + + if((c->ismax = !c->ismax)) { + c->rx = c->x; c->x = wax; + c->ry = c->y; c->y = way; + c->rw = c->w; c->w = waw - 2 * BORDERPX; + c->rh = c->h; c->h = wah - 2 * BORDERPX; + } + else { + c->x = c->rx; + c->y = c->ry; + c->w = c->rw; + c->h = c->rh; + } + resize(c, True); + while(XCheckMaskEvent(dpy, EnterWindowMask, &ev)); +} + + + +void (*arrange)(void) = DEFMODE; + +void +detach(Client *c) { + if(c->prev) + c->prev->next = c->next; + if(c->next) + c->next->prev = c->prev; + if(c == clients) + clients = c->next; + c->next = c->prev = NULL; +} + +void +dofloat(void) { + Client *c; + + for(c = clients; c; c = c->next) { + if(isvisible(c)) { + resize(c, True); + } + else + XMoveWindow(dpy, c->win, c->x + 2 * sw, c->y); + } + if(!sel || !isvisible(sel)) { + for(c = stack; c && !isvisible(c); c = c->snext); + focus(c); + } + restack(); +} + +void +dotile(void) { + unsigned int i, n, mw, mh, tw, th; + Client *c; + + for(n = 0, c = nexttiled(clients); c; c = nexttiled(c->next)) + n++; + /* window geoms */ + mh = (n > nmaster) ? wah / nmaster : wah / (n > 0 ? n : 1); + mw = (n > nmaster) ? waw / 2 : waw; + th = (n > nmaster) ? wah / (n - nmaster) : 0; + tw = waw - mw; + + for(i = 0, c = clients; c; c = c->next) + if(isvisible(c)) { + if(c->isfloat) { + resize(c, True); + continue; + } + c->ismax = False; + c->x = wax; + c->y = way; + if(i < nmaster) { + c->y += i * mh; + c->w = mw - 2 * BORDERPX; + c->h = mh - 2 * BORDERPX; + } + else { /* tile window */ + c->x += mw; + c->w = tw - 2 * BORDERPX; + if(th > 2 * BORDERPX) { + c->y += (i - nmaster) * th; + c->h = th - 2 * BORDERPX; + } + else /* fallback if th <= 2 * BORDERPX */ + c->h = wah - 2 * BORDERPX; + } + resize(c, False); + i++; + } + else + XMoveWindow(dpy, c->win, c->x + 2 * sw, c->y); + if(!sel || !isvisible(sel)) { + for(c = stack; c && !isvisible(c); c = c->snext); + focus(c); + } + restack(); +} + +/* begin code by mitch */ +void +arrangemax(Client *c) { + if(c == sel) { + c->ismax = True; + c->x = sx; + c->y = bh; + c->w = sw - 2 * BORDERPX; + c->h = sh - bh - 2 * BORDERPX; + XRaiseWindow(dpy, c->win); + } else { + c->ismax = False; + XMoveWindow(dpy, c->win, c->x + 2 * sw, c->y); + XLowerWindow(dpy, c->win); + } +} + +void +domax(void) { + Client *c; + + for(c = clients; c; c = c->next) { + if(isvisible(c)) { + if(c->isfloat) { + resize(c, True); + continue; + } + arrangemax(c); + resize(c, False); + } else { + XMoveWindow(dpy, c->win, c->x + 2 * sw, c->y); + } + + } + if(!sel || !isvisible(sel)) { + for(c = stack; c && !isvisible(c); c = c->snext); + focus(c); + } + restack(); +} +/* end code by mitch */ + +void +focusnext(Arg *arg) { + Client *c; + + if(!sel) + return; + if(!(c = getnext(sel->next))) + c = getnext(clients); + if(c) { + focus(c); + restack(); + } +} + +void +incnmaster(Arg *arg) { + if((arrange == dofloat) || (nmaster + arg->i < 1) + || (wah / (nmaster + arg->i) <= 2 * BORDERPX)) + return; + nmaster += arg->i; + if(sel) + arrange(); + else + drawstatus(); +} + +Bool +isvisible(Client *c) { + unsigned int i; + + for(i = 0; i < ntags; i++) + if(c->tags[i] && seltag[i]) + return True; + return False; +} + +void +restack(void) { + Client *c; + XEvent ev; + + drawstatus(); + if(!sel) + return; + if(sel->isfloat || arrange == dofloat) + XRaiseWindow(dpy, sel->win); + + /* begin code by mitch */ + if(arrange == domax) { + for(c = nexttiled(clients); c; c = nexttiled(c->next)) { + arrangemax(c); + resize(c, False); + } + + } else if (arrange == dotile) { + /* end code by mitch */ + + if(!sel->isfloat) + XLowerWindow(dpy, sel->win); + for(c = nexttiled(clients); c; c = nexttiled(c->next)) { + if(c == sel) + continue; + XLowerWindow(dpy, c->win); + } + } + XSync(dpy, False); + while(XCheckMaskEvent(dpy, EnterWindowMask, &ev)); +} + +void +togglefloat(Arg *arg) { + if (!sel || arrange == dofloat) + return; + sel->isfloat = !sel->isfloat; + arrange(); +} + +void +togglemode(Arg *arg) { + /* only toggle between tile and max - float is just available through togglefloat */ + arrange = (arrange == dotile) ? domax : dotile; + if(sel) + arrange(); + else + drawstatus(); +} + +void +view(Arg *arg) { + unsigned int i; + + for(i = 0; i < ntags; i++) + seltag[i] = (arg->i == -1) ? True : False; + if(arg->i >= 0 && arg->i < ntags) + seltag[arg->i] = True; + arrange(); +} + +void +zoom(Arg *arg) { + unsigned int n; + Client *c; + + if(!sel) + return; + if(sel->isfloat || (arrange == dofloat)) { + togglemax(sel); + return; + } + for(n = 0, c = nexttiled(clients); c; c = nexttiled(c->next)) + n++; + + if((c = sel) == nexttiled(clients)) + if(!(c = nexttiled(c->next))) + return; + detach(c); + if(clients) + clients->prev = c; + c->next = clients; + clients = c; + focus(c); + arrange(); +} + + + + + + + + + + + + + + + + +/* from util.c */ + + +void * +emallocz(unsigned int size) { + void *res = calloc(1, size); + + if(!res) + eprint("fatal: could not malloc() %u bytes\n", size); + return res; +} + +void +eprint(const char *errstr, ...) { + va_list ap; + + va_start(ap, errstr); + vfprintf(stderr, errstr, ap); + va_end(ap); + exit(EXIT_FAILURE); +} + +void +spawn(Arg *arg) { + static char *shell = NULL; + + if(!shell && !(shell = getenv("SHELL"))) + shell = "/bin/sh"; + if(!arg->cmd) + return; + /* The double-fork construct avoids zombie processes and keeps the code + * clean from stupid signal handlers. */ + if(fork() == 0) { + if(fork() == 0) { + if(dpy) + close(ConnectionNumber(dpy)); + setsid(); + execl(shell, shell, "-c", arg->cmd, (char *)NULL); + fprintf(stderr, "dwm: execl '%s -c %s'", shell, arg->cmd); + perror(" failed"); + } + exit(0); + } + wait(0); +} + + + + + + + + + + + + + +/* from tag.c */ + +/* static */ + +Client * +getnext(Client *c) { + for(; c && !isvisible(c); c = c->next); + return c; +} + +Client * +getprev(Client *c) { + for(; c && !isvisible(c); c = c->prev); + return c; +} + +void +initrregs(void) { + unsigned int i; + regex_t *reg; + + if(rreg) + return; + len = sizeof rule / sizeof rule[0]; + rreg = emallocz(len * sizeof(RReg)); + for(i = 0; i < len; i++) { + if(rule[i].clpattern) { + reg = emallocz(sizeof(regex_t)); + if(regcomp(reg, rule[i].clpattern, REG_EXTENDED)) + free(reg); + else + rreg[i].clregex = reg; + } + if(rule[i].tpattern) { + reg = emallocz(sizeof(regex_t)); + if(regcomp(reg, rule[i].tpattern, REG_EXTENDED)) + free(reg); + else + rreg[i].tregex = reg; + } + } +} + +void +settags(Client *c, Client *trans) { + char prop[512]; + unsigned int i, j; + regmatch_t tmp; + Bool matched = trans != NULL; + XClassHint ch = { 0 }; + + if(matched) { + for(i = 0; i < ntags; i++) + c->tags[i] = trans->tags[i]; + } + else { + XGetClassHint(dpy, c->win, &ch); + snprintf(prop, sizeof prop, "%s:%s:%s", + ch.res_class ? ch.res_class : "", + ch.res_name ? ch.res_name : "", c->name); + for(i = 0; i < len; i++) + if(rreg[i].clregex && !regexec(rreg[i].clregex, prop, 1, &tmp, 0)) { + c->isfloat = rule[i].isfloat; + for(j = 0; rreg[i].tregex && j < ntags; j++) { + if(!regexec(rreg[i].tregex, tags[j], 1, &tmp, 0)) { + matched = True; + c->tags[j] = True; + } + } + break; /* perform only the first rule matching */ + } + if(ch.res_class) + XFree(ch.res_class); + if(ch.res_name) + XFree(ch.res_name); + } + if(!matched) + for(i = 0; i < ntags; i++) + c->tags[i] = seltag[i]; +} + +void +tag(Arg *arg) { + unsigned int i; + + if(!sel) + return; + for(i = 0; i < ntags; i++) + sel->tags[i] = (arg->i == -1) ? True : False; + if(arg->i >= 0 && arg->i < ntags) + sel->tags[arg->i] = True; + arrange(); +} + +void +toggletag(Arg *arg) { + unsigned int i; + + if(!sel) + return; + sel->tags[arg->i] = !sel->tags[arg->i]; + for(i = 0; i < ntags && !sel->tags[i]; i++); + if(i == ntags) + sel->tags[arg->i] = True; + arrange(); +} + +/* begin code by jukka */ +void +viewnext(Arg *arg) { + unsigned int i; + Bool last = seltag[ntags-1]; + + for (i=ntags-1; i>0; --i) + seltag[i] = seltag[i-1]; + seltag[0] = last; + arrange(); +} +/* end code by jukka */ + + + + + + + + + + + + + + + + +/* from event.c */ +/* static */ + +KEYS + + + +static void +movemouse(Client *c) { + int x1, y1, ocx, ocy, di; + unsigned int dui; + Window dummy; + XEvent ev; + + ocx = c->x; + ocy = c->y; + if(XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync, + None, cursor[CurMove], CurrentTime) != GrabSuccess) + return; + c->ismax = False; + XQueryPointer(dpy, root, &dummy, &dummy, &x1, &y1, &di, &di, &dui); + for(;;) { + XMaskEvent(dpy, MOUSEMASK | ExposureMask | SubstructureRedirectMask, &ev); + switch (ev.type) { + case ButtonRelease: + resize(c, True); + XUngrabPointer(dpy, CurrentTime); + return; + case ConfigureRequest: + case Expose: + case MapRequest: + handler[ev.type](&ev); + break; + case MotionNotify: + XSync(dpy, False); + c->x = ocx + (ev.xmotion.x - x1); + c->y = ocy + (ev.xmotion.y - y1); + if(abs(wax + c->x) < SNAP) + c->x = wax; + else if(abs((wax + waw) - (c->x + c->w + 2 * c->border)) < SNAP) + c->x = wax + waw - c->w - 2 * c->border; + if(abs(way - c->y) < SNAP) + c->y = way; + else if(abs((way + wah) - (c->y + c->h + 2 * c->border)) < SNAP) + c->y = way + wah - c->h - 2 * c->border; + resize(c, False); + break; + } + } +} + +static void +resizemouse(Client *c) { + int ocx, ocy; + int nw, nh; + XEvent ev; + + ocx = c->x; + ocy = c->y; + if(XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync, + None, cursor[CurResize], CurrentTime) != GrabSuccess) + return; + c->ismax = False; + XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->border - 1, c->h + c->border - 1); + for(;;) { + XMaskEvent(dpy, MOUSEMASK | ExposureMask | SubstructureRedirectMask , &ev); + switch(ev.type) { + case ButtonRelease: + resize(c, True); + XUngrabPointer(dpy, CurrentTime); + return; + case ConfigureRequest: + case Expose: + case MapRequest: + handler[ev.type](&ev); + break; + case MotionNotify: + XSync(dpy, False); + nw = ev.xmotion.x - ocx - 2 * c->border + 1; + c->w = nw > 0 ? nw : 1; + nh = ev.xmotion.y - ocy - 2 * c->border + 1; + c->h = nh > 0 ? nh : 1; + resize(c, True); + break; + } + } +} + +static void +buttonpress(XEvent *e) { + int x; + Arg a; + Client *c; + XButtonPressedEvent *ev = &e->xbutton; + + if(barwin == ev->window) { + x = 0; + for(a.i = 0; a.i < ntags; a.i++) { + x += textw(tags[a.i]); + if(ev->x < x) { + if(ev->button == Button1) { + view(&a); + } + return; + } + } + if(ev->x < x + bmw) + if (ev->button == Button1) { + togglemode(NULL); + } + } + else if((c = getclient(ev->window))) { + focus(c); + if(CLEANMASK(ev->state) != MODKEY) + return; + if(ev->button == Button1 && (arrange == dofloat || c->isfloat)) { + restack(); + movemouse(c); + } + else if(ev->button == Button2) + zoom(NULL); + else if(ev->button == Button3 && (arrange == dofloat || c->isfloat) && + !c->isfixed) { + restack(); + resizemouse(c); + } + } +} + +static void +configurerequest(XEvent *e) { + unsigned long newmask; + Client *c; + XConfigureRequestEvent *ev = &e->xconfigurerequest; + XWindowChanges wc; + + if((c = getclient(ev->window))) { + c->ismax = False; + if(ev->value_mask & CWX) + c->x = ev->x; + if(ev->value_mask & CWY) + c->y = ev->y; + if(ev->value_mask & CWWidth) + c->w = ev->width; + if(ev->value_mask & CWHeight) + c->h = ev->height; + if(ev->value_mask & CWBorderWidth) + c->border = ev->border_width; + wc.x = c->x; + wc.y = c->y; + wc.width = c->w; + wc.height = c->h; + newmask = ev->value_mask & (~(CWSibling | CWStackMode | CWBorderWidth)); + if(newmask) + XConfigureWindow(dpy, c->win, newmask, &wc); + else + configure(c); + XSync(dpy, False); + if(c->isfloat) { + resize(c, False); + if(!isvisible(c)) + XMoveWindow(dpy, c->win, c->x + 2 * sw, c->y); + } + else + arrange(); + } + else { + wc.x = ev->x; + wc.y = ev->y; + wc.width = ev->width; + wc.height = ev->height; + wc.border_width = ev->border_width; + wc.sibling = ev->above; + wc.stack_mode = ev->detail; + XConfigureWindow(dpy, ev->window, ev->value_mask, &wc); + XSync(dpy, False); + } +} + +static void +destroynotify(XEvent *e) { + Client *c; + XDestroyWindowEvent *ev = &e->xdestroywindow; + + if((c = getclient(ev->window))) + unmanage(c); +} + +static void +enternotify(XEvent *e) { + Client *c; + XCrossingEvent *ev = &e->xcrossing; + + if(ev->mode != NotifyNormal || ev->detail == NotifyInferior) + return; + if((c = getclient(ev->window)) && isvisible(c)) + focus(c); + else if(ev->window == root) { + selscreen = True; + for(c = stack; c && !isvisible(c); c = c->snext); + focus(c); + } +} + +static void +expose(XEvent *e) { + XExposeEvent *ev = &e->xexpose; + + if(ev->count == 0) { + if(barwin == ev->window) + drawstatus(); + } +} + +static void +keypress(XEvent *e) { + static unsigned int len = sizeof key / sizeof key[0]; + unsigned int i; + KeySym keysym; + XKeyEvent *ev = &e->xkey; + + keysym = XKeycodeToKeysym(dpy, (KeyCode)ev->keycode, 0); + for(i = 0; i < len; i++) { + if(keysym == key[i].keysym + && CLEANMASK(key[i].mod) == CLEANMASK(ev->state)) + { + if(key[i].func) + key[i].func(&key[i].arg); + } + } +} + +static void +leavenotify(XEvent *e) { + XCrossingEvent *ev = &e->xcrossing; + + if((ev->window == root) && !ev->same_screen) { + selscreen = False; + focus(NULL); + } +} + +static void +mappingnotify(XEvent *e) { + XMappingEvent *ev = &e->xmapping; + + XRefreshKeyboardMapping(ev); + if(ev->request == MappingKeyboard) + grabkeys(); +} + +static void +maprequest(XEvent *e) { + static XWindowAttributes wa; + XMapRequestEvent *ev = &e->xmaprequest; + + if(!XGetWindowAttributes(dpy, ev->window, &wa)) + return; + if(wa.override_redirect) { + XSelectInput(dpy, ev->window, + (StructureNotifyMask | PropertyChangeMask)); + return; + } + if(!getclient(ev->window)) + manage(ev->window, &wa); +} + +static void +propertynotify(XEvent *e) { + Client *c; + Window trans; + XPropertyEvent *ev = &e->xproperty; + + if(ev->state == PropertyDelete) + return; /* ignore */ + if((c = getclient(ev->window))) { + switch (ev->atom) { + default: break; + case XA_WM_TRANSIENT_FOR: + XGetTransientForHint(dpy, c->win, &trans); + if(!c->isfloat && (c->isfloat = (trans != 0))) + arrange(); + break; + case XA_WM_NORMAL_HINTS: + updatesizehints(c); + break; + } + if(ev->atom == XA_WM_NAME || ev->atom == netatom[NetWMName]) { + updatetitle(c); + if(c == sel) + drawstatus(); + } + } +} + +static void +unmapnotify(XEvent *e) { + Client *c; + XUnmapEvent *ev = &e->xunmap; + + if((c = getclient(ev->window))) + unmanage(c); +} + + + +void (*handler[LASTEvent]) (XEvent *) = { + [ButtonPress] = buttonpress, + [ConfigureRequest] = configurerequest, + [DestroyNotify] = destroynotify, + [EnterNotify] = enternotify, + [LeaveNotify] = leavenotify, + [Expose] = expose, + [KeyPress] = keypress, + [MappingNotify] = mappingnotify, + [MapRequest] = maprequest, + [PropertyNotify] = propertynotify, + [UnmapNotify] = unmapnotify +}; + +void +grabkeys(void) { + static unsigned int len = sizeof key / sizeof key[0]; + unsigned int i; + KeyCode code; + + XUngrabKey(dpy, AnyKey, AnyModifier, root); + for(i = 0; i < len; i++) { + code = XKeysymToKeycode(dpy, key[i].keysym); + XGrabKey(dpy, code, key[i].mod, root, True, + GrabModeAsync, GrabModeAsync); + XGrabKey(dpy, code, key[i].mod | LockMask, root, True, + GrabModeAsync, GrabModeAsync); + XGrabKey(dpy, code, key[i].mod | numlockmask, root, True, + GrabModeAsync, GrabModeAsync); + XGrabKey(dpy, code, key[i].mod | numlockmask | LockMask, root, True, + GrabModeAsync, GrabModeAsync); + } +} + +void +procevent(void) { + XEvent ev; + + while(XPending(dpy)) { + XNextEvent(dpy, &ev); + if(handler[ev.type]) + (handler[ev.type])(&ev); /* call handler */ + } +} + + + + + + + + + + + + + + + +/* from draw.c */ +/* static */ + +static unsigned int +textnw(const char *text, unsigned int len) { + XRectangle r; + + if(dc.font.set) { + XmbTextExtents(dc.font.set, text, len, NULL, &r); + return r.width; + } + return XTextWidth(dc.font.xfont, text, len); +} + +static void +drawtext(const char *text, unsigned long col[ColLast]) { + int x, y, w, h; + static char buf[256]; + unsigned int len, olen; + XGCValues gcv; + XRectangle r = { dc.x, dc.y, dc.w, dc.h }; + + XSetForeground(dpy, dc.gc, col[ColBG]); + XFillRectangles(dpy, dc.drawable, dc.gc, &r, 1); + if(!text) + return; + w = 0; + olen = len = strlen(text); + if(len >= sizeof buf) + len = sizeof buf - 1; + memcpy(buf, text, len); + buf[len] = 0; + h = dc.font.ascent + dc.font.descent; + y = dc.y + (dc.h / 2) - (h / 2) + dc.font.ascent; + x = dc.x + (h / 2); + /* shorten text if necessary */ + while(len && (w = textnw(buf, len)) > dc.w - h) + buf[--len] = 0; + if(len < olen) { + if(len > 1) + buf[len - 1] = '.'; + if(len > 2) + buf[len - 2] = '.'; + if(len > 3) + buf[len - 3] = '.'; + } + if(w > dc.w) + return; /* too long */ + gcv.foreground = col[ColFG]; + if(dc.font.set) { + XChangeGC(dpy, dc.gc, GCForeground, &gcv); + XmbDrawString(dpy, dc.drawable, dc.font.set, dc.gc, x, y, buf, len); + } else { + gcv.font = dc.font.xfont->fid; + XChangeGC(dpy, dc.gc, GCForeground | GCFont, &gcv); + XDrawString(dpy, dc.drawable, dc.gc, x, y, buf, len); + } +} + + + +void +drawstatus(void) { + int i, x; + + dc.x = dc.y = 0; + for(i = 0; i < ntags; i++) { + dc.w = textw(tags[i]); + drawtext(tags[i], (seltag[i] ? dc.sel : dc.norm)); + dc.x += dc.w + 1; + } + dc.w = bmw; + drawtext("", dc.norm); + x = dc.x + dc.w; + dc.w = textw(stext); + dc.x = sw - dc.w; + if(dc.x < x) { + dc.x = x; + dc.w = sw - x; + } + drawtext(stext, dc.norm); + if((dc.w = dc.x - x) > bh) { + dc.x = x; + drawtext(sel ? sel->name : NULL, dc.norm); + } + XCopyArea(dpy, dc.drawable, barwin, dc.gc, 0, 0, sw, bh, 0, 0); + XSync(dpy, False); +} + +unsigned long +getcolor(const char *colstr) { + Colormap cmap = DefaultColormap(dpy, screen); + XColor color; + + if(!XAllocNamedColor(dpy, cmap, colstr, &color, &color)) + eprint("error, cannot allocate color '%s'\n", colstr); + return color.pixel; +} + +void +setfont(const char *fontstr) { + char *def, **missing; + int i, n; + + missing = NULL; + if(dc.font.set) + XFreeFontSet(dpy, dc.font.set); + dc.font.set = XCreateFontSet(dpy, fontstr, &missing, &n, &def); + if(missing) { + while(n--) + fprintf(stderr, "missing fontset: %s\n", missing[n]); + XFreeStringList(missing); + } + if(dc.font.set) { + XFontSetExtents *font_extents; + XFontStruct **xfonts; + char **font_names; + dc.font.ascent = dc.font.descent = 0; + font_extents = XExtentsOfFontSet(dc.font.set); + n = XFontsOfFontSet(dc.font.set, &xfonts, &font_names); + for(i = 0, dc.font.ascent = 0, dc.font.descent = 0; i < n; i++) { + if(dc.font.ascent < (*xfonts)->ascent) + dc.font.ascent = (*xfonts)->ascent; + if(dc.font.descent < (*xfonts)->descent) + dc.font.descent = (*xfonts)->descent; + xfonts++; + } + } else { + if(dc.font.xfont) + XFreeFont(dpy, dc.font.xfont); + dc.font.xfont = NULL; + if(!(dc.font.xfont = XLoadQueryFont(dpy, fontstr))) + eprint("error, cannot load font: '%s'\n", fontstr); + dc.font.ascent = dc.font.xfont->ascent; + dc.font.descent = dc.font.xfont->descent; + } + dc.font.height = dc.font.ascent + dc.font.descent; +} + +unsigned int +textw(const char *text) { + return textnw(text, strlen(text)) + dc.font.height; +} + + + + + + + + + + + +/* from client.c */ +/* static */ + +static void +detachstack(Client *c) { + Client **tc; + for(tc=&stack; *tc && *tc != c; tc=&(*tc)->snext); + *tc = c->snext; +} + +static void +grabbuttons(Client *c, Bool focused) { + XUngrabButton(dpy, AnyButton, AnyModifier, c->win); + + if(focused) { + XGrabButton(dpy, Button1, MODKEY, c->win, False, BUTTONMASK, + GrabModeAsync, GrabModeSync, None, None); + XGrabButton(dpy, Button1, MODKEY | LockMask, c->win, False, BUTTONMASK, + GrabModeAsync, GrabModeSync, None, None); + XGrabButton(dpy, Button1, MODKEY | numlockmask, c->win, False, BUTTONMASK, + GrabModeAsync, GrabModeSync, None, None); + XGrabButton(dpy, Button1, MODKEY | numlockmask | LockMask, c->win, False, BUTTONMASK, + GrabModeAsync, GrabModeSync, None, None); + + XGrabButton(dpy, Button2, MODKEY, c->win, False, BUTTONMASK, + GrabModeAsync, GrabModeSync, None, None); + XGrabButton(dpy, Button2, MODKEY | LockMask, c->win, False, BUTTONMASK, + GrabModeAsync, GrabModeSync, None, None); + XGrabButton(dpy, Button2, MODKEY | numlockmask, c->win, False, BUTTONMASK, + GrabModeAsync, GrabModeSync, None, None); + XGrabButton(dpy, Button2, MODKEY | numlockmask | LockMask, c->win, False, BUTTONMASK, + GrabModeAsync, GrabModeSync, None, None); + + XGrabButton(dpy, Button3, MODKEY, c->win, False, BUTTONMASK, + GrabModeAsync, GrabModeSync, None, None); + XGrabButton(dpy, Button3, MODKEY | LockMask, c->win, False, BUTTONMASK, + GrabModeAsync, GrabModeSync, None, None); + XGrabButton(dpy, Button3, MODKEY | numlockmask, c->win, False, BUTTONMASK, + GrabModeAsync, GrabModeSync, None, None); + XGrabButton(dpy, Button3, MODKEY | numlockmask | LockMask, c->win, False, BUTTONMASK, + GrabModeAsync, GrabModeSync, None, None); + } else { + XGrabButton(dpy, AnyButton, AnyModifier, c->win, False, BUTTONMASK, + GrabModeAsync, GrabModeSync, None, None); + } +} + +static void +setclientstate(Client *c, long state) { + long data[] = {state, None}; + XChangeProperty(dpy, c->win, wmatom[WMState], wmatom[WMState], 32, + PropModeReplace, (unsigned char *)data, 2); +} + +static int +xerrordummy(Display *dsply, XErrorEvent *ee) { + return 0; +} + + + +void +configure(Client *c) { + XEvent synev; + + synev.type = ConfigureNotify; + synev.xconfigure.display = dpy; + synev.xconfigure.event = c->win; + synev.xconfigure.window = c->win; + synev.xconfigure.x = c->x; + synev.xconfigure.y = c->y; + synev.xconfigure.width = c->w; + synev.xconfigure.height = c->h; + synev.xconfigure.border_width = c->border; + synev.xconfigure.above = None; + XSendEvent(dpy, c->win, True, NoEventMask, &synev); +} + +void +focus(Client *c) { + if(c && !isvisible(c)) + return; + if(sel && sel != c) { + grabbuttons(sel, False); + XSetWindowBorder(dpy, sel->win, dc.norm[ColBorder]); + } + if(c) { + detachstack(c); + c->snext = stack; + stack = c; + grabbuttons(c, True); + } + sel = c; + drawstatus(); + if(!selscreen) + return; + if(c) { + XSetWindowBorder(dpy, c->win, dc.sel[ColBorder]); + XSetInputFocus(dpy, c->win, RevertToPointerRoot, CurrentTime); + } else { + XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime); + } +} + +Client * +getclient(Window w) { + Client *c; + + for(c = clients; c; c = c->next) { + if(c->win == w) { + return c; + } + } + return NULL; +} + +Bool +isprotodel(Client *c) { + int i, n; + Atom *protocols; + Bool ret = False; + + if(XGetWMProtocols(dpy, c->win, &protocols, &n)) { + for(i = 0; !ret && i < n; i++) + if(protocols[i] == wmatom[WMDelete]) + ret = True; + XFree(protocols); + } + return ret; +} + +void +killclient(Arg *arg) { + if(!sel) + return; + if(isprotodel(sel)) + sendevent(sel->win, wmatom[WMProtocols], wmatom[WMDelete]); + else + XKillClient(dpy, sel->win); +} + +void +manage(Window w, XWindowAttributes *wa) { + Client *c; + Window trans; + + c = emallocz(sizeof(Client)); + c->tags = emallocz(ntags * sizeof(Bool)); + c->win = w; + c->x = wa->x; + c->y = wa->y; + c->w = wa->width; + c->h = wa->height; + if(c->w == sw && c->h == sh) { + c->border = 0; + c->x = sx; + c->y = sy; + } else { + c->border = BORDERPX; + if(c->x + c->w + 2 * c->border > wax + waw) + c->x = wax + waw - c->w - 2 * c->border; + if(c->y + c->h + 2 * c->border > way + wah) + c->y = way + wah - c->h - 2 * c->border; + if(c->x < wax) + c->x = wax; + if(c->y < way) + c->y = way; + } + updatesizehints(c); + XSelectInput(dpy, c->win, + StructureNotifyMask | PropertyChangeMask | EnterWindowMask); + XGetTransientForHint(dpy, c->win, &trans); + grabbuttons(c, False); + XSetWindowBorder(dpy, c->win, dc.norm[ColBorder]); + updatetitle(c); + settags(c, getclient(trans)); + if(!c->isfloat) + c->isfloat = trans || c->isfixed; + if(clients) + clients->prev = c; + c->next = clients; + c->snext = stack; + stack = clients = c; + XMoveWindow(dpy, c->win, c->x + 2 * sw, c->y); + XMapWindow(dpy, c->win); + setclientstate(c, NormalState); + if(isvisible(c)) + focus(c); + arrange(); +} + +void +resize(Client *c, Bool sizehints) { + float actual, dx, dy, max, min; + XWindowChanges wc; + + if(c->w <= 0 || c->h <= 0) + return; + if(sizehints) { + if(c->minw && c->w < c->minw) + c->w = c->minw; + if(c->minh && c->h < c->minh) + c->h = c->minh; + if(c->maxw && c->w > c->maxw) + c->w = c->maxw; + if(c->maxh && c->h > c->maxh) + c->h = c->maxh; + /* inspired by algorithm from fluxbox */ + if(c->minay > 0 && c->maxay && (c->h - c->baseh) > 0) { + dx = (float)(c->w - c->basew); + dy = (float)(c->h - c->baseh); + min = (float)(c->minax) / (float)(c->minay); + max = (float)(c->maxax) / (float)(c->maxay); + actual = dx / dy; + if(max > 0 && min > 0 && actual > 0) { + if(actual < min) { + dy = (dx * min + dy) / (min * min + 1); + dx = dy * min; + c->w = (int)dx + c->basew; + c->h = (int)dy + c->baseh; + } + else if(actual > max) { + dy = (dx * min + dy) / (max * max + 1); + dx = dy * min; + c->w = (int)dx + c->basew; + c->h = (int)dy + c->baseh; + } + } + } + if(c->incw) + c->w -= (c->w - c->basew) % c->incw; + if(c->inch) + c->h -= (c->h - c->baseh) % c->inch; + } + if(c->w == sw && c->h == sh) + c->border = 0; + else + c->border = BORDERPX; + /* offscreen appearance fixes */ + if(c->x > sw) + c->x = sw - c->w - 2 * c->border; + if(c->y > sh) + c->y = sh - c->h - 2 * c->border; + if(c->x + c->w + 2 * c->border < sx) + c->x = sx; + if(c->y + c->h + 2 * c->border < sy) + c->y = sy; + wc.x = c->x; + wc.y = c->y; + wc.width = c->w; + wc.height = c->h; + wc.border_width = c->border; + XConfigureWindow(dpy, c->win, CWX | CWY | CWWidth | CWHeight | CWBorderWidth, &wc); + configure(c); + XSync(dpy, False); +} + +void +updatesizehints(Client *c) { + long msize; + XSizeHints size; + + if(!XGetWMNormalHints(dpy, c->win, &size, &msize) || !size.flags) + size.flags = PSize; + c->flags = size.flags; + if(c->flags & PBaseSize) { + c->basew = size.base_width; + c->baseh = size.base_height; + } else { + c->basew = c->baseh = 0; + } + if(c->flags & PResizeInc) { + c->incw = size.width_inc; + c->inch = size.height_inc; + } else { + c->incw = c->inch = 0; + } + if(c->flags & PMaxSize) { + c->maxw = size.max_width; + c->maxh = size.max_height; + } else { + c->maxw = c->maxh = 0; + } + if(c->flags & PMinSize) { + c->minw = size.min_width; + c->minh = size.min_height; + } else { + c->minw = c->minh = 0; + } + if(c->flags & PAspect) { + c->minax = size.min_aspect.x; + c->minay = size.min_aspect.y; + c->maxax = size.max_aspect.x; + c->maxay = size.max_aspect.y; + } else { + c->minax = c->minay = c->maxax = c->maxay = 0; + } + c->isfixed = (c->maxw && c->minw && c->maxh && c->minh && + c->maxw == c->minw && c->maxh == c->minh); +} + +void +updatetitle(Client *c) { + char **list = NULL; + int n; + XTextProperty name; + + name.nitems = 0; + c->name[0] = 0; + XGetTextProperty(dpy, c->win, &name, netatom[NetWMName]); + if(!name.nitems) + XGetWMName(dpy, c->win, &name); + if(!name.nitems) + return; + if(name.encoding == XA_STRING) + strncpy(c->name, (char *)name.value, sizeof c->name); + else { + if(XmbTextPropertyToTextList(dpy, &name, &list, &n) >= Success && n > 0 && *list) { + strncpy(c->name, *list, sizeof c->name); + XFreeStringList(list); + } + } + XFree(name.value); +} + +void +unmanage(Client *c) { + Client *nc; + + /* The server grab construct avoids race conditions. */ + XGrabServer(dpy); + XSetErrorHandler(xerrordummy); + detach(c); + detachstack(c); + if(sel == c) { + for(nc = stack; nc && !isvisible(nc); nc = nc->snext); + focus(nc); + } + XUngrabButton(dpy, AnyButton, AnyModifier, c->win); + setclientstate(c, WithdrawnState); + free(c->tags); + free(c); + XSync(dpy, False); + XSetErrorHandler(xerror); + XUngrabServer(dpy); + arrange(); +} + + + + + + + + + + + + + + + + + + + +/* static */ + + +static void +cleanup(void) { + close(STDIN_FILENO); + while(stack) { + resize(stack, True); + unmanage(stack); + } + if(dc.font.set) + XFreeFontSet(dpy, dc.font.set); + else + XFreeFont(dpy, dc.font.xfont); + XUngrabKey(dpy, AnyKey, AnyModifier, root); + XFreePixmap(dpy, dc.drawable); + XFreeGC(dpy, dc.gc); + XDestroyWindow(dpy, barwin); + XFreeCursor(dpy, cursor[CurNormal]); + XFreeCursor(dpy, cursor[CurResize]); + XFreeCursor(dpy, cursor[CurMove]); + XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime); + XSync(dpy, False); + free(seltag); +} + +static void +scan(void) { + unsigned int i, num; + Window *wins, d1, d2; + XWindowAttributes wa; + + wins = NULL; + if(XQueryTree(dpy, root, &d1, &d2, &wins, &num)) { + for(i = 0; i < num; i++) { + if(!XGetWindowAttributes(dpy, wins[i], &wa)) + continue; + if(wa.override_redirect || XGetTransientForHint(dpy, wins[i], &d1)) + continue; + if(wa.map_state == IsViewable) + manage(wins[i], &wa); + } + } + if(wins) + XFree(wins); +} + +static void +setup(void) { + int i, j; + unsigned int mask; + Window w; + XModifierKeymap *modmap; + XSetWindowAttributes wa; + + /* init atoms */ + wmatom[WMProtocols] = XInternAtom(dpy, "WM_PROTOCOLS", False); + wmatom[WMDelete] = XInternAtom(dpy, "WM_DELETE_WINDOW", False); + wmatom[WMState] = XInternAtom(dpy, "WM_STATE", False); + netatom[NetSupported] = XInternAtom(dpy, "_NET_SUPPORTED", False); + netatom[NetWMName] = XInternAtom(dpy, "_NET_WM_NAME", False); + XChangeProperty(dpy, root, netatom[NetSupported], XA_ATOM, 32, + PropModeReplace, (unsigned char *) netatom, NetLast); + /* init cursors */ + cursor[CurNormal] = XCreateFontCursor(dpy, XC_left_ptr); + cursor[CurResize] = XCreateFontCursor(dpy, XC_sizing); + cursor[CurMove] = XCreateFontCursor(dpy, XC_fleur); + /* init modifier map */ + numlockmask = 0; + modmap = XGetModifierMapping(dpy); + for (i = 0; i < 8; i++) { + for (j = 0; j < modmap->max_keypermod; j++) { + if(modmap->modifiermap[i * modmap->max_keypermod + j] == XKeysymToKeycode(dpy, XK_Num_Lock)) + numlockmask = (1 << i); + } + } + XFreeModifiermap(modmap); + /* select for events */ + wa.event_mask = SubstructureRedirectMask | SubstructureNotifyMask + | EnterWindowMask | LeaveWindowMask; + wa.cursor = cursor[CurNormal]; + XChangeWindowAttributes(dpy, root, CWEventMask | CWCursor, &wa); + grabkeys(); + initrregs(); + for(ntags = 0; tags[ntags]; ntags++); + seltag = emallocz(sizeof(Bool) * ntags); + seltag[0] = True; + /* style */ + dc.norm[ColBorder] = getcolor(NORMBORDERCOLOR); + dc.norm[ColBG] = getcolor(NORMBGCOLOR); + dc.norm[ColFG] = getcolor(NORMFGCOLOR); + dc.sel[ColBorder] = getcolor(SELBORDERCOLOR); + dc.sel[ColBG] = getcolor(SELBGCOLOR); + dc.sel[ColFG] = getcolor(SELFGCOLOR); + setfont(FONT); + /* geometry */ + sx = sy = 0; + sw = DisplayWidth(dpy, screen); + sh = DisplayHeight(dpy, screen); + nmaster = NMASTER; + bmw = 1; + /* bar */ + dc.h = bh = dc.font.height + 2; + wa.override_redirect = 1; + wa.background_pixmap = ParentRelative; + wa.event_mask = ButtonPressMask | ExposureMask; + barwin = XCreateWindow(dpy, root, sx, sy, sw, bh, 0, + DefaultDepth(dpy, screen), CopyFromParent, DefaultVisual(dpy, screen), + CWOverrideRedirect | CWBackPixmap | CWEventMask, &wa); + XDefineCursor(dpy, barwin, cursor[CurNormal]); + XMapRaised(dpy, barwin); + strcpy(stext, "dwm-"VERSION); + /* windowarea */ + wax = sx; + way = sy + bh; + wah = sh - bh; + waw = sw; + /* pixmap for everything */ + dc.drawable = XCreatePixmap(dpy, root, sw, bh, DefaultDepth(dpy, screen)); + dc.gc = XCreateGC(dpy, root, 0, 0); + XSetLineAttributes(dpy, dc.gc, 1, LineSolid, CapButt, JoinMiter); + /* multihead support */ + selscreen = XQueryPointer(dpy, root, &w, &w, &i, &i, &i, &i, &mask); +} + +/* + * Startup Error handler to check if another window manager + * is already running. + */ +static int +xerrorstart(Display *dsply, XErrorEvent *ee) { + otherwm = True; + return -1; +} + + + +void +sendevent(Window w, Atom a, long value) { + XEvent e; + + e.type = ClientMessage; + e.xclient.window = w; + e.xclient.message_type = a; + e.xclient.format = 32; + e.xclient.data.l[0] = value; + e.xclient.data.l[1] = CurrentTime; + XSendEvent(dpy, w, False, NoEventMask, &e); + XSync(dpy, False); +} + +void +quit(Arg *arg) { + readin = running = False; +} + +/* There's no way to check accesses to destroyed windows, thus those cases are + * ignored (especially on UnmapNotify's). Other types of errors call Xlibs + * default error handler, which may call exit. + */ +int +xerror(Display *dpy, XErrorEvent *ee) { + if(ee->error_code == BadWindow + || (ee->request_code == X_SetInputFocus && ee->error_code == BadMatch) + || (ee->request_code == X_PolyText8 && ee->error_code == BadDrawable) + || (ee->request_code == X_PolyFillRectangle && ee->error_code == BadDrawable) + || (ee->request_code == X_PolySegment && ee->error_code == BadDrawable) + || (ee->request_code == X_ConfigureWindow && ee->error_code == BadMatch) + || (ee->request_code == X_GrabKey && ee->error_code == BadAccess) + || (ee->request_code == X_CopyArea && ee->error_code == BadDrawable)) + return 0; + fprintf(stderr, "dwm: fatal error: request code=%d, error code=%d\n", + ee->request_code, ee->error_code); + return xerrorxlib(dpy, ee); /* may call exit */ +} + +int +main(int argc, char *argv[]) { + char *p; + int r, xfd; + fd_set rd; + + if(argc == 2 && !strncmp("-v", argv[1], 3)) { + fputs("dwm-"VERSION", (C)opyright MMVI-MMVII Anselm R. Garbe\n", stdout); + exit(EXIT_SUCCESS); + } + else if(argc != 1) + eprint("usage: dwm [-v]\n"); + setlocale(LC_CTYPE, ""); + dpy = XOpenDisplay(0); + if(!dpy) + eprint("dwm: cannot open display\n"); + xfd = ConnectionNumber(dpy); + screen = DefaultScreen(dpy); + root = RootWindow(dpy, screen); + otherwm = False; + XSetErrorHandler(xerrorstart); + /* this causes an error if some other window manager is running */ + XSelectInput(dpy, root, SubstructureRedirectMask); + XSync(dpy, False); + if(otherwm) + eprint("dwm: another window manager is already running\n"); + + XSync(dpy, False); + XSetErrorHandler(NULL); + xerrorxlib = XSetErrorHandler(xerror); + XSync(dpy, False); + setup(); + drawstatus(); + scan(); + + /* main event loop, also reads status text from stdin */ + XSync(dpy, False); + procevent(); + readin = True; + while(running) { + FD_ZERO(&rd); + if(readin) + FD_SET(STDIN_FILENO, &rd); + FD_SET(xfd, &rd); + if(select(xfd + 1, &rd, NULL, NULL, NULL) == -1) { + if(errno == EINTR) + continue; + eprint("select failed\n"); + } + if(FD_ISSET(STDIN_FILENO, &rd)) { + switch(r = read(STDIN_FILENO, stext, sizeof stext - 1)) { + case -1: + strncpy(stext, strerror(errno), sizeof stext - 1); + stext[sizeof stext - 1] = '\0'; + readin = False; + break; + case 0: + strncpy(stext, "EOF", 4); + readin = False; + break; + default: + for(stext[r] = '\0', p = stext + strlen(stext) - 1; p >= stext && *p == '\n'; *p-- = '\0'); + for(; p >= stext && *p != '\n'; --p); + if(p > stext) + strncpy(stext, p + 1, sizeof stext); + } + drawstatus(); + } + if(FD_ISSET(xfd, &rd)) + procevent(); + } + cleanup(); + XCloseDisplay(dpy); + return 0; +}