commit c64aac0fcf7f17b3dcb5d6abd12c050f0975fc06
Author: Jaywalker <[email protected]>
Date:   Tue Mar 19 13:52:39 2024 -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.

diff --git a/tools.suckless.org/tabbed/patches/unix-socket-control/index.md 
b/tools.suckless.org/tabbed/patches/unix-socket-control/index.md
new file mode 100644
index 00000000..c846395e
--- /dev/null
+++ 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 `+
+For example, to select the next tab, you would use: `printf "%s+
+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
new file mode 100644
index 00000000..6903f3bc
--- /dev/null
+++ 
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] = '++                            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.
");
++                              }
++                              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.
");
++              }
++
++              sock_fd = socket(AF_UNIX, SOCK_STREAM, 0);
++
++              if (sock_fd == -1) {
++                      fprintf(stderr, "Couldn't create the socket.
");
++              }
++
++              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.
");
++              }
++
++              if (listen(sock_fd, SOMAXCONN) == -1) {
++                      fprintf(stderr, "Couldn't listen to the socket.
");
++              }
++      }
++
++      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;


Reply via email to