select or open new tab with Ctrl-T - tabbed - tab interface for application supporting Xembed
  HTML git clone git://git.suckless.org/tabbed
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
   DIR commit a1aa453e99f198186d39b90b1229cc1bcb8dc466
   DIR parent b46a3a278856bb33dc299d11d0be1b01d04df39c
  HTML 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:
         M config.def.h                        |       9 +++++++++
         M tabbed.c                            |      51 ++++++++++++++++++++++++++-----
       
       2 files changed, 53 insertions(+), 7 deletions(-)
       ---
   DIR 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 } },
   DIR 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]);
                        }