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;