sites

public wiki contents of suckless.org
git clone git://git.suckless.org/sites
Log | Files | Refs

commit c64aac0fcf7f17b3dcb5d6abd12c050f0975fc06
parent e10be19558a92637133680c6501832befc8bec6f
Author: Jaywalker <justin@justinandyadi.com>
Date:   Tue, 19 Mar 2024 13:52:39 -0500

Added new patch for tabbed: unix-socket-control

This patch allows tabbed to be controlled by a UNIX socket, much like bspwm is
controlled by a UNIX socket. This allows a user to use sxhkd for all keybinds
needed to interact with tabbed.

Diffstat:
Atools.suckless.org/tabbed/patches/unix-socket-control/index.md | 31+++++++++++++++++++++++++++++++
Atools.suckless.org/tabbed/patches/unix-socket-control/tabbed-unix-socket-control-20240319.diff | 386+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 417 insertions(+), 0 deletions(-)

diff --git a/tools.suckless.org/tabbed/patches/unix-socket-control/index.md b/tools.suckless.org/tabbed/patches/unix-socket-control/index.md @@ -0,0 +1,31 @@ +UNIX Socket Control +================================ + +Description +----------- +This patch removes the built-in keybinds and instead replaces them with a UNIX socket and protocol for input. +The idea is essentially the same as how bspwm uses bspc for controlling the windows and sxkhd to bind the bspc +commands to keys. + +The UNIX socket path is `/tmp/tabbed_$WINID-socket` where `WINID` is the tabbed window id in decimal format. +The protocol uses the same names and arguments as the internal functions separated by `\0`. + +For example, to select the next tab, you would use: `printf "%s\0%s\0" "rotate" "1" | nc -U "/tmp/tabbed_$WINID-socket"` + +I have also created two scripts to be used in conjunction with this patch: `tabc` and `bspctab`. +The `tabc` script allows more easy access to common actions like changing tabs, moving tabs, etc. +For example, the above command to select the next tab using `tabc` would be simply: `tabc tabnext "$WINID"` + +The `bspctab` uses `tabc` to making using tabs as seamless as possible in bspwm. The intent was for tabs in bspwm +to feel much like using tabs in i3 as that was the only feature I missed when switching to bspwm. +Details on how to implement `bspctab` in your `sxhkdrc` can be found along side the scripts in my repo listed below. + +[Helper Scripts Repo](https://github.com/Jaywalker/tabbed-unix-socket-scripts) + +Download +-------- +* [tabbed-unix-socket-control-20240319.diff](tabbed-unix-socket-control-20240319.diff) + +Authors +------- +* Justin Williams diff --git a/tools.suckless.org/tabbed/patches/unix-socket-control/tabbed-unix-socket-control-20240319.diff b/tools.suckless.org/tabbed/patches/unix-socket-control/tabbed-unix-socket-control-20240319.diff @@ -0,0 +1,386 @@ +diff --git a/Makefile b/Makefile +index 54ba350..049a086 100644 +--- a/Makefile ++++ b/Makefile +@@ -10,7 +10,7 @@ DOCPREFIX = ${PREFIX}/share/doc/${NAME} + + # use system flags. + TABBED_CFLAGS = -I/usr/X11R6/include -I/usr/include/freetype2 ${CFLAGS} +-TABBED_LDFLAGS = -L/usr/X11R6/lib -lX11 -lfontconfig -lXft ${LDFLAGS} ++TABBED_LDFLAGS = -L/usr/X11R6/lib -lX11 -lfontconfig -lXft -lpthread ${LDFLAGS} + TABBED_CPPFLAGS = -DVERSION=\"${VERSION}\" -D_DEFAULT_SOURCE -D_XOPEN_SOURCE=700L + + # OpenBSD (uncomment) +diff --git a/config.def.h b/config.def.h +index 51bb13d..2c0f3fd 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -32,35 +32,3 @@ static Bool npisrelative = False; + p, winid, NULL \ + } \ + } +- +-#define MODKEY ControlMask +-static const Key keys[] = { +- /* modifier key function argument */ +- { MODKEY|ShiftMask, XK_Return, focusonce, { 0 } }, +- { MODKEY|ShiftMask, XK_Return, spawn, { 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_grave, spawn, SETPROP("_TABBED_SELECT_TAB") }, +- { 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_u, focusurgent, { 0 } }, +- { MODKEY|ShiftMask, XK_u, toggle, { .v = (void*) &urgentswitch } }, +- +- { 0, XK_F11, fullscreen, { 0 } }, +-}; +diff --git a/tabbed.c b/tabbed.c +index 81be5e4..47de28b 100644 +--- a/tabbed.c ++++ b/tabbed.c +@@ -2,8 +2,12 @@ + * See LICENSE file for copyright and license details. + */ + ++#include <sys/socket.h> ++#include <sys/un.h> + #include <sys/wait.h> ++#include <fcntl.h> + #include <locale.h> ++#include <pthread.h> + #include <signal.h> + #include <stdarg.h> + #include <stdio.h> +@@ -47,6 +51,10 @@ + #define LENGTH(x) (sizeof((x)) / sizeof(*(x))) + #define CLEANMASK(mask) (mask & ~(numlockmask | LockMask)) + #define TEXTW(x) (textnw(x, strlen(x)) + dc.font.height) ++#define streq(s1, s2) (strcmp((s1), (s2)) == 0) ++ ++/* UNIX Socket settings */ ++#define SOCKET_PATH_TPL "/tmp/tabbed_%s-socket" + + enum { ColFG, ColBG, ColLast }; /* color */ + enum { WMProtocols, WMDelete, WMName, WMState, WMFullscreen, +@@ -57,13 +65,6 @@ typedef union { + const void *v; + } Arg; + +-typedef struct { +- unsigned int mod; +- KeySym keysym; +- void (*func)(const Arg *); +- const Arg arg; +-} Key; +- + typedef struct { + int x, y, w, h; + XftColor norm[ColLast]; +@@ -111,6 +112,7 @@ static int getclient(Window w); + static XftColor getcolor(const char *colstr); + static int getfirsttab(void); + static Bool gettextprop(Window w, Atom atom, char *text, unsigned int size); ++static void handle_message(char *msg, int msg_len, FILE *rsp, int sock_fd); + static void initfont(const char *fontstr); + static Bool isprotodel(int c); + static void keypress(const XEvent *e); +@@ -119,6 +121,7 @@ static void manage(Window win); + static void maprequest(const XEvent *e); + static void move(const Arg *arg); + static void movetab(const Arg *arg); ++static void process_message(char **args, int num, FILE *rsp, int sock_fd); + static void propertynotify(const XEvent *e); + static void resize(int c, int w, int h); + static void rotate(const Arg *arg); +@@ -148,7 +151,6 @@ static void (*handler[LASTEvent]) (const XEvent *) = { + [DestroyNotify] = destroynotify, + [Expose] = expose, + [FocusIn] = focusin, +- [KeyPress] = keypress, + [MapRequest] = maprequest, + [PropertyNotify] = propertynotify, + }; +@@ -169,6 +171,8 @@ static char winid[64]; + static char **cmd; + static char *wmname = "tabbed"; + static const char *geometry; ++static int sock_fd = -1; ++static char socket_path[256]; + + char *argv0; + +@@ -228,6 +232,9 @@ cleanup(void) + XDestroyWindow(dpy, win); + XSync(dpy, False); + free(cmd); ++ ++ close(sock_fd); ++ unlink(socket_path); + } + + void +@@ -657,22 +664,6 @@ isprotodel(int c) + return ret; + } + +-void +-keypress(const XEvent *e) +-{ +- const XKeyEvent *ev = &e->xkey; +- unsigned int i; +- KeySym keysym; +- +- keysym = XkbKeycodeToKeysym(dpy, (KeyCode)ev->keycode, 0, 0); +- for (i = 0; i < LENGTH(keys); i++) { +- if (keysym == keys[i].keysym && +- CLEANMASK(keys[i].mod) == CLEANMASK(ev->state) && +- keys[i].func) +- keys[i].func(&(keys[i].arg)); +- } +-} +- + void + killclient(const Arg *arg) + { +@@ -713,16 +704,6 @@ manage(Window w) + 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); +- } +- } +- } +- + c = ecalloc(1, sizeof *c); + c->win = w; + +@@ -919,6 +900,152 @@ rotate(const Arg *arg) + } + } + ++void ++handle_message(char *msg, int msg_len, FILE *rsp, int sock_fd) ++{ ++ int cap = 8; ++ int num = 0; ++ char **args = calloc(cap, sizeof(char *)); ++ ++ if (args == NULL) { ++ perror("Handle message: calloc"); ++ return; ++ } ++ ++ for (int i = 0, j = 0; i < msg_len; i++) { ++ if (msg[i] == 0) { ++ args[num++] = msg + j; ++ j = i + 1; ++ } ++ if (num >= cap) { ++ cap *= 2; ++ char **new = realloc(args, cap * sizeof(char *)); ++ if (new == NULL) { ++ free(args); ++ perror("Handle message: realloc"); ++ return; ++ } else { ++ args = new; ++ } ++ } ++ } ++ ++ if (num < 1) { ++ free(args); ++ perror("No arguments given."); ++ return; ++ } ++ ++ char **args_orig = args; ++ process_message(args, num, rsp, sock_fd); ++ free(args_orig); ++} ++ ++void ++process_message(char **args, int num, FILE *rsp, int sock_fd) ++{ ++ char buf[256]; ++ Arg arg; ++ if (streq("rotate", *args)) { ++ if (num == 2) { ++ args++; ++ arg.i = atoi(*args); ++ rotate(&arg); ++ } else { ++ arg.i = 1; ++ rotate(&arg); ++ } ++ } else if (streq("movetab", *args)) { ++ if (num == 2) { ++ args++; ++ arg.i = atoi(*args); ++ movetab(&arg); ++ } ++ } else if (streq("move", *args)) { ++ if (num == 2) { ++ args++; ++ arg.i = atoi(*args); ++ move(&arg); ++ } ++ } else if (streq("toggle", *args)) { ++ arg.v = (void*)&urgentswitch; ++ toggle(&arg); ++ } else if (streq("killclient", *args)) { ++ arg.i = 0; ++ killclient(&arg); ++ } else if (streq("focusonce", *args)) { ++ arg.i = 0; ++ focusonce(&arg); ++ } else if (streq("focusurgent", *args)) { ++ arg.i = 0; ++ focusurgent(&arg); ++ } else if (streq("spawn", *args)) { ++ arg.i = 0; ++ spawn(&arg); ++ } else if (streq("fullscreen", *args)) { ++ arg.i = 0; ++ fullscreen(&arg); ++ } else if (streq("isfirst", *args)) { ++ if (sel == 0) { ++ write(sock_fd, "1", 1); ++ } else { ++ write(sock_fd, "0", 1); ++ } ++ } else if (streq("islast", *args)) { ++ if (nclients > 0 && sel == (nclients-1)) { ++ write(sock_fd, "1", 1); ++ } else { ++ write(sock_fd, "0", 1); ++ } ++ } else if (streq("isempty", *args)) { ++ if (nclients == 0) { ++ write(sock_fd, "1", 1); ++ } else { ++ write(sock_fd, "0", 1); ++ } ++ } else if (streq("totaltabs", *args)) { ++ if (nclients > 0) { ++ snprintf(buf, sizeof(buf), "%d", nclients); ++ write(sock_fd, buf, strlen(buf)); ++ } else { ++ write(sock_fd, "0", 1); ++ } ++ } else if (streq("tabnumber", *args)) { ++ snprintf(buf, sizeof(buf), "%d", sel); ++ write(sock_fd, buf, strlen(buf)); ++ } ++ fflush(rsp); ++ fclose(rsp); ++} ++ ++void * ++socket_run(void *vargp) ++{ ++ fd_set descriptors; ++ int cli_fd, n; ++ char msg[BUFSIZ] = {0}; ++ ++ while (running) { ++ FD_ZERO(&descriptors); ++ FD_SET(sock_fd, &descriptors); ++ ++ if (FD_ISSET(sock_fd, &descriptors)) { ++ cli_fd = accept(sock_fd, NULL, 0); ++ if (cli_fd > 0 && (n = recv(cli_fd, msg, sizeof(msg)-1, 0)) > 0) { ++ msg[n] = '\0'; ++ FILE *rsp = fdopen(cli_fd, "w"); ++ if (rsp != NULL) { ++ handle_message(msg, n, rsp, cli_fd); ++ } else { ++ fprintf(stderr, "warn: Can't open the client socket as file.\n"); ++ } ++ close(cli_fd); ++ } ++ } ++ } ++ return NULL; ++} ++ + void + run(void) + { +@@ -930,6 +1057,9 @@ run(void) + if (doinitspawn == True) + spawn(NULL); + ++ pthread_t thread_id; ++ pthread_create(&thread_id, NULL, socket_run, NULL); ++ + while (running) { + XNextEvent(dpy, &ev); + if (handler[ev.type]) +@@ -1083,6 +1213,35 @@ setup(void) + + nextfocus = foreground; + focus(-1); ++ ++ /* Setup UNIX socket */ ++ struct sockaddr_un sock_address; ++ if (sock_fd == -1) { ++ snprintf(socket_path, sizeof(socket_path), SOCKET_PATH_TPL, winid); ++ ++ sock_address.sun_family = AF_UNIX; ++ if (snprintf(sock_address.sun_path, sizeof(sock_address.sun_path), "%s", socket_path) < 0) { ++ fprintf(stderr, "Couldn't write the socket path.\n"); ++ } ++ ++ sock_fd = socket(AF_UNIX, SOCK_STREAM, 0); ++ ++ if (sock_fd == -1) { ++ fprintf(stderr, "Couldn't create the socket.\n"); ++ } ++ ++ unlink(socket_path); ++ ++ if (bind(sock_fd, (struct sockaddr *) &sock_address, sizeof(sock_address)) == -1) { ++ fprintf(stderr, "Couldn't bind a name to the socket.\n"); ++ } ++ ++ if (listen(sock_fd, SOMAXCONN) == -1) { ++ fprintf(stderr, "Couldn't listen to the socket.\n"); ++ } ++ } ++ ++ fcntl(sock_fd, F_SETFD, FD_CLOEXEC | fcntl(sock_fd, F_GETFD)); + } + + void +@@ -1286,6 +1445,8 @@ main(int argc, char *argv[]) + int replace = 0; + char *pstr; + ++ XInitThreads(); ++ + ARGBEGIN { + case 'c': + closelastclient = True;