sites

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

tabbed-unix-socket-control-20240319.diff (10135B)


      1 diff --git a/Makefile b/Makefile
      2 index 54ba350..049a086 100644
      3 --- a/Makefile
      4 +++ b/Makefile
      5 @@ -10,7 +10,7 @@ DOCPREFIX = ${PREFIX}/share/doc/${NAME}
      6  
      7  # use system flags.
      8  TABBED_CFLAGS = -I/usr/X11R6/include -I/usr/include/freetype2 ${CFLAGS}
      9 -TABBED_LDFLAGS = -L/usr/X11R6/lib -lX11 -lfontconfig -lXft ${LDFLAGS}
     10 +TABBED_LDFLAGS = -L/usr/X11R6/lib -lX11 -lfontconfig -lXft -lpthread ${LDFLAGS}
     11  TABBED_CPPFLAGS = -DVERSION=\"${VERSION}\" -D_DEFAULT_SOURCE -D_XOPEN_SOURCE=700L
     12  
     13  # OpenBSD (uncomment)
     14 diff --git a/config.def.h b/config.def.h
     15 index 51bb13d..2c0f3fd 100644
     16 --- a/config.def.h
     17 +++ b/config.def.h
     18 @@ -32,35 +32,3 @@ static Bool npisrelative  = False;
     19                  p, winid, NULL \
     20          } \
     21  }
     22 -
     23 -#define MODKEY ControlMask
     24 -static const Key keys[] = {
     25 -	/* modifier             key        function     argument */
     26 -	{ MODKEY|ShiftMask,     XK_Return, focusonce,   { 0 } },
     27 -	{ MODKEY|ShiftMask,     XK_Return, spawn,       { 0 } },
     28 -
     29 -	{ MODKEY|ShiftMask,     XK_l,      rotate,      { .i = +1 } },
     30 -	{ MODKEY|ShiftMask,     XK_h,      rotate,      { .i = -1 } },
     31 -	{ MODKEY|ShiftMask,     XK_j,      movetab,     { .i = -1 } },
     32 -	{ MODKEY|ShiftMask,     XK_k,      movetab,     { .i = +1 } },
     33 -	{ MODKEY,               XK_Tab,    rotate,      { .i = 0 } },
     34 -
     35 -	{ MODKEY,               XK_grave,  spawn,       SETPROP("_TABBED_SELECT_TAB") },
     36 -	{ MODKEY,               XK_1,      move,        { .i = 0 } },
     37 -	{ MODKEY,               XK_2,      move,        { .i = 1 } },
     38 -	{ MODKEY,               XK_3,      move,        { .i = 2 } },
     39 -	{ MODKEY,               XK_4,      move,        { .i = 3 } },
     40 -	{ MODKEY,               XK_5,      move,        { .i = 4 } },
     41 -	{ MODKEY,               XK_6,      move,        { .i = 5 } },
     42 -	{ MODKEY,               XK_7,      move,        { .i = 6 } },
     43 -	{ MODKEY,               XK_8,      move,        { .i = 7 } },
     44 -	{ MODKEY,               XK_9,      move,        { .i = 8 } },
     45 -	{ MODKEY,               XK_0,      move,        { .i = 9 } },
     46 -
     47 -	{ MODKEY,               XK_q,      killclient,  { 0 } },
     48 -
     49 -	{ MODKEY,               XK_u,      focusurgent, { 0 } },
     50 -	{ MODKEY|ShiftMask,     XK_u,      toggle,      { .v = (void*) &urgentswitch } },
     51 -
     52 -	{ 0,                    XK_F11,    fullscreen,  { 0 } },
     53 -};
     54 diff --git a/tabbed.c b/tabbed.c
     55 index 81be5e4..47de28b 100644
     56 --- a/tabbed.c
     57 +++ b/tabbed.c
     58 @@ -2,8 +2,12 @@
     59   * See LICENSE file for copyright and license details.
     60   */
     61  
     62 +#include <sys/socket.h>
     63 +#include <sys/un.h>
     64  #include <sys/wait.h>
     65 +#include <fcntl.h>
     66  #include <locale.h>
     67 +#include <pthread.h>
     68  #include <signal.h>
     69  #include <stdarg.h>
     70  #include <stdio.h>
     71 @@ -47,6 +51,10 @@
     72  #define LENGTH(x)               (sizeof((x)) / sizeof(*(x)))
     73  #define CLEANMASK(mask)         (mask & ~(numlockmask | LockMask))
     74  #define TEXTW(x)                (textnw(x, strlen(x)) + dc.font.height)
     75 +#define streq(s1, s2)           (strcmp((s1), (s2)) == 0)
     76 +
     77 +/* UNIX Socket settings */
     78 +#define SOCKET_PATH_TPL  "/tmp/tabbed_%s-socket"
     79  
     80  enum { ColFG, ColBG, ColLast };       /* color */
     81  enum { WMProtocols, WMDelete, WMName, WMState, WMFullscreen,
     82 @@ -57,13 +65,6 @@ typedef union {
     83  	const void *v;
     84  } Arg;
     85  
     86 -typedef struct {
     87 -	unsigned int mod;
     88 -	KeySym keysym;
     89 -	void (*func)(const Arg *);
     90 -	const Arg arg;
     91 -} Key;
     92 -
     93  typedef struct {
     94  	int x, y, w, h;
     95  	XftColor norm[ColLast];
     96 @@ -111,6 +112,7 @@ static int getclient(Window w);
     97  static XftColor getcolor(const char *colstr);
     98  static int getfirsttab(void);
     99  static Bool gettextprop(Window w, Atom atom, char *text, unsigned int size);
    100 +static void handle_message(char *msg, int msg_len, FILE *rsp, int sock_fd);
    101  static void initfont(const char *fontstr);
    102  static Bool isprotodel(int c);
    103  static void keypress(const XEvent *e);
    104 @@ -119,6 +121,7 @@ static void manage(Window win);
    105  static void maprequest(const XEvent *e);
    106  static void move(const Arg *arg);
    107  static void movetab(const Arg *arg);
    108 +static void process_message(char **args, int num, FILE *rsp, int sock_fd);
    109  static void propertynotify(const XEvent *e);
    110  static void resize(int c, int w, int h);
    111  static void rotate(const Arg *arg);
    112 @@ -148,7 +151,6 @@ static void (*handler[LASTEvent]) (const XEvent *) = {
    113  	[DestroyNotify] = destroynotify,
    114  	[Expose] = expose,
    115  	[FocusIn] = focusin,
    116 -	[KeyPress] = keypress,
    117  	[MapRequest] = maprequest,
    118  	[PropertyNotify] = propertynotify,
    119  };
    120 @@ -169,6 +171,8 @@ static char winid[64];
    121  static char **cmd;
    122  static char *wmname = "tabbed";
    123  static const char *geometry;
    124 +static int sock_fd = -1;
    125 +static char socket_path[256];
    126  
    127  char *argv0;
    128  
    129 @@ -228,6 +232,9 @@ cleanup(void)
    130  	XDestroyWindow(dpy, win);
    131  	XSync(dpy, False);
    132  	free(cmd);
    133 +
    134 +	close(sock_fd);
    135 +	unlink(socket_path);
    136  }
    137  
    138  void
    139 @@ -657,22 +664,6 @@ isprotodel(int c)
    140  	return ret;
    141  }
    142  
    143 -void
    144 -keypress(const XEvent *e)
    145 -{
    146 -	const XKeyEvent *ev = &e->xkey;
    147 -	unsigned int i;
    148 -	KeySym keysym;
    149 -
    150 -	keysym = XkbKeycodeToKeysym(dpy, (KeyCode)ev->keycode, 0, 0);
    151 -	for (i = 0; i < LENGTH(keys); i++) {
    152 -		if (keysym == keys[i].keysym &&
    153 -		    CLEANMASK(keys[i].mod) == CLEANMASK(ev->state) &&
    154 -		    keys[i].func)
    155 -			keys[i].func(&(keys[i].arg));
    156 -	}
    157 -}
    158 -
    159  void
    160  killclient(const Arg *arg)
    161  {
    162 @@ -713,16 +704,6 @@ manage(Window w)
    163  		             StructureNotifyMask | EnterWindowMask);
    164  		XSync(dpy, False);
    165  
    166 -		for (i = 0; i < LENGTH(keys); i++) {
    167 -			if ((code = XKeysymToKeycode(dpy, keys[i].keysym))) {
    168 -				for (j = 0; j < LENGTH(modifiers); j++) {
    169 -					XGrabKey(dpy, code, keys[i].mod |
    170 -					         modifiers[j], w, True,
    171 -					         GrabModeAsync, GrabModeAsync);
    172 -				}
    173 -			}
    174 -		}
    175 -
    176  		c = ecalloc(1, sizeof *c);
    177  		c->win = w;
    178  
    179 @@ -919,6 +900,152 @@ rotate(const Arg *arg)
    180  	}
    181  }
    182  
    183 +void
    184 +handle_message(char *msg, int msg_len, FILE *rsp, int sock_fd)
    185 +{
    186 +	int cap = 8;
    187 +	int num = 0;
    188 +	char **args = calloc(cap, sizeof(char *));
    189 +
    190 +	if (args == NULL) {
    191 +		perror("Handle message: calloc");
    192 +		return;
    193 +	}
    194 +
    195 +	for (int i = 0, j = 0; i < msg_len; i++) {
    196 +		if (msg[i] == 0) {
    197 +			args[num++] = msg + j;
    198 +			j = i + 1;
    199 +		}
    200 +		if (num >= cap) {
    201 +			cap *= 2;
    202 +			char **new = realloc(args, cap * sizeof(char *));
    203 +			if (new == NULL) {
    204 +				free(args);
    205 +				perror("Handle message: realloc");
    206 +				return;
    207 +			} else {
    208 +				args = new;
    209 +			}
    210 +		}
    211 +	}
    212 +
    213 +	if (num < 1) {
    214 +		free(args);
    215 +		perror("No arguments given.");
    216 +		return;
    217 +	}
    218 +
    219 +	char **args_orig = args;
    220 +	process_message(args, num, rsp, sock_fd);
    221 +	free(args_orig);
    222 +}
    223 +
    224 +void
    225 +process_message(char **args, int num, FILE *rsp, int sock_fd)
    226 +{
    227 +	char buf[256];
    228 +	Arg arg;
    229 +	if (streq("rotate", *args)) {
    230 +		if (num == 2) {
    231 +			args++;
    232 +			arg.i = atoi(*args);
    233 +			rotate(&arg);
    234 +		} else {
    235 +			arg.i = 1;
    236 +			rotate(&arg);
    237 +		}
    238 +	} else if (streq("movetab", *args)) {
    239 +		if (num == 2) {
    240 +			args++;
    241 +			arg.i = atoi(*args);
    242 +			movetab(&arg);
    243 +		}
    244 +	} else if (streq("move", *args)) {
    245 +		if (num == 2) {
    246 +			args++;
    247 +			arg.i = atoi(*args);
    248 +			move(&arg);
    249 +		}
    250 +	} else if (streq("toggle", *args)) {
    251 +		arg.v = (void*)&urgentswitch;
    252 +		toggle(&arg);
    253 +	} else if (streq("killclient", *args)) {
    254 +		arg.i = 0;
    255 +		killclient(&arg);
    256 +	} else if (streq("focusonce", *args)) {
    257 +		arg.i = 0;
    258 +		focusonce(&arg);
    259 +	} else if (streq("focusurgent", *args)) {
    260 +		arg.i = 0;
    261 +		focusurgent(&arg);
    262 +	} else if (streq("spawn", *args)) {
    263 +		arg.i = 0;
    264 +		spawn(&arg);
    265 +	} else if (streq("fullscreen", *args)) {
    266 +		arg.i = 0;
    267 +		fullscreen(&arg);
    268 +	} else if (streq("isfirst", *args)) {
    269 +		if (sel == 0) {
    270 +			write(sock_fd, "1", 1);
    271 +		} else {
    272 +			write(sock_fd, "0", 1);
    273 +		}
    274 +	} else if (streq("islast", *args)) {
    275 +		if (nclients > 0 && sel == (nclients-1)) {
    276 +			write(sock_fd, "1", 1);
    277 +		} else {
    278 +			write(sock_fd, "0", 1);
    279 +		}
    280 +	} else if (streq("isempty", *args)) {
    281 +		if (nclients == 0) {
    282 +			write(sock_fd, "1", 1);
    283 +		} else {
    284 +			write(sock_fd, "0", 1);
    285 +		}
    286 +	} else if (streq("totaltabs", *args)) {
    287 +		if (nclients > 0) {
    288 +			snprintf(buf, sizeof(buf), "%d", nclients);
    289 +			write(sock_fd, buf, strlen(buf));
    290 +		} else {
    291 +			write(sock_fd, "0", 1);
    292 +		}
    293 +	} else if (streq("tabnumber", *args)) {
    294 +		snprintf(buf, sizeof(buf), "%d", sel);
    295 +		write(sock_fd, buf, strlen(buf));
    296 +	}
    297 +	fflush(rsp);
    298 +	fclose(rsp);
    299 +}
    300 +
    301 +void *
    302 +socket_run(void *vargp)
    303 +{
    304 +	fd_set descriptors;
    305 +	int cli_fd, n;
    306 +	char msg[BUFSIZ] = {0};
    307 +
    308 +	while (running) {
    309 +		FD_ZERO(&descriptors);
    310 +		FD_SET(sock_fd, &descriptors);
    311 +
    312 +		if (FD_ISSET(sock_fd, &descriptors)) {
    313 +			cli_fd = accept(sock_fd, NULL, 0);
    314 +			if (cli_fd > 0 && (n = recv(cli_fd, msg, sizeof(msg)-1, 0)) > 0) {
    315 +				msg[n] = '\0';
    316 +				FILE *rsp = fdopen(cli_fd, "w");
    317 +				if (rsp != NULL) {
    318 +					handle_message(msg, n, rsp, cli_fd);
    319 +				} else {
    320 +					fprintf(stderr, "warn: Can't open the client socket as file.\n");
    321 +				}
    322 +				close(cli_fd);
    323 +			}
    324 +		}
    325 +	}
    326 +	return NULL;
    327 +}
    328 +
    329  void
    330  run(void)
    331  {
    332 @@ -930,6 +1057,9 @@ run(void)
    333  	if (doinitspawn == True)
    334  		spawn(NULL);
    335  
    336 +	pthread_t thread_id;
    337 +	pthread_create(&thread_id, NULL, socket_run, NULL);
    338 +
    339  	while (running) {
    340  		XNextEvent(dpy, &ev);
    341  		if (handler[ev.type])
    342 @@ -1083,6 +1213,35 @@ setup(void)
    343  
    344  	nextfocus = foreground;
    345  	focus(-1);
    346 +
    347 +	/* Setup UNIX socket */
    348 +	struct sockaddr_un sock_address;
    349 +	if (sock_fd == -1) {
    350 +		snprintf(socket_path, sizeof(socket_path), SOCKET_PATH_TPL, winid);
    351 +
    352 +		sock_address.sun_family = AF_UNIX;
    353 +		if (snprintf(sock_address.sun_path, sizeof(sock_address.sun_path), "%s", socket_path) < 0) {
    354 +			fprintf(stderr, "Couldn't write the socket path.\n");
    355 +		}
    356 +
    357 +		sock_fd = socket(AF_UNIX, SOCK_STREAM, 0);
    358 +
    359 +		if (sock_fd == -1) {
    360 +			fprintf(stderr, "Couldn't create the socket.\n");
    361 +		}
    362 +
    363 +		unlink(socket_path);
    364 +
    365 +		if (bind(sock_fd, (struct sockaddr *) &sock_address, sizeof(sock_address)) == -1) {
    366 +			fprintf(stderr, "Couldn't bind a name to the socket.\n");
    367 +		}
    368 +
    369 +		if (listen(sock_fd, SOMAXCONN) == -1) {
    370 +			fprintf(stderr, "Couldn't listen to the socket.\n");
    371 +		}
    372 +	}
    373 +
    374 +	fcntl(sock_fd, F_SETFD, FD_CLOEXEC | fcntl(sock_fd, F_GETFD));
    375  }
    376  
    377  void
    378 @@ -1286,6 +1445,8 @@ main(int argc, char *argv[])
    379  	int replace = 0;
    380  	char *pstr;
    381  
    382 +	XInitThreads();
    383 +
    384  	ARGBEGIN {
    385  	case 'c':
    386  		closelastclient = True;