Add toggle for bar appearance and position. - ttabbed - Simple tabbing application for X11.
       
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) LICENSE
       ---
 (DIR) commit 23623e4ffa5aacb896fa4ac7164ad57918a528ba
 (DIR) parent 1c5078bc89c1ccf1b732c0f35f0689de6b24926f
 (HTM) 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(-)
       ---
 (DIR) 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 } },
        };
       +
 (DIR) diff --git a/tabbed.1 b/tabbed.1
       @@ -3,9 +3,13 @@
        ttabbed \- 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
        ttabbed 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
        ttoggle between the selected and last selected tab
        .TP
       +.B Ctrl\-b
       +ttoggle the status bar
       +.TP
       +.B Ctrl\-m
       +ttoggle 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.
 (DIR) 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
        ttoggle(const Arg *arg)
        {
       -    *(Bool*) arg->v = !*(Bool*) arg->v;
       +        *(Bool*) arg->v = !*(Bool*) arg->v;
       +}
       +
       +void
       +ttogglebar(const Arg *arg)
       +{
       +        showbar = !showbar;
       +        reembedclients();
       +}
       +
       +void
       +ttoggletop(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;