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