tabbed

Simple tabbing application for X11.
git clone git://r-36.net/tabbed
Log | Files | Refs | README | LICENSE

commit 3f0067b1c775a7f0ade805e10546272f8ce01dd3
parent bcf3d90ce7d93bf2a34c2bec3b2fdd2a7c5e0af0
Author: Christoph Lohmann <20h@r-36.net>
Date:   Thu,  8 Nov 2012 21:40:58 +0100

Style inquistion was here.
Diffstat:
tabbed.c | 193+++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------------
1 file changed, 129 insertions(+), 64 deletions(-)

diff --git a/tabbed.c b/tabbed.c @@ -42,7 +42,7 @@ /* Macros */ #define MAX(a, b) ((a) > (b) ? (a) : (b)) #define MIN(a, b) ((a) < (b) ? (a) : (b)) -#define LENGTH(x) (sizeof (x) / sizeof *(x)) +#define LENGTH(x) (sizeof((x)) / sizeof(*(x))) #define CLEANMASK(mask) (mask & ~(numlockmask|LockMask)) #define TEXTW(x) (textnw(x, strlen(x)) + dc.font.height) @@ -201,10 +201,13 @@ cleanup(void) { n = c->next; unmanage(c); } - if(dc.font.set) + + if(dc.font.set) { XFreeFontSet(dpy, dc.font.set); - else + } else { XFreeFont(dpy, dc.font.xfont); + } + XFreePixmap(dpy, dc.drawable); XFreeGC(dpy, dc.gc); XDestroyWindow(dpy, win); @@ -217,8 +220,9 @@ clientmessage(const XEvent *e) { const XClientMessageEvent *ev = &e->xclient; if(ev->message_type == wmatom[WMProtocols] - && ev->data.l[0] == wmatom[WMDelete]) + && ev->data.l[0] == wmatom[WMDelete]) { running = False; + } } void @@ -229,7 +233,8 @@ configurenotify(const XEvent *e) { ww = ev->width; wh = ev->height; XFreePixmap(dpy, dc.drawable); - dc.drawable = XCreatePixmap(dpy, root, ww, wh, DefaultDepth(dpy, screen)); + dc.drawable = XCreatePixmap(dpy, root, ww, wh, + DefaultDepth(dpy, screen)); if(sel) resize(sel, ww, wh - bh); XSync(dpy, False); @@ -297,10 +302,13 @@ drawbar() { XSync(dpy, False); return; } + width = ww; for(c = clients; c; c = c->next) c->tabx = -1; + for(n = 0, fc = c = getfirsttab(); c; c = c->next, n++); + if(n * tabwidth > width) { dc.w = TEXTW(after); dc.x = width - dc.w; @@ -308,22 +316,24 @@ drawbar() { width -= dc.w; } dc.x = 0; + if(fc != clients) { dc.w = TEXTW(before); drawtext(before, dc.sel); dc.x += dc.w; width -= dc.w; } + for(c = fc; c && dc.x < width; c = c->next) { dc.w = tabwidth; if(c == sel) { col = dc.sel; - if(n * tabwidth > width) + if(n * tabwidth > width) { dc.w += width % tabwidth; - else + } else { dc.w = width - (n - 1) * tabwidth; - } - else { + } + } else { col = dc.norm; } drawtext(c->name, col); @@ -344,22 +354,30 @@ drawtext(const char *text, unsigned long col[ColLast]) { XFillRectangles(dpy, dc.drawable, dc.gc, &r, 1); if(!text) return; + olen = strlen(text); 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 */ - for(len = MIN(olen, sizeof buf); len && textnw(text, len) > dc.w - h; len--); + for(len = MIN(olen, sizeof(buf)); + len && textnw(text, len) > dc.w - h; len--); if(!len) return; + memcpy(buf, text, len); - if(len < olen) + if(len < olen) { for(i = len; i && i > len - 3; buf[--i] = '.'); + } + XSetForeground(dpy, dc.gc, col[ColFG]); - if(dc.font.set) - XmbDrawString(dpy, dc.drawable, dc.font.set, dc.gc, x, y, buf, len); - else + if(dc.font.set) { + XmbDrawString(dpy, dc.drawable, dc.font.set, + dc.gc, x, y, buf, len); + } else { XDrawString(dpy, dc.drawable, dc.gc, x, y, buf, len); + } } void * @@ -381,26 +399,31 @@ expose(const XEvent *e) { void focus(Client *c) { + char buf[BUFSIZ] = "tabbed-"VERSION" ::"; + size_t i, n; + /* If c, sel and clients are NULL, raise tabbed-win itself */ if(!c && !(c = sel ? sel : clients)) { - char buf[BUFSIZ] = "tabbed-"VERSION" ::"; - size_t i, n; + for(i = 0, n = strlen(buf); cmd[i] && n < sizeof(buf); i++) + n += snprintf(&buf[n], sizeof(buf) - n, " %s", cmd[i]); - for(i = 0, n = strlen(buf); cmd[i] && n < sizeof buf; i++) - n += snprintf(&buf[n], sizeof buf - n, " %s", cmd[i]); XStoreName(dpy, win, buf); XRaiseWindow(dpy, win); + return; } + resize(c, ww, wh - bh); XRaiseWindow(dpy, c->win); XSetInputFocus(dpy, c->win, RevertToParent, CurrentTime); sendxembed(c, XEMBED_FOCUS_IN, XEMBED_FOCUS_CURRENT, 0, 0); sendxembed(c, XEMBED_WINDOW_ACTIVATE, 0, 0, 0); XStoreName(dpy, win, c->name); + if(sel != c) { lastsel = sel; } + sel = c; drawbar(); } @@ -427,9 +450,11 @@ Client * getclient(Window w) { Client *c; - for(c = clients; c; c = c->next) + for(c = clients; c; c = c->next) { if(c->win == w) return c; + } + return NULL; } @@ -440,6 +465,7 @@ getcolor(const char *colstr) { if(!XAllocNamedColor(dpy, cmap, colstr, &color, &color)) die("tabbed: cannot allocate color '%s'\n", colstr); + return color.pixel; } @@ -450,10 +476,13 @@ getfirsttab() { if(!sel) return NULL; + c = fc = clients; for(n = 0; c; c = c->next, n++); + if(n * tabwidth > ww) { for(seli = 0, c = clients; c && c != sel; c = c->next, seli++); + for(; seli * tabwidth > ww / 2 && n * tabwidth > ww; fc = fc->next, seli--, n--); } @@ -468,40 +497,45 @@ gettextprop(Window w, Atom atom, char *text, unsigned int size) { if(!text || size == 0) return False; + text[0] = '\0'; XGetTextProperty(dpy, w, &name, atom); if(!name.nitems) return False; - if(name.encoding == XA_STRING) + + if(name.encoding == XA_STRING) { strncpy(text, (char *)name.value, size - 1); - else { - if(XmbTextPropertyToTextList(dpy, &name, &list, &n) >= Success && n > 0 && *list) { + } 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 initfont(const char *fontstr) { - char *def, **missing; + char *def, **missing, **font_names; int i, n; + XFontStruct **xfonts; 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, "tabbed: missing fontset: %s\n", missing[n]); XFreeStringList(missing); } + if(dc.font.set) { - XFontStruct **xfonts; - char **font_names; dc.font.ascent = dc.font.descent = 0; n = XFontsOfFontSet(dc.font.set, &xfonts, &font_names); for(i = 0, dc.font.ascent = 0, dc.font.descent = 0; i < n; i++) { @@ -509,14 +543,15 @@ initfont(const char *fontstr) { dc.font.descent = MAX(dc.font.descent,(*xfonts)->descent); xfonts++; } - } - else { + } else { if(dc.font.xfont) XFreeFont(dpy, dc.font.xfont); dc.font.xfont = NULL; if(!(dc.font.xfont = XLoadQueryFont(dpy, fontstr)) - && !(dc.font.xfont = XLoadQueryFont(dpy, "fixed"))) + && !(dc.font.xfont = XLoadQueryFont(dpy, "fixed"))) { die("tabbed: cannot load font: '%s'\n", fontstr); + } + dc.font.ascent = dc.font.xfont->ascent; dc.font.descent = dc.font.xfont->descent; } @@ -530,11 +565,13 @@ isprotodel(Client *c) { Bool ret = False; if(XGetWMProtocols(dpy, c->win, &protocols, &n)) { - for(i = 0; !ret && i < n; i++) + for(i = 0; !ret && i < n; i++) { if(protocols[i] == wmatom[WMDelete]) ret = True; + } XFree(protocols); } + return ret; } @@ -545,11 +582,13 @@ keypress(const XEvent *e) { KeySym keysym; keysym = XkbKeycodeToKeysym(dpy, (KeyCode)ev->keycode, 0, 0); - for(i = 0; i < LENGTH(keys); i++) + for(i = 0; i < LENGTH(keys); i++) { if(keysym == keys[i].keysym - && CLEANMASK(keys[i].mod) == CLEANMASK(ev->state) - && keys[i].func) + && CLEANMASK(keys[i].mod) == CLEANMASK(ev->state) + && keys[i].func) { keys[i].func(&(keys[i].arg)); + } + } } void @@ -558,6 +597,7 @@ killclient(const Arg *arg) { if(!sel) return; + if(isprotodel(sel) && !sel->closed) { ev.type = ClientMessage; ev.xclient.window = sel->win; @@ -567,9 +607,9 @@ killclient(const Arg *arg) { ev.xclient.data.l[1] = CurrentTime; XSendEvent(dpy, sel->win, False, NoEventMask, &ev); sel->closed = True; - } - else + } else { XKillClient(dpy, sel->win); + } } void @@ -587,18 +627,22 @@ manage(Window w) { XSelectInput(dpy, w, PropertyChangeMask|StructureNotifyMask|EnterWindowMask); XSync(dpy, False); for(i = 0; i < LENGTH(keys); i++) { - if((code = XKeysymToKeycode(dpy, keys[i].keysym))) + if((code = XKeysymToKeycode(dpy, keys[i].keysym))) { for(j = 0; j < LENGTH(modifiers); j++) XGrabKey(dpy, code, keys[i].mod | modifiers[j], w, True, GrabModeAsync, GrabModeAsync); + } } - c = emallocz(sizeof *c); + + c = emallocz(sizeof(*c)); c->next = clients; c->win = w; clients = c; + updatetitle(c); drawbar(); XMapRaised(dpy, w); + e.xclient.window = w; e.xclient.type = ClientMessage; e.xclient.message_type = wmatom[XEmbed]; @@ -609,9 +653,11 @@ manage(Window w) { e.xclient.data.l[3] = win; e.xclient.data.l[4] = 0; XSendEvent(dpy, root, False, NoEventMask, &e); + XSync(dpy, False); focus(nextfocus ? c : sel); nextfocus = foreground; + if(!lastsel) lastsel = c; } @@ -671,15 +717,15 @@ void rotate(const Arg *arg) { Client *c; - if(arg->i == 0) + if(arg->i == 0) { focus(lastsel); - else if(arg->i > 0) { - if(sel && sel->next) + } else if(arg->i > 0) { + if(sel && sel->next) { focus(sel->next); - else + } else { focus(clients); - } - else { + } + } else { for(c = clients; c && c->next && c->next != sel; c = c->next); if(c) focus(c); @@ -695,6 +741,7 @@ run(void) { drawbar(); if(doinitspawn == True) spawn(NULL); + while(running) { XNextEvent(dpy, &ev); if(handler[ev.type]) @@ -722,7 +769,7 @@ void setcmd(int argc, char *argv[]) { int i; - cmd = emallocz((argc+2) * sizeof *cmd); + cmd = emallocz((argc+2) * sizeof(*cmd)); for(i = 0; i < argc; i++) cmd[i] = argv[i]; cmd[argc] = winid; @@ -756,12 +803,14 @@ setup(void) { dc.norm[ColFG] = getcolor(normfgcolor); dc.sel[ColBG] = getcolor(selbgcolor); dc.sel[ColFG] = getcolor(selfgcolor); - dc.drawable = XCreatePixmap(dpy, root, ww, wh, DefaultDepth(dpy, screen)); + dc.drawable = XCreatePixmap(dpy, root, ww, wh, + DefaultDepth(dpy, screen)); dc.gc = XCreateGC(dpy, root, 0, 0); if(!dc.font.set) XSetFont(dpy, dc.gc, dc.font.xfont->fid); - win = XCreateSimpleWindow(dpy, root, wx, wy, ww, wh, 0, dc.norm[ColFG], dc.norm[ColBG]); + win = XCreateSimpleWindow(dpy, root, wx, wy, ww, wh, 0, + dc.norm[ColFG], dc.norm[ColBG]); XMapRaised(dpy, win); XSelectInput(dpy, win, SubstructureNotifyMask|FocusChangeMask| ButtonPressMask|ExposureMask|KeyPressMask| @@ -775,7 +824,7 @@ setup(void) { XSetWMProtocols(dpy, win, &wmatom[WMDelete], 1); - snprintf(winid, sizeof winid, "%lu", win); + snprintf(winid, sizeof(winid), "%lu", win); nextfocus = foreground; focus(clients); } @@ -784,6 +833,7 @@ void sigchld(int unused) { if(signal(SIGCHLD, sigchld) == SIG_ERR) die("tabbed: cannot install SIGCHLD handler"); + while(0 < waitpid(-1, NULL, WNOHANG)); } @@ -792,6 +842,7 @@ spawn(const Arg *arg) { if(fork() == 0) { if(dpy) close(ConnectionNumber(dpy)); + setsid(); if(arg && arg->v) { execvp(((char **)arg->v)[0], (char **)arg->v); @@ -813,6 +864,7 @@ textnw(const char *text, unsigned int len) { XmbTextExtents(dc.font.set, text, len, NULL, &r); return r.width; } + return XTextWidth(dc.font.xfont, text, len); } @@ -820,11 +872,11 @@ void unmanage(Client *c) { Client *pc; - if(!clients) + if(!clients) { return; - else if(c == clients) + } else if(c == clients) { pc = clients = c->next; - else { + } else { for(pc = clients; pc && pc->next && pc->next != c; pc = pc->next); pc->next = c->next; } @@ -846,18 +898,21 @@ updatenumlockmask(void) { numlockmask = 0; modmap = XGetModifierMapping(dpy); - for(i = 0; i < 8; i++) - for(j = 0; j < modmap->max_keypermod; j++) + 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)) + == XKeysymToKeycode(dpy, XK_Num_Lock)) { numlockmask = (1 << i); + } + } + } XFreeModifiermap(modmap); } void updatetitle(Client *c) { - if(!gettextprop(c->win, wmatom[WMName], c->name, sizeof c->name)) - gettextprop(c->win, XA_WM_NAME, c->name, sizeof c->name); + if(!gettextprop(c->win, wmatom[WMName], c->name, sizeof(c->name))) + gettextprop(c->win, XA_WM_NAME, c->name, sizeof(c->name)); if(sel == c) XStoreName(dpy, win, c->name); drawbar(); @@ -869,15 +924,25 @@ updatetitle(Client *c) { 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_GrabButton && ee->error_code == BadAccess) - || (ee->request_code == X_GrabKey && ee->error_code == BadAccess) - || (ee->request_code == X_CopyArea && ee->error_code == BadDrawable)) + || (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_GrabButton + && ee->error_code == BadAccess) + || (ee->request_code == X_GrabKey + && ee->error_code == BadAccess) + || (ee->request_code == X_CopyArea + && ee->error_code == BadDrawable)) { return 0; + } + fprintf(stderr, "tabbed: fatal error: request code=%d, error code=%d\n", ee->request_code, ee->error_code); return xerrorxlib(dpy, ee); /* may call exit */ @@ -886,8 +951,7 @@ xerror(Display *dpy, XErrorEvent *ee) { char *argv0; void -usage(void) -{ +usage(void) { die("usage: %s [-dhsv] [-n name] command...\n", argv0); } @@ -923,6 +987,7 @@ main(int argc, char *argv[]) { fprintf(stderr, "tabbed: no locale support\n"); if(!(dpy = XOpenDisplay(NULL))) die("tabbed: cannot open display\n"); + setup(); printf("0x%lx\n", win); fflush(NULL);