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;