arg@644: /* (C)opyright MMVI-MMVII Anselm R. Garbe meillo@759: * (C)opyright MMVIII markus schnalke 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@777: * The event handlers of dwm are organized in an array which is accessed meillo@777: * whenever a new event has been fetched. This allows event dispatching in O(1) meillo@777: * 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@766: * stack list. [...] meillo@754: * meillo@754: * Keys and tagging rules are organized as arrays and defined in the config.h meillo@771: * file. [...] The current mode is represented by the arrange() function meillo@771: * pointer, which wether points to [domax()] or dotile(). meillo@754: * meillo@754: * To understand everything else, start reading main.c:main(). meillo@766: * meillo@766: * -- and now about aewl -- meillo@766: * meillo@766: * aewl is a stripped down dwm. It stated as a patchset, but finally forked off meillo@766: * completely. The reason for this was the increasing gap between my wish to meillo@766: * stay where dwm was, and dwm direction to go further. Further more did I meillo@766: * always use only a small subset of dwm's features, so condencing dwm had been meillo@766: * my wish for a long time. meillo@766: * meillo@766: * In aewl clients are either tagged or not (only one tag). Visible are either meillo@766: * all tagged clients or all without the tag. garbeam@0: */ garbeam@0: meillo@766: garbeam@59: #include arg@619: #include garbeam@0: #include meillo@754: #include garbeam@0: #include garbeam@57: #include garbeam@59: #include meillo@776: #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@769: #include "config.h" meillo@769: meillo@769: 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@764: enum { 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@786: Bool isfixed, isfloat, ismax, ispopup; meillo@766: 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@767: const char* class; meillo@767: const char* instance; meillo@767: const char* title; meillo@766: int tag; meillo@754: Bool isfloat; meillo@754: } Rule; 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: char stext[256]; /* status text */ meillo@771: int bh; /* bar height */ 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@761: unsigned int numlockmask; /* dynamic lock mask */ meillo@754: void (*handler[LASTEvent])(XEvent *); /* event handler */ meillo@782: void arrange(void); /* arrange */ meillo@782: void (*arrange1)(void); /* arrange function, indicates mode */ meillo@782: void (*arrange2)(void); /* arrange function, indicates mode */ arg@333: Atom wmatom[WMLast], netatom[NetLast]; meillo@772: Bool running = True; meillo@772: Bool selscreen = True; meillo@772: Bool seltag; meillo@782: Bool viewfloats; meillo@772: Client* clients = NULL; /* global client list */ meillo@772: Client* stack = NULL; /* global client stack */ meillo@772: Client* sel = NULL; /* selected client */ meillo@754: Cursor cursor[CurLast]; meillo@772: DC dc = {0}; /* global draw context */ meillo@754: Display *dpy; meillo@754: Window root, barwin; meillo@754: meillo@754: static int (*xerrorxlib)(Display *, XErrorEvent *); meillo@777: static Bool otherwm; meillo@754: static unsigned int len = 0; meillo@754: meillo@754: meillo@772: 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@777: void drawbar(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: void grabkeys(void); /* grab all keys defined in config.h */ meillo@754: meillo@754: void sendevent(Window w, Atom a, long value); /* send synthetic event to w */ meillo@772: int xerror(Display *dsply, XErrorEvent *ee); /* X error handler */ meillo@754: meillo@754: Client *getnext(Client *c); /* returns next visible client */ meillo@766: void settag(Client *c, Client *trans); /* sets tag of c */ meillo@754: meillo@754: void *emallocz(unsigned int size); /* allocates zero-initialized memory, exits on error */ meillo@778: void die(const char *errstr, ...); /* prints errstr and exits with 1 */ meillo@754: meillo@754: void detach(Client *c); /* detaches c from global client list */ 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@766: void toggleview(void); /* toggle the view */ meillo@782: void floattoggle(); /* toggle floatsview */ meillo@760: void focusnext(void); /* focuses next visible client */ meillo@760: void zoom(void); /* zooms the focused client to master area */ meillo@760: void killclient(void); /* kill c nicely */ meillo@772: void quit(void); /* quit nicely */ meillo@782: void togglemode(void); /* toggles arrange function (dotile/domax) */ meillo@760: void togglefloat(void); /* toggles focusesd client between floating/non-floating state */ meillo@760: void incnmaster(void); /* increments nmaster */ meillo@760: void decnmaster(void); /* decrements nmaster */ meillo@766: void toggletag(void); /* toggles tag of c */ meillo@758: void spawn(const char* cmd); /* forks a new subprocess with cmd */ meillo@754: meillo@777: void updatestatus(void); /* update the status text */ meillo@754: meillo@754: meillo@773: RULES meillo@773: KEYS meillo@773: meillo@773: meillo@754: meillo@754: meillo@754: meillo@754: meillo@754: meillo@754: meillo@754: meillo@754: /* from view.c */ meillo@754: meillo@773: Client * meillo@754: nexttiled(Client *c) { meillo@786: for(c = getnext(c); c && (c->isfloat || c->ispopup); c = getnext(c->next)); meillo@754: return c; meillo@754: } meillo@754: meillo@773: 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@769: c->rx = c->x; meillo@769: c->ry = c->y; meillo@769: c->rw = c->w; meillo@769: c->rh = c->h; meillo@769: c->x = wax; meillo@769: c->y = way; meillo@769: c->w = waw - 2 * BORDERPX; meillo@769: c->h = wah - 2 * BORDERPX; meillo@769: } 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@769: resize(c, False); meillo@754: while(XCheckMaskEvent(dpy, EnterWindowMask, &ev)); meillo@754: } meillo@754: meillo@754: meillo@754: 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: 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@786: if(c->isfloat || c->ispopup) { 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: 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@786: if(c->isfloat || c->ispopup) { meillo@754: resize(c, True); meillo@754: continue; meillo@754: } meillo@768: c->ismax = True; meillo@769: c->x = wax; meillo@769: c->y = way; meillo@769: c->w = waw - 2 * BORDERPX; meillo@769: c->h = wah - 2 * BORDERPX; meillo@769: resize(c, False); meillo@754: } else { meillo@754: XMoveWindow(dpy, c->win, c->x + 2 * sw, c->y); 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: 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@761: if(wah / (nmaster + 1) <= 2 * BORDERPX) meillo@754: return; meillo@756: nmaster++; meillo@756: if(sel) meillo@756: arrange(); meillo@756: } meillo@756: meillo@756: void meillo@756: decnmaster() { meillo@761: if(nmaster <= 1) meillo@756: return; meillo@756: nmaster--; meillo@754: if(sel) meillo@754: arrange(); meillo@754: } meillo@754: meillo@754: Bool meillo@754: isvisible(Client *c) { meillo@782: if (c->isfloat) { meillo@782: return viewfloats; meillo@782: } meillo@782: return (!viewfloats && c->tag == seltag); meillo@754: } meillo@754: meillo@754: void meillo@754: restack(void) { meillo@754: Client *c; meillo@754: XEvent ev; meillo@754: meillo@777: drawbar(); meillo@754: if(!sel) meillo@754: return; meillo@786: if(sel->isfloat || sel->ispopup) meillo@754: XRaiseWindow(dpy, sel->win); meillo@786: else meillo@768: XLowerWindow(dpy, sel->win); meillo@768: for(c = nexttiled(clients); c; c = nexttiled(c->next)) { meillo@768: if(c == sel) meillo@768: continue; meillo@768: XLowerWindow(dpy, c->win); meillo@768: } 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@761: if (!sel) meillo@754: return; meillo@754: sel->isfloat = !sel->isfloat; meillo@782: floattoggle(); meillo@754: } meillo@754: meillo@754: void meillo@756: togglemode() { meillo@782: /* toggle between tile and max */ meillo@782: if (viewfloats) { meillo@782: return; meillo@782: } meillo@782: if (seltag) { meillo@782: arrange1 = (arrange1 == dotile) ? domax : dotile; meillo@782: } else { meillo@782: arrange2 = (arrange2 == dotile) ? domax : dotile; meillo@782: } meillo@754: if(sel) meillo@754: arrange(); meillo@776: zoom(); meillo@754: } meillo@754: meillo@754: void meillo@782: arrange() { meillo@782: if (seltag) { meillo@782: arrange1(); meillo@782: } else { meillo@782: arrange2(); meillo@782: } meillo@782: } meillo@782: meillo@782: void meillo@755: toggleview() { meillo@782: if (viewfloats) { meillo@786: floattoggle(); meillo@782: return; meillo@782: } meillo@766: seltag = !seltag; meillo@754: arrange(); meillo@754: } meillo@754: meillo@754: void meillo@782: floattoggle() { meillo@782: viewfloats = !viewfloats; meillo@782: arrange(); meillo@782: } meillo@782: meillo@782: void meillo@756: zoom() { meillo@754: unsigned int n; meillo@754: Client *c; meillo@754: meillo@754: if(!sel) meillo@754: return; meillo@786: if(sel->isfloat || sel->ispopup) { 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: /* from util.c */ 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@778: die("fatal: could not malloc() %u bytes\n", size); meillo@754: return res; meillo@754: } meillo@754: meillo@754: void meillo@778: die(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@758: if(!cmd) meillo@754: return; meillo@769: if(!(shell = getenv("SHELL"))) meillo@769: shell = "/bin/sh"; 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@772: fprintf(stderr, "aewl: 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: /* from tag.c */ arg@333: meillo@754: Client * meillo@754: getnext(Client *c) { meillo@768: while(c && !isvisible(c)) { meillo@768: c = c->next; meillo@768: } meillo@754: return c; meillo@754: } meillo@754: meillo@754: void meillo@767: settag(Client *c, Client *trans) { meillo@754: unsigned int i; meillo@767: XClassHint ch = { 0 }; meillo@754: meillo@767: if(trans) { meillo@767: c->tag = trans->tag; meillo@786: c->isfloat = trans->isfloat; meillo@754: return; meillo@767: } meillo@767: c->tag = seltag; /* default */ meillo@767: XGetClassHint(dpy, c->win, &ch); meillo@754: len = sizeof rule / sizeof rule[0]; meillo@754: for(i = 0; i < len; i++) { meillo@767: if((rule[i].title && strstr(c->name, rule[i].title)) meillo@767: || (ch.res_class && rule[i].class && strstr(ch.res_class, rule[i].class)) meillo@767: || (ch.res_name && rule[i].instance && strstr(ch.res_name, rule[i].instance))) { meillo@767: c->isfloat = rule[i].isfloat; meillo@767: if (rule[i].tag < 0) { meillo@767: c->tag = seltag; meillo@767: } else if (rule[i].tag) { meillo@767: c->tag = True; meillo@767: } else { meillo@767: c->tag = False; meillo@767: } meillo@767: break; meillo@754: } meillo@754: } meillo@767: if(ch.res_class) meillo@767: XFree(ch.res_class); meillo@767: if(ch.res_name) meillo@767: XFree(ch.res_name); meillo@754: } meillo@754: meillo@754: void meillo@766: toggletag() { meillo@754: if(!sel) meillo@754: return; meillo@766: sel->tag = !sel->tag; meillo@755: toggleview(); meillo@754: } meillo@754: meillo@773: /* from event.c */ meillo@754: meillo@773: 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@773: 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@773: void meillo@754: buttonpress(XEvent *e) { meillo@754: Client *c; meillo@754: XButtonPressedEvent *ev = &e->xbutton; meillo@754: meillo@754: if(barwin == ev->window) { meillo@758: return; meillo@763: } meillo@763: if((c = getclient(ev->window))) { meillo@754: focus(c); meillo@754: if(CLEANMASK(ev->state) != MODKEY) meillo@754: return; meillo@786: if(ev->button == Button1 && (c->isfloat || c->ispopup)) { meillo@754: restack(); meillo@754: movemouse(c); meillo@786: } else if(ev->button == Button3 && (c->isfloat || c->ispopup) && !c->isfixed) { meillo@754: restack(); meillo@754: resizemouse(c); meillo@754: } meillo@754: } meillo@754: } meillo@754: meillo@773: 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@786: if(c->isfloat || c->ispopup) { 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@773: 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@773: 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@773: 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@777: drawbar(); meillo@754: } meillo@754: } meillo@754: meillo@773: 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@773: 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@773: 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@773: 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@773: void meillo@754: propertynotify(XEvent *e) { meillo@754: Client *c; meillo@754: Window trans; meillo@754: XPropertyEvent *ev = &e->xproperty; meillo@754: meillo@777: if((ev->window == root) && (ev->atom = XA_WM_NAME)) meillo@777: updatestatus(); meillo@777: else if(ev->state == PropertyDelete) meillo@754: return; /* ignore */ meillo@777: else 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@786: if(!c->ispopup && (c->ispopup = (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@777: drawbar(); meillo@754: } meillo@754: } meillo@754: } meillo@754: meillo@773: 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@773: /* from draw.c */ meillo@754: meillo@773: 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@773: 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: void meillo@777: drawbar(void) { meillo@756: int x; meillo@771: unsigned long black[ColLast]; meillo@771: black[ColBG] = getcolor("#000000"); meillo@771: black[ColFG] = getcolor("#ffffff"); meillo@754: meillo@771: /* views */ meillo@754: dc.x = dc.y = 0; meillo@766: dc.w = textw(NAMETAGGED); meillo@782: drawtext(NAMETAGGED, ( (seltag&&!viewfloats) ? dc.sel : dc.norm )); meillo@771: meillo@771: dc.x += dc.w; meillo@771: dc.w = BORDERPX; meillo@771: drawtext(NULL, black); meillo@771: meillo@771: dc.x += dc.w; meillo@766: dc.w = textw(NAMEUNTAGGED); meillo@782: drawtext(NAMEUNTAGGED, ( (!seltag&&!viewfloats) ? dc.sel : dc.norm )); meillo@771: meillo@771: dc.x += dc.w; meillo@771: dc.w = BORDERPX; meillo@771: drawtext(NULL, black); meillo@771: meillo@771: /* status text */ 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@771: meillo@771: /* client title */ meillo@754: if((dc.w = dc.x - x) > bh) { meillo@754: dc.x = x; meillo@754: drawtext(sel ? sel->name : NULL, dc.norm); meillo@771: meillo@771: dc.x += dc.w; meillo@771: dc.w = BORDERPX; meillo@771: drawtext(NULL, black); meillo@754: } meillo@771: 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@778: die("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@778: die("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@773: /* from client.c */ meillo@754: meillo@773: 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@773: 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@773: 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@773: int meillo@754: xerrordummy(Display *dsply, XErrorEvent *ee) { meillo@754: return 0; 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@764: XSetWindowBorder(dpy, sel->win, dc.norm[ColBG]); 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@777: drawbar(); meillo@754: if(!selscreen) meillo@754: return; meillo@754: if(c) { meillo@764: XSetWindowBorder(dpy, c->win, dc.sel[ColBG]); 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@766: 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@764: XSetWindowBorder(dpy, c->win, dc.norm[ColBG]); meillo@754: updatetitle(c); meillo@766: settag(c, getclient(trans)); meillo@786: if(!c->ispopup) meillo@786: c->ispopup = 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@777: if (!gettextprop(c->win, netatom[NetWMName], c->name, sizeof c->name)) { meillo@777: gettextprop(c->win, XA_WM_NAME, c->name, sizeof c->name); meillo@777: } meillo@777: } meillo@777: meillo@777: meillo@777: Bool meillo@777: gettextprop(Window w, Atom atom, char* text, unsigned int size) { meillo@754: char **list = NULL; meillo@754: int n; meillo@754: XTextProperty name; meillo@754: meillo@777: if (!text || size == 0) { meillo@777: return False; meillo@777: } meillo@777: text[0] = '\0'; meillo@777: XGetTextProperty(dpy, w, &name, atom); meillo@754: if(!name.nitems) meillo@777: return False; meillo@754: if(name.encoding == XA_STRING) meillo@777: strncpy(text, (char *)name.value, size - 1); meillo@754: else { meillo@754: if(XmbTextPropertyToTextList(dpy, &name, &list, &n) >= Success && n > 0 && *list) { meillo@777: strncpy(text, *list, size - 1); meillo@754: XFreeStringList(list); meillo@754: } meillo@754: } meillo@777: text[size - 1] = '\0'; meillo@754: XFree(name.value); meillo@777: return True; 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@773: /* from main.c */ meillo@754: meillo@773: void arg@487: cleanup(void) { 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: meillo@773: 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: meillo@773: 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: meillo@777: screen = DefaultScreen(dpy); meillo@777: root = RootWindow(dpy, screen); meillo@777: 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 meillo@777: | EnterWindowMask | LeaveWindowMask | PropertyChangeMask; arg@333: wa.cursor = cursor[CurNormal]; arg@333: XChangeWindowAttributes(dpy, root, CWEventMask | CWCursor, &wa); arg@333: grabkeys(); meillo@784: seltag = False; meillo@782: viewfloats = False; arg@333: /* style */ arg@353: dc.norm[ColBG] = getcolor(NORMBGCOLOR); arg@353: dc.norm[ColFG] = getcolor(NORMFGCOLOR); 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@782: arrange1 = DEFMODE; meillo@782: arrange2 = DEFMODE; 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@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); meillo@777: meillo@777: updatestatus(); arg@333: } arg@333: garbeam@76: /* garbeam@76: * Startup Error handler to check if another window manager garbeam@76: * is already running. garbeam@76: */ meillo@773: int arg@461: xerrorstart(Display *dsply, XErrorEvent *ee) { garbeam@76: otherwm = True; garbeam@76: return -1; garbeam@76: } garbeam@76: 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() { meillo@777: 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; meillo@772: fprintf(stderr, "aewl: 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: meillo@777: void meillo@777: checkotherwm(void) { meillo@777: otherwm = False; meillo@777: xerrorxlib = XSetErrorHandler(xerrorstart); meillo@777: /* this causes an error if some other window manager is running */ meillo@777: XSelectInput(dpy, DefaultRootWindow(dpy), SubstructureRedirectMask); meillo@777: XSync(dpy, False); meillo@777: if(otherwm) { meillo@778: die("aewl: another window manager is already running\n"); meillo@777: } meillo@777: meillo@777: XSetErrorHandler(xerror); meillo@777: XSync(dpy, False); meillo@777: } meillo@777: meillo@777: void meillo@777: run(void) { meillo@777: XEvent ev; meillo@777: meillo@777: XSync(dpy, False); meillo@777: /* main event loop */ meillo@777: while(running && !XNextEvent(dpy, &ev)) { meillo@777: if(handler[ev.type]) { meillo@777: (handler[ev.type])(&ev); /* call handler */ meillo@777: } meillo@777: } meillo@777: } meillo@777: meillo@777: void meillo@777: updatestatus(void) { meillo@777: if (!gettextprop(root, XA_WM_NAME, stext, sizeof(stext))) { meillo@777: strcpy(stext, "aewl-"VERSION); meillo@777: } meillo@777: drawbar(); meillo@777: } meillo@777: garbeam@0: int arg@461: main(int argc, char *argv[]) { garbeam@0: arg@137: if(argc == 2 && !strncmp("-v", argv[1], 3)) { meillo@772: fputs("aewl-"VERSION", Copyright 2008 markus schnalke \n", stdout); meillo@772: fputs("forked off dwm-3.4, (C)opyright MMVI-MMVII Anselm R. Garbe\n", stdout); arg@137: exit(EXIT_SUCCESS); meillo@755: } else if(argc != 1) { meillo@778: die("usage: aewl [-v]\n"); garbeam@0: } arg@619: setlocale(LC_CTYPE, ""); garbeam@0: dpy = XOpenDisplay(0); meillo@755: if(!dpy) { meillo@778: die("aewl: cannot open display\n"); meillo@755: } garbeam@0: meillo@777: checkotherwm(); arg@333: setup(); garbeam@75: scan(); meillo@777: run(); meillo@777: cleanup(); garbeam@3: garbeam@0: XCloseDisplay(dpy); garbeam@0: return 0; garbeam@0: }