commit 91009a76b6a0313a1be930d77b9c79c312d0da61 from: Brett Fisher date: Thu May 14 15:46:04 2026 UTC bxhkd.c: - Add included C header files. - Create macros for LENGTH and SOCKET_SUFFIX. - Set global variables. - Set function forward declarations. - Create functions for for key handling - Create function for sending keypress commands to window manager socket. - Create functions for gracefully terminating and cleanup (releasing keys and closing the X display). - Create the main() function. commit - 193276b680731f03b45f7fe6d86fcfe75f573466 commit + 91009a76b6a0313a1be930d77b9c79c312d0da61 blob - 0368179e9c5d6888c5404ef0219b0d559a0c3d0a blob + d554e22e149f851f331d3b11d8534de9a6713e81 --- bxhkd.c +++ bxhkd.c @@ -5,3 +5,172 @@ * See LICENSE.md and README.md for details. */ +/* bxhkd - basic X hotkey daemon */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "config.h" + +#define LENGTH(arr) (sizeof(arr) / sizeof(arr[0])) + +#define SOCKET_SUFFIX "/.local/tmp/bxwm.sock" + +static Display *dpy; +static Window root; +static volatile sig_atomic_t running = 1; + +static void grabkeys(void); +static void keypress(XKeyEvent *ev); +static void spawn(const char *cmd); +static void send_cmd(char *cmd) +static void sigchld(int sig); +static void cleanup(void); + +static void +grabkeys(void) +{ + unsigned int i, j; + KeyCode code; + unsigned int modifiers[] = { 0, LockMask, Mod2Mask, LockMask|Mod2Mask }; + + XUngrabKey(dpy, AnyKey, AnyModifier, root); + for (i = 0; i < LENGTH(keys); i++) { + code = XKeysymToKeycode(dpy, keys[i].keysym); + if (code == 0) + continue; + for (j = 0; j < LENGTH(modifiers); j++) + XGrabKey(dpy, code, keys[i].mod | modifiers[j], + root, True, GrabModeAsync, GrabModeAsync); + } +} + +static void +keypress(XKeyEvent *ev) +{ + KeySym keysym; + unsigned int i; + + keysym = XLookupKeysym(ev, 0); + for (i = 0; i < LENGTH(keys); i++) { + if (keysym == keys[i].keysym + && CLEANMASK(keys[i].mod) == CLEANMASK(ev->state)) { + spawn(keys[i].command); + return; + } + } +} + +static void +spawn(const char *cmd) +{ + char buf[256]; + char *argv[64]; + char *tok; + int argc = 0; + + strncpy(buf, cmd, sizeof(buf) - 1); + buf[sizeof(buf) - 1] = '\0'; + + tok = strtok(buf, " "); + while (tok && argc < 63) { + argv[argc++] = tok; + tok = strtok(NULL, " "); + } + argv[argc] = NULL; + + if (fork() == 0) { + if (dpy) + close(ConnectionNumber(dpy)); + setsid(); + execvp(argv[0], argv); + fprintf(stderr, "bxhkd: execvp %s failed\n", argv[0]); + _exit(EXIT_FAILURE); + } +} + +static void +mappingnotify(XMappingEvent *ev) +{ + XRefreshKeyboardMapping(ev); + if (ev->request == MappingKeyboard) + grabkeys(); +} + +static void +send_cmd(char *cmd) { + int sock; + struct sockaddr_un addr; + char *home = getenv("HOME"); + char path[256]; + + snprintf(path, sizeof(path), "%s%s", home, "/.local/tmp/bxwm.sock"); + + if ((sock = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) return; + + addr.sun_family = AF_UNIX; + strncpy(addr.sun_path, path, sizeof(addr.sun_path)-1); + + if (connect(sock, (struct sockaddr*)&addr, sizeof(addr)) != -1) { + write(sock, cmd, strlen(cmd)); + } + close(sock); +} + +static void +sigchld(int sig) +{ + (void)sig; + while (waitpid(-1, NULL, WNOHANG) > 0); +} + +static void +sig_term(int sig) +{ + (void)sig; + running = 0; +} + +static void +cleanup(void) +{ + XUngrabKey(dpy, AnyKey, AnyModifier, root); + XCloseDisplay(dpy); +} + +int +main(void) +{ + XEvent ev; + + signal(SIGCHLD, sigchld); + signal(SIGINT, sig_term); + signal(SIGTERM, sig_term); + + if (!(dpy = XOpenDisplay(NULL))) { + fprintf(stderr, "bxhkd: cannot open display\n"); + return EXIT_FAILURE; + } + root = DefaultRootWindow(dpy); + + XSelectInput(dpy, root, KeyPressMask|StructureNotifyMask); + grabkeys(); + + while (running && XNextEvent(dpy, &ev) == 0) { + if (ev.type == KeyPress) + keypress(&ev.xkey); + else if (ev.type == MappingNotify) + mappingnotify(&ev.xmapping); + } + + cleanup(); + return EXIT_SUCCESS; +} +