tabbed-noxz

[fork] suckless tabbed - personal fork
git clone https://noxz.tech/git/tabbed-noxz.git
tabbed-noxz

commit: 4592bfb4ac1ba80c5b4fe749d561653aad68a3c8
parent: 0833df691cdaa0fcbb6253644447464864e0d908
author: Chris Noxz <chris@noxz.tech>
date:   Sun, 13 Oct 2024 11:04:56 +0200
ungrab and drag tabs
Mconfig.def.h18++--
Mtabbed.c114++++++++++++++++----
2 files changed, 97 insertions(+), 35 deletions(-)
diff --git a/config.def.h b/config.def.h
@@ -2,6 +2,8 @@
 
 /* appearance */
 static char* font         = "monospace:size=9";
+static char* pidbgcolor   = "#222222";
+static char* pidfgcolor   = "#cccccc";
 static char* normbgcolor  = "#222222";
 static char* normfgcolor  = "#cccccc";
 static char* selbgcolor   = "#555555";
@@ -11,7 +13,7 @@ static char* urgfgcolor   = "#cc0000";
 static char* before       = "<";
 static char* after        = ">";
 static char* titletrim    = "...";
-static int tabwidth       = 200;
+static int tabwidth       = 100;
 static int focusnew       = 1;
 static int urgentswitch   = 0;
 
@@ -23,21 +25,13 @@ static int urgentswitch   = 0;
 static int newposition   = -1;
 static int npisrelative  = 0;
 
-#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 -w $1`\" &&" \
-                "xprop -id $1 -f $0 8s -set $0 \"$prop\"", \
-                p, winid, NULL \
-        } \
-}
-
 /*
  * Xresources preferences to load at startup
  */
 ResourcePref resources[] = {
 	{ "font",                   STRING,  &font},
+	{ "pidBackground",          STRING,  &pidbgcolor},
+	{ "pidForeground",          STRING,  &pidfgcolor},
 	{ "normalBackground",       STRING,  &normbgcolor},
 	{ "normalForeground",       STRING,  &normfgcolor},
 	{ "selectedBackground",     STRING,  &selbgcolor},
@@ -66,7 +60,6 @@ static const Key keys[] = {
 	{ MODKEY|ShiftMask,     XK_k,      movetab,     { .i = +1 } },
 	{ MODKEY,               XK_Tab,    rotate,      { .i = 0 } },
 
-	{ MODKEY,               XK_grave,  spawn,       SETPROP("_TABBED_SELECT_TAB") },
 	{ MODKEY,               XK_1,      move,        { .i = 0 } },
 	{ MODKEY,               XK_2,      move,        { .i = 1 } },
 	{ MODKEY,               XK_3,      move,        { .i = 2 } },
@@ -82,6 +75,7 @@ static const Key keys[] = {
 
 	{ MODKEY,               XK_u,      focusurgent, { 0 } },
 	{ MODKEY|ShiftMask,     XK_u,      toggle,      { .v = (void*) &urgentswitch } },
+	{ MODKEY,               XK_b,      togglebar,   { 0 } },
 
 	{ 0,                    XK_F11,    fullscreen,  { 0 } },
 };
diff --git a/tabbed.c b/tabbed.c
@@ -68,6 +68,8 @@ typedef struct {
 
 typedef struct {
 	int x, y, w, h;
+	Bool visible;
+	XftColor pid[ColLast];
 	XftColor norm[ColLast];
 	XftColor sel[ColLast];
 	XftColor urg[ColLast];
@@ -135,6 +137,7 @@ static void keypress(const XEvent *e);
 static void killclient(const Arg *arg);
 static void manage(Window win);
 static void maprequest(const XEvent *e);
+static void motionnotify(const XEvent *e);
 static void move(const Arg *arg);
 static void movetab(const Arg *arg);
 static void propertynotify(const XEvent *e);
@@ -148,6 +151,7 @@ static void setup(void);
 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 unmanage(int c);
 static void unmapnotify(const XEvent *e);
 static void updatenumlockmask(void);
@@ -163,15 +167,16 @@ static void (*handler[LASTEvent]) (const XEvent *) = {
 	[ConfigureNotify] = configurenotify,
 	[ConfigureRequest] = configurerequest,
 	[CreateNotify] = createnotify,
-	[UnmapNotify] = unmapnotify,
 	[DestroyNotify] = destroynotify,
 	[Expose] = expose,
 	[FocusIn] = focusin,
 	[KeyPress] = keypress,
 	[MapRequest] = maprequest,
+	[MotionNotify] = motionnotify,
 	[PropertyNotify] = propertynotify,
+	[UnmapNotify] = unmapnotify,
 };
-static int bh, obh, wx, wy, ww, wh;
+static int bh, obh, vbh, wx, wy, ww, wh;
 static unsigned int numlockmask;
 static Bool running = True, nextfocus, doinitspawn = True,
             fillagain = False, closelastclient = False,
@@ -185,6 +190,7 @@ static int nclients, sel = -1, lastsel = -1;
 static int (*xerrorxlib)(Display *, XErrorEvent *);
 static int cmd_append_pos;
 static char winid[64];
+static char winpid[64];
 static char **cmd;
 static char *wmname = "tabbed";
 static pid_t nextpid;
@@ -228,11 +234,11 @@ pid_t getchildpid(pid_t pid) {
 		return -1;
 
 	f = fopen(path, "r");
-	if(f == NULL)
+	if (f == NULL)
 		return -1;
 
 	// guess first child
-	if(fscanf(f, "%d ", &pid) != 1)
+	if (fscanf(f, "%d ", &pid) != 1)
 		return -1;
 
 	return pid;
@@ -248,7 +254,7 @@ buttonpress(const XEvent *e)
 	if (ev->y < 0 || ev->y > bh)
 		return;
 
-	if (((fc = getfirsttab()) > 0 && ev->x < TEXTW(before)) || ev->x < 0)
+	if (ev->x < TEXTW(winpid) || ((fc = getfirsttab()) > 0 && ev->x < (TEXTW(before))) || ev->x < 0)
 		return;
 
 	for (i = fc; i < nclients; i++) {
@@ -408,6 +414,15 @@ drawbar(void)
 	char *name = NULL;
 	char tabtitle[256];
 
+	if ((dc.visible ? vbh : 0) != bh) {
+		bh = dc.visible ? vbh : 0;
+		for (c = 0; c < nclients; c++)
+			XMoveResizeWindow(dpy, clients[c]->win, 0, bh, ww, wh - bh);
+	}
+
+	if (bh == 0)
+		return;
+
 	if (nclients == 0) {
 		dc.x = 0;
 		dc.w = ww;
@@ -422,7 +437,7 @@ drawbar(void)
 	width = ww;
 	cc = ww / tabwidth;
 	if (nclients > cc)
-		cc = (ww - TEXTW(before) - TEXTW(after)) / tabwidth;
+		cc = (ww - TEXTW(winpid) - TEXTW(before) - TEXTW(after)) / tabwidth;
 
 	if ((fc = getfirsttab()) + cc < nclients) {
 		dc.w = TEXTW(after);
@@ -432,6 +447,11 @@ drawbar(void)
 	}
 	dc.x = 0;
 
+	dc.w = TEXTW(winpid);
+	drawtext(winpid, dc.pid);
+	dc.x += dc.w;
+	width -= dc.w;
+
 	if (fc > 0) {
 		dc.w = TEXTW(before);
 		drawtext(before, dc.sel);
@@ -483,11 +503,8 @@ drawtext(const char *text, XftColor col[ColLast])
 		return;
 
 	memcpy(buf, text, len);
-	if (len < olen) {
-		for (i = len, j = strlen(titletrim); j && i;
-		     buf[--i] = titletrim[--j])
-			;
-	}
+	if (len < olen)
+		for (i = len, j = strlen(titletrim); j && i; buf[--i] = titletrim[--j]);
 
 	d = XftDrawCreate(dpy, dc.drawable, DefaultVisual(dpy, screen), DefaultColormap(dpy, screen));
 	XftDrawStringUtf8(d, &col[ColFG], dc.font.xfont, x, y, (XftChar8 *) buf, len);
@@ -673,7 +690,7 @@ getfirsttab(void)
 
 	cc = ww / tabwidth;
 	if (nclients > cc)
-		cc = (ww - TEXTW(before) - TEXTW(after)) / tabwidth;
+		cc = (ww - TEXTW(winpid) - TEXTW(before) - TEXTW(after)) / tabwidth;
 
 	ret = sel - cc / 2 + (cc + 1) % 2;
 	return ret < 0 ? 0 :
@@ -867,11 +884,33 @@ maprequest(const XEvent *e)
 		manage(ev->window);
 }
 
+void
+motionnotify(const XEvent *e)
+{
+	int i, fc;
+	const XMotionEvent *ev = &e->xmotion;
+	Arg arg;
+
+	if (sel < 0 || !(ev->state & Button1Mask) || ev->x < 0 ||
+	    ((fc = getfirsttab()) > 0 && ev->x < TEXTW(before)))
+		return;
+
+	for (i = fc; i < nclients; i++) {
+		if (clients[i]->tabx > ev->x) {
+			if (i == sel + (arg.i = 1) || i == sel + (arg.i = -1))
+				movetab(&arg);
+			break;
+		}
+	}
+}
+
 void
 move(const Arg *arg)
 {
-	if (arg->i >= 0 && arg->i < nclients)
-		focus(arg->i);
+	int i;
+
+	if ((i = arg->i < nclients ? arg->i : nclients - 1) >= 0)
+		focus(i);
 }
 
 void
@@ -1094,6 +1133,7 @@ setup(void)
 	XClassHint class_hint;
 	XSizeHints *size_hint;
 	struct sigaction sa;
+	pid_t pid = getpid();
 
 	/* do not transform children into zombies when they terminate */
 	sigemptyset(&sa.sa_mask);
@@ -1108,7 +1148,8 @@ setup(void)
 	screen = DefaultScreen(dpy);
 	root = RootWindow(dpy, screen);
 	initfont(font);
-	bh = dc.h = dc.font.height + 2;
+	vbh = dc.h = dc.font.height + 4;
+	dc.visible = 1;
 
 	/* init atoms */
 	wmatom[WMDelete] = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
@@ -1154,6 +1195,8 @@ setup(void)
 			wy = dh + wy - wh - 1;
 	}
 
+	dc.pid[ColBG] = getcolor(pidbgcolor);
+	dc.pid[ColFG] = getcolor(pidfgcolor);
 	dc.norm[ColBG] = getcolor(normbgcolor);
 	dc.norm[ColFG] = getcolor(normfgcolor);
 	dc.sel[ColBG] = getcolor(selbgcolor);
@@ -1170,7 +1213,7 @@ setup(void)
 	XSelectInput(dpy, win, SubstructureNotifyMask | FocusChangeMask |
 	             ButtonPressMask | ExposureMask | KeyPressMask |
 	             PropertyChangeMask | StructureNotifyMask |
-	             SubstructureRedirectMask);
+	             SubstructureRedirectMask | ButtonMotionMask);
 	xerrorxlib = XSetErrorHandler(xerror);
 
 	class_hint.res_name = wmname;
@@ -1178,13 +1221,14 @@ setup(void)
 	XSetClassHint(dpy, win, &class_hint);
 
 	size_hint = XAllocSizeHints();
-	if (!isfixed) {
-		size_hint->flags = PSize | PMinSize;
-		size_hint->height = wh;
-		size_hint->width = ww;
-		size_hint->min_height = bh + 1;
-	} else {
-		size_hint->flags = PMaxSize | PMinSize;
+	size_hint->flags = PSize | PResizeInc | PBaseSize | PMinSize;
+	size_hint->height = wh;
+	size_hint->width = ww;
+	size_hint->min_height = vbh + 1;
+	size_hint->min_width = tabwidth;
+
+	if (isfixed) {
+		size_hint->flags |= PMaxSize;
 		size_hint->min_width = size_hint->max_width = ww;
 		size_hint->min_height = size_hint->max_height = wh;
 	}
@@ -1194,8 +1238,11 @@ setup(void)
 	XFree(wmh);
 
 	XSetWMProtocols(dpy, win, &wmatom[WMDelete], 1);
+	XChangeProperty(dpy, win, XInternAtom(dpy, "_NET_WM_PID", False),
+		XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&pid, 1);
 
 	snprintf(winid, sizeof(winid), "%lu", win);
+	snprintf(winpid, sizeof(winpid), "%X", pid);
 	setenv("XEMBED", winid, 1);
 
 	nextfocus = focusnew;
@@ -1254,9 +1301,21 @@ toggle(const Arg *arg)
     *(Bool*) arg->v = !*(Bool*) arg->v;
 }
 
+void
+togglebar(const Arg *arg)
+{
+	dc.visible = !dc.visible;
+	drawbar();
+}
+
 void
 unmanage(int c)
 {
+	int i, j;
+	unsigned int modifiers[] = { 0, LockMask, numlockmask,
+	                             numlockmask | LockMask };
+	KeyCode code;
+
 	if (c < 0 || c >= nclients) {
 		drawbar();
 		XSync(dpy, False);
@@ -1266,6 +1325,15 @@ unmanage(int c)
 	if (!nclients)
 		return;
 
+	/* ungrab keys */
+	for (i = 0; i < LENGTH(keys); i++) {
+		if ((code = XKeysymToKeycode(dpy, keys[i].keysym))) {
+			for (j = 0; j < LENGTH(modifiers); j++) {
+				XUngrabKey(dpy, code, keys[i].mod | modifiers[j], clients[c]->win);
+			}
+		}
+	}
+
 	if (c == 0) {
 		/* First client. */
 		nclients--;