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