tabbed

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

commit 23623e4ffa5aacb896fa4ac7164ad57918a528ba
parent 1c5078bc89c1ccf1b732c0f35f0689de6b24926f
Author: Christoph Lohmann <20h@r-36.net>
Date:   Sun, 16 Apr 2017 23:47:30 +0200

Add toggle for bar appearance and position.

Diffstat:
config.def.h | 67++++++++++++++++++++++++++++++++++++-------------------------------
tabbed.1 | 22++++++++++++++++++++++
tabbed.c | 227+++++++++++++++++++++++++++++++++++++++++++++++++------------------------------
3 files changed, 200 insertions(+), 116 deletions(-)

diff --git a/config.def.h b/config.def.h @@ -14,6 +14,8 @@ static const char titletrim[] = "..."; static const int tabwidth = 200; static const Bool foreground = True; static Bool urgentswitch = False; +Bool showbar = True; /* False means no bar */ +Bool bottombar = True; /* False means top bar */ /* * Where to place a new tab when it is opened. When npisrelative is True, @@ -24,43 +26,46 @@ static int newposition = 0; static Bool npisrelative = False; #define SETPROP(p) { \ - .v = (char *[]){ "/bin/sh", "-c", \ - "prop=\"`xwininfo -children -id $1 | grep '^ 0x' |" \ - "sed -e's@^ *\\(0x[0-9a-f]*\\) \"\\([^\"]*\\)\".*@\\1 \\2@' |" \ - "xargs -0 printf %b | dmenu -l 10`\" &&" \ - "xprop -id $1 -f $0 8s -set $0 \"$prop\"", \ - p, winid, NULL \ - } \ + .v = (char *[]){ "/bin/sh", "-c", \ + "prop=\"`xwininfo -children -id $1 | grep '^ 0x' |" \ + "sed -e's@^ *\\(0x[0-9a-f]*\\) \"\\([^\"]*\\)\".*@\\1 \\2@' |" \ + "xargs -0 printf %b | dmenu -l 10`\" &&" \ + "xprop -id $1 -f $0 8s -set $0 \"$prop\"", \ + p, winid, NULL \ + } \ } #define MODKEY ControlMask -static Key keys[] = { - /* modifier key function argument */ - { MODKEY|ShiftMask, XK_Return, focusonce, { 0 } }, - { MODKEY|ShiftMask, XK_Return, spawn, { 0 } }, - { MODKEY, XK_t, spawn, SETPROP("_TABBED_SELECT_TAB") }, +static Key keys[] = { \ + /* modifier key function argument */ + { MODKEY|ShiftMask, XK_Return, focusonce, { 0 } }, + { MODKEY|ShiftMask, XK_Return, spawn, { 0 } }, + { MODKEY, XK_t, spawn, SETPROP("_TABBED_SELECT_TAB") }, + { MODKEY, XK_b, togglebar, { 0 } }, + { MODKEY, XK_m, toggletop, { 0 } }, - { MODKEY|ShiftMask, XK_l, rotate, { .i = +1 } }, - { MODKEY|ShiftMask, XK_h, rotate, { .i = -1 } }, - { MODKEY|ShiftMask, XK_j, movetab, { .i = -1 } }, - { MODKEY|ShiftMask, XK_k, movetab, { .i = +1 } }, - { MODKEY, XK_Tab, rotate, { .i = 0 } }, + { MODKEY|ShiftMask, XK_l, rotate, { .i = +1 } }, + { MODKEY|ShiftMask, XK_h, rotate, { .i = -1 } }, + { MODKEY|ShiftMask, XK_j, movetab, { .i = -1 } }, + { MODKEY|ShiftMask, XK_k, movetab, { .i = +1 } }, + { MODKEY, XK_Tab, rotate, { .i = 0 } }, - { MODKEY, XK_1, move, { .i = 0 } }, - { MODKEY, XK_2, move, { .i = 1 } }, - { MODKEY, XK_3, move, { .i = 2 } }, - { MODKEY, XK_4, move, { .i = 3 } }, - { MODKEY, XK_5, move, { .i = 4 } }, - { MODKEY, XK_6, move, { .i = 5 } }, - { MODKEY, XK_7, move, { .i = 6 } }, - { MODKEY, XK_8, move, { .i = 7 } }, - { MODKEY, XK_9, move, { .i = 8 } }, - { MODKEY, XK_0, move, { .i = 9 } }, + { MODKEY, XK_1, move, { .i = 0 } }, + { MODKEY, XK_2, move, { .i = 1 } }, + { MODKEY, XK_3, move, { .i = 2 } }, + { MODKEY, XK_4, move, { .i = 3 } }, + { MODKEY, XK_5, move, { .i = 4 } }, + { MODKEY, XK_6, move, { .i = 5 } }, + { MODKEY, XK_7, move, { .i = 6 } }, + { MODKEY, XK_8, move, { .i = 7 } }, + { MODKEY, XK_9, move, { .i = 8 } }, + { MODKEY, XK_0, move, { .i = 9 } }, - { MODKEY, XK_q, killclient, { 0 } }, + { MODKEY, XK_q, killclient, { 0 } }, - { MODKEY, XK_u, focusurgent, { 0 } }, - { MODKEY|ShiftMask, XK_u, toggle, { .v = (void*) &urgentswitch } }, + { MODKEY, XK_u, focusurgent, { .v = NULL } }, + { MODKEY|ShiftMask, XK_u, toggle, { .v = (void *)&urgentswitch } }, - { 0, XK_F11, fullscreen, { 0 } }, + { 0, XK_F11, fullscreen, { 0 } }, }; + diff --git a/tabbed.1 b/tabbed.1 @@ -3,9 +3,13 @@ tabbed \- generic tabbed interface .SH SYNOPSIS .B tabbed +.RB [ \-B ] +.RB [ \-b ] .RB [ \-c ] .RB [ \-d ] .RB [ \-k ] +.RB [ \-M ] +.RB [ \-m ] .RB [ \-s ] .RB [ \-v ] .RB [ \-g @@ -38,6 +42,12 @@ disabled by providing the -s parameter. If no command is provided tabbed will just print its xid and run no command. .SH OPTIONS .TP +.B \-B +show the status bar. +.TP +.B \-b +do not show the status bar. +.TP .B \-c close tabbed when the last tab is closed. Mutually exclusive with -f. .TP @@ -61,6 +71,12 @@ for further details. close foreground tabbed client (instead of tabbed and all clients) when WM_DELETE_WINDOW is sent. .TP +.B \-M +show the status bar at the bottom. +.TP +.B \-b +show the status bar at the top. +.TP .BI \-n " name" will set the WM_CLASS attribute to .I name. @@ -131,6 +147,12 @@ toggle autofocus of urgent tabs .B Ctrl\-Tab toggle between the selected and last selected tab .TP +.B Ctrl\-b +toggle the status bar +.TP +.B Ctrl\-m +toggle the bar position between top and bottom +.TP .B Ctrl\-t open dmenu to either create a new tab appending the entered string or select an already existing tab. diff --git a/tabbed.c b/tabbed.c @@ -82,6 +82,7 @@ typedef struct { char name[256]; Window win; int tabx; + int remapped; Bool urgent; Bool closed; } Client; @@ -99,6 +100,7 @@ static void drawbar(void); static void drawtext(const char *text, XftColor col[ColLast]); static void *ecalloc(size_t n, size_t size); static void *erealloc(void *o, size_t size); +static void embedwindow(Window w); static void expose(const XEvent *e); static void focus(int c); static void focusin(const XEvent *e); @@ -119,6 +121,7 @@ static void maprequest(const XEvent *e); static void move(const Arg *arg); static void movetab(const Arg *arg); static void propertynotify(const XEvent *e); +static void reembedclients(void); static void resize(int c, int w, int h); static void rotate(const Arg *arg); static void run(void); @@ -129,6 +132,8 @@ static void sigchld(int unused); static void spawn(const Arg *arg); static int textnw(const char *text, unsigned int len); static void toggle(const Arg *arg); +static void togglebar(const Arg *arg); +static void toggletop(const Arg *arg); static void unmanage(int c); static void unmapnotify(const XEvent *e); static void updatenumlockmask(void); @@ -182,7 +187,7 @@ buttonpress(const XEvent *e) int i, fc; Arg arg; - if (ev->y < 0 || ev->y > bh) + if (showbar == False || ev->y < 0 || ev->y > bh) return; if (((fc = getfirsttab()) > 0 && ev->x < TEXTW(before)) || ev->x < 0) @@ -253,11 +258,12 @@ configurenotify(const XEvent *e) if (ev->window == win && (ev->width != ww || ev->height != wh)) { ww = ev->width; wh = ev->height; + XFreePixmap(dpy, dc.drawable); dc.drawable = XCreatePixmap(dpy, root, ww, wh, - DefaultDepth(dpy, screen)); + DefaultDepth(dpy, screen)); if (sel > -1) - resize(sel, ww, wh - bh); + resize(sel, ww, wh - (showbar? bh : 0)); XSync(dpy, False); } } @@ -271,9 +277,9 @@ configurerequest(const XEvent *e) if ((c = getclient(ev->window)) > -1) { wc.x = 0; - wc.y = bh; + wc.y = showbar? (bottombar? bh : 0) : 0; wc.width = ww; - wc.height = wh - bh; + wc.height = wh - (showbar? bh : 0); wc.border_width = 0; wc.sibling = ev->above; wc.stack_mode = ev->detail; @@ -318,12 +324,16 @@ drawbar(void) int c, cc, fc, width; char *name = NULL; + if (showbar == False) + return; + if (nclients == 0) { dc.x = 0; dc.w = ww; XFetchName(dpy, win, &name); - drawtext(name ? name : "", dc.norm); - XCopyArea(dpy, dc.drawable, win, dc.gc, 0, 0, ww, bh, 0, 0); + drawtext(name? name : "", dc.norm); + XCopyArea(dpy, dc.drawable, win, dc.gc, 0, 0, ww, bh, 0, + bottombar? wh - bh - 1 : 0); XSync(dpy, False); return; @@ -362,7 +372,7 @@ drawbar(void) dc.x += dc.w; clients[c]->tabx = dc.x; } - XCopyArea(dpy, dc.drawable, win, dc.gc, 0, 0, ww, bh, 0, 0); + XCopyArea(dpy, dc.drawable, win, dc.gc, 0, 0, ww, bh, 0, bottombar? wh - bh - 1 : 0); XSync(dpy, False); } @@ -424,6 +434,51 @@ erealloc(void *o, size_t size) } void +embedwindow(Window w) +{ + int i, j; + unsigned int modifiers[] = { 0, LockMask, numlockmask, + numlockmask | LockMask }; + KeyCode code; + XEvent e; + + updatenumlockmask(); + + XWithdrawWindow(dpy, w, 0); + XReparentWindow(dpy, w, win, 0, (showbar? (bottombar? 0 : bh) : 0)); + XSelectInput(dpy, w, PropertyChangeMask | + StructureNotifyMask | EnterWindowMask); + XSync(dpy, False); + + for (i = 0; i < LENGTH(keys); i++) { + 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); + } + } + } + + XLowerWindow(dpy, w); + XMapWindow(dpy, w); + + e.xclient.window = w; + e.xclient.type = ClientMessage; + e.xclient.message_type = wmatom[XEmbed]; + e.xclient.format = 32; + e.xclient.data.l[0] = CurrentTime; + e.xclient.data.l[1] = XEMBED_EMBEDDED_NOTIFY; + e.xclient.data.l[2] = 0; + e.xclient.data.l[3] = win; + e.xclient.data.l[4] = 0; + XSendEvent(dpy, root, False, NoEventMask, &e); + + XSync(dpy, False); +} + + +void expose(const XEvent *e) { const XExposeEvent *ev = &e->xexpose; @@ -454,7 +509,7 @@ focus(int c) if (c < 0 || c >= nclients) return; - resize(c, ww, wh - bh); + resize(c, ww, wh - (showbar? bh : 0)); XRaiseWindow(dpy, clients[c]->win); XSetInputFocus(dpy, clients[c]->win, RevertToParent, CurrentTime); sendxembed(c, XEMBED_FOCUS_IN, XEMBED_FOCUS_CURRENT, 0, 0); @@ -686,81 +741,44 @@ killclient(const Arg *arg) void manage(Window w) { - updatenumlockmask(); - { - int i, j, nextpos; - unsigned int modifiers[] = { 0, LockMask, numlockmask, - numlockmask | LockMask }; - KeyCode code; - Client *c; - XEvent e; - - XWithdrawWindow(dpy, w, 0); - XReparentWindow(dpy, w, win, 0, bh); - XSelectInput(dpy, w, PropertyChangeMask | - StructureNotifyMask | EnterWindowMask); - XSync(dpy, False); + Client *c; + int nextpos; - for (i = 0; i < LENGTH(keys); i++) { - 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 = ecalloc(1, sizeof *c); - c->win = w; - - nclients++; - clients = erealloc(clients, sizeof(Client *) * nclients); + embedwindow(w); - if(npisrelative) { - nextpos = sel + newposition; - } else { - if (newposition < 0) - nextpos = nclients - newposition; - else - nextpos = newposition; - } - if (nextpos >= nclients) - nextpos = nclients - 1; - if (nextpos < 0) - nextpos = 0; - - if (nclients > 1 && nextpos < nclients - 1) - memmove(&clients[nextpos + 1], &clients[nextpos], - sizeof(Client *) * (nclients - nextpos - 1)); - - clients[nextpos] = c; - updatetitle(nextpos); - - XLowerWindow(dpy, w); - XMapWindow(dpy, w); - - e.xclient.window = w; - e.xclient.type = ClientMessage; - e.xclient.message_type = wmatom[XEmbed]; - e.xclient.format = 32; - e.xclient.data.l[0] = CurrentTime; - e.xclient.data.l[1] = XEMBED_EMBEDDED_NOTIFY; - e.xclient.data.l[2] = 0; - e.xclient.data.l[3] = win; - e.xclient.data.l[4] = 0; - XSendEvent(dpy, root, False, NoEventMask, &e); + c = ecalloc(1, sizeof *c); + c->win = w; - XSync(dpy, False); + nclients++; + clients = erealloc(clients, sizeof(Client *) * nclients); - /* Adjust sel before focus does set it to lastsel. */ - if (sel >= nextpos) - sel++; - focus(nextfocus ? nextpos : - sel < 0 ? 0 : - sel); - nextfocus = foreground; + if(npisrelative) { + nextpos = sel + newposition; + } else { + if (newposition < 0) + nextpos = nclients - newposition; + else + nextpos = newposition; } + if (nextpos >= nclients) + nextpos = nclients - 1; + if (nextpos < 0) + nextpos = 0; + + if (nclients > 1 && nextpos < nclients - 1) + memmove(&clients[nextpos + 1], &clients[nextpos], + sizeof(Client *) * (nclients - nextpos - 1)); + + clients[nextpos] = c; + updatetitle(nextpos); + + /* Adjust sel before focus does set it to lastsel. */ + if (sel >= nextpos) + sel++; + focus(nextfocus ? nextpos : + sel < 0 ? 0 : + sel); + nextfocus = foreground; } void @@ -860,20 +878,30 @@ propertynotify(const XEvent *e) } void +reembedclients(void) +{ + for (int c = 0; c < nclients; c++) { + embedwindow(clients[c]->win); + clients[c]->remapped = 2; + } + focus(sel); +} + +void resize(int c, int w, int h) { XConfigureEvent ce; XWindowChanges wc; ce.x = 0; - ce.y = bh; + ce.y = (showbar? bh : 0); ce.width = wc.width = w; ce.height = wc.height = h; ce.type = ConfigureNotify; ce.display = dpy; ce.event = clients[c]->win; ce.window = clients[c]->win; - ce.above = None; + ce.above = -1; ce.override_redirect = False; ce.border_width = 0; @@ -1104,7 +1132,21 @@ textnw(const char *text, unsigned int len) void toggle(const Arg *arg) { - *(Bool*) arg->v = !*(Bool*) arg->v; + *(Bool*) arg->v = !*(Bool*) arg->v; +} + +void +togglebar(const Arg *arg) +{ + showbar = !showbar; + reembedclients(); +} + +void +toggletop(const Arg *arg) +{ + bottombar = !bottombar; + reembedclients(); } void @@ -1172,8 +1214,11 @@ unmapnotify(const XEvent *e) const XUnmapEvent *ev = &e->xunmap; int c; - if ((c = getclient(ev->window)) > -1) - unmanage(c); + if ((c = getclient(ev->window)) > -1) { + if (clients[c]->remapped == 0) + unmanage(c); + clients[c]->remapped--; + } } void @@ -1252,7 +1297,7 @@ xsettitle(Window w, const char *str) void usage(void) { - die("usage: %s [-dfksv] [-g geometry] [-n name] [-p [s+/-]pos]\n" + die("usage: %s [-BbdfkMmsv] [-g geometry] [-n name] [-p [s+/-]pos]\n" " [-r narg] [-o color] [-O color] [-t color] [-T color]\n" " [-u color] [-U color] command...\n", argv0); } @@ -1265,6 +1310,12 @@ main(int argc, char *argv[]) char *pstr; ARGBEGIN { + case 'B': + showbar = True; + break; + case 'b': + showbar = False; + break; case 'c': closelastclient = True; fillagain = False; @@ -1281,6 +1332,12 @@ main(int argc, char *argv[]) case 'k': killclientsfirst = True; break; + case 'M': + bottombar = True; + break; + case 'm': + bottombar = False; + break; case 'n': wmname = EARGF(usage()); break;