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 +#include #include +#include #include +#include #include #include #include @@ -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;