tabbed

tab interface for application supporting Xembed
git clone git://git.suckless.org/tabbed
Log | Files | Refs | README | LICENSE

commit a1aa453e99f198186d39b90b1229cc1bcb8dc466
parent b46a3a278856bb33dc299d11d0be1b01d04df39c
Author: Markus Teich <markus.teich@stusta.mhn.de>
Date:   Thu, 28 Nov 2013 18:26:21 +0100

select or open new tab with Ctrl-T

The default config will display all open tabs in dmenu. You can either select
one of them, or if you enter a string, that does not start with „0x“, tabbed
will open a new tab and run the command specified at tabbed-startup but append
the entered string to the command first.

With
tabbed -r 2 surf -e ''
you can hit Ctrl-T and either select an already opened tab or enter an URL,
which will be opened in a new tab with surf.

Signed-off-by: Christoph Lohmann <20h@r-36.net>

Diffstat:
config.def.h | 9+++++++++
tabbed.c | 51++++++++++++++++++++++++++++++++++++++++++++-------
2 files changed, 53 insertions(+), 7 deletions(-)

diff --git a/config.def.h b/config.def.h @@ -19,11 +19,20 @@ static const Bool foreground = True; 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 \ + } \ +} + #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") }, { MODKEY|ShiftMask, XK_l, rotate, { .i = +1 } }, { MODKEY|ShiftMask, XK_h, rotate, { .i = -1 } }, diff --git a/tabbed.c b/tabbed.c @@ -48,7 +48,7 @@ enum { ColFG, ColBG, ColLast }; /* color */ enum { WMProtocols, WMDelete, WMName, WMState, WMFullscreen, - XEmbed, WMLast }; /* default atoms */ + XEmbed, WMSelectTab, WMLast }; /* default atoms */ typedef union { int i; @@ -103,6 +103,7 @@ static void focus(int c); static void focusin(const XEvent *e); static void focusonce(const Arg *arg); static void fullscreen(const Arg *arg); +static char* getatom(int a); static int getclient(Window w); static unsigned long getcolor(const char *colstr); static int getfirsttab(void); @@ -157,6 +158,7 @@ static Window root, win; static Client **clients = NULL; static int nclients = 0, sel = -1, lastsel = -1; static int (*xerrorxlib)(Display *, XErrorEvent *); +static int cmd_append_pos = 0; static char winid[64]; static char **cmd = NULL; static char *wmname = "tabbed"; @@ -428,6 +430,7 @@ focus(int c) { /* If c, sel and clients are -1, raise tabbed-win itself */ if(nclients == 0) { + cmd[cmd_append_pos] = NULL; for(i = 0, n = strlen(buf); cmd[i] && n < sizeof(buf); i++) n += snprintf(&buf[n], sizeof(buf) - n, " %s", cmd[i]); @@ -489,6 +492,26 @@ fullscreen(const Arg *arg) { XSendEvent(dpy, root, False, SubstructureNotifyMask, &e); } +char * +getatom(int a) { + static char buf[BUFSIZ]; + Atom adummy; + int idummy; + unsigned long ldummy; + unsigned char *p = NULL; + + XGetWindowProperty(dpy, win, wmatom[a], 0L, BUFSIZ, False, XA_STRING, + &adummy, &idummy, &ldummy, &ldummy, &p); + if(p) { + strncpy(buf, (char *)p, LENGTH(buf)-1); + } else { + buf[0] = '\0'; + } + XFree(p); + + return buf; +} + int getclient(Window w) { int i; @@ -775,8 +798,20 @@ void propertynotify(const XEvent *e) { const XPropertyEvent *ev = &e->xproperty; int c; + char* selection = NULL; + Arg arg; - if(ev->state != PropertyDelete && ev->atom == XA_WM_NAME + if(ev->state == PropertyNewValue && ev->atom == wmatom[WMSelectTab]) { + selection = getatom(WMSelectTab); + if(!strncmp(selection, "0x", 2)) { + arg.i = getclient(strtoul(selection, NULL, 0)); + move(&arg); + } else { + cmd[cmd_append_pos] = selection; + arg.v = cmd; + spawn(&arg); + } + } else if(ev->state != PropertyDelete && ev->atom == XA_WM_NAME && (c = getclient(ev->window)) > -1) { updatetitle(c); } @@ -862,13 +897,14 @@ void setcmd(int argc, char *argv[], int replace) { int i; - cmd = emallocz((argc+2) * sizeof(*cmd)); + cmd = emallocz((argc+3) * sizeof(*cmd)); if (argc == 0) return; for(i = 0; i < argc; i++) cmd[i] = argv[i]; cmd[(replace > 0)? replace : argc] = winid; - cmd[argc + !(replace > 0)] = NULL; + cmd_append_pos = argc + !replace; + cmd[cmd_append_pos] = cmd[cmd_append_pos+1] = NULL; } void @@ -892,8 +928,8 @@ setup(void) { wmatom[XEmbed] = XInternAtom(dpy, "_XEMBED", False); wmatom[WMName] = XInternAtom(dpy, "_NET_WM_NAME", False); wmatom[WMState] = XInternAtom(dpy, "_NET_WM_STATE", False); - wmatom[WMFullscreen] = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", - False); + wmatom[WMFullscreen] = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False); + wmatom[WMSelectTab] = XInternAtom(dpy, "_TABBED_SELECT_TAB", False); /* init appearance */ wx = 0; @@ -943,7 +979,7 @@ setup(void) { dc.norm[ColFG], dc.norm[ColBG]); XMapRaised(dpy, win); XSelectInput(dpy, win, SubstructureNotifyMask|FocusChangeMask| - ButtonPressMask|ExposureMask|KeyPressMask| + ButtonPressMask|ExposureMask|KeyPressMask|PropertyChangeMask| StructureNotifyMask|SubstructureRedirectMask); xerrorxlib = XSetErrorHandler(xerror); @@ -993,6 +1029,7 @@ spawn(const Arg *arg) { fprintf(stderr, "tabbed: execvp %s", ((char **)arg->v)[0]); } else { + cmd[cmd_append_pos] = NULL; execvp(cmd[0], cmd); fprintf(stderr, "tabbed: execvp %s", cmd[0]); }