diff options
Diffstat (limited to '')
-rw-r--r-- | xkbdbind.c | 164 |
1 files changed, 164 insertions, 0 deletions
diff --git a/xkbdbind.c b/xkbdbind.c new file mode 100644 index 0000000..6df0520 --- /dev/null +++ b/xkbdbind.c @@ -0,0 +1,164 @@ +/* See LICENSE file for copyright and license details. */ +#include <sys/wait.h> +#include <errno.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <xcb/xcb.h> +#include <xcb/xcb_keysyms.h> +#include <xcb/xproto.h> +#include <X11/keysym.h> + +#define eprint(STR) (fprintf(stderr, "%s: %s\n", argv0, (STR)), exit(1)) + +#define _MOD_CAPS XCB_MOD_MASK_LOCK +#define _MOD_NUM XCB_MOD_MASK_2 + +#define MOD_SHIFT XCB_MOD_MASK_SHIFT +#define MOD_CTRL XCB_MOD_MASK_CONTROL +#define MOD_ALT XCB_MOD_MASK_1 +#define MOD_HYPER XCB_MOD_MASK_3 +#define MOD_SUPER XCB_MOD_MASK_4 +#define MOD_ALTGR XCB_MOD_MASK_5 + +#define CMD(...) (const char *[]){__VA_ARGS__, NULL} + +struct hotkey { + uint32_t key; /* xcb_keysym_t is uint32_t and xcb_keycode_t is uint8_t */ + uint16_t modifiers; + pid_t repeatable; + const char **command; +}; + +#include "config.h" + +static char *argv0; +static xcb_connection_t *xconn; + +static pid_t +spawn(const char **command) +{ + pid_t r; + switch ((r = fork())) { + case -1: + perror(argv0); + break; + case 0: + execvp(*command, (char *const *)command); + fprintf(stderr, "%s: %s: %s\n", argv0, *command, strerror(errno)); + exit(1); + break; + default: + break; + } + return r; +} + +static void +sigchld(int signo) +{ + pid_t r; + size_t i; + r = wait(NULL); + if (r > 0) + for (i = 0; i < sizeof(hotkeys) / sizeof(*hotkeys); i++) + if (hotkeys[i].repeatable == r) + hotkeys[i].repeatable = -1; + (void) signo; +} + +static void +setup(void) +{ +#define GRAB_KEY(KEY, MODS) (xcb_grab_key(xconn, 1, xscreen->root, (MODS), (KEY), XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC)) + + const xcb_setup_t *xsetup; + xcb_screen_iterator_t xscreen_iter; + xcb_screen_t *xscreen; + int default_xscreen; + xcb_key_symbols_t *keysyms; + size_t i; + + if (signal(SIGCHLD, sigchld) == SIG_ERR) + eprint("failed to setup handler for SIGCHLD"); + + xconn = xcb_connect(NULL, &default_xscreen); + if (!xconn || xcb_connection_has_error(xconn)) + eprint("failed to connect to display"); + if ((xsetup = xcb_get_setup(xconn)) == NULL) + eprint("failed to get list of X screens"); + xscreen_iter = xcb_setup_roots_iterator(xsetup); + if (xscreen_iter.rem <= default_xscreen) + eprint("could not find the X screen"); + for (; default_xscreen && xscreen_iter.rem; default_xscreen--) + xcb_screen_next(&xscreen_iter); + if (!xscreen_iter.rem || !(xscreen = xscreen_iter.data)) + eprint("could not find the X screen"); + + keysyms = xcb_key_symbols_alloc(xconn); + for (i = 0; i < sizeof(hotkeys) / sizeof(*hotkeys); i++) { + hotkeys[i].key = *xcb_key_symbols_get_keycode(keysyms, hotkeys[i].key); + GRAB_KEY(hotkeys[i].key, hotkeys[i].modifiers); + GRAB_KEY(hotkeys[i].key, hotkeys[i].modifiers | _MOD_CAPS); + GRAB_KEY(hotkeys[i].key, hotkeys[i].modifiers | _MOD_NUM); + GRAB_KEY(hotkeys[i].key, hotkeys[i].modifiers | _MOD_CAPS | _MOD_NUM); + if (hotkeys[i].repeatable) + hotkeys[i].repeatable = -1; + } + xcb_key_symbols_free(keysyms); + + xcb_flush(xconn); +} + +int +main(int argc, char *argv[]) +{ + xcb_generic_event_t *e, *next_e = NULL; + xcb_keycode_t key; + uint16_t mods; + int suppress = 0; + size_t i; + + argv0 = argv[0]; + if (argc > 1) { + fprintf(stderr, "usage: %s\n", argv0); + return 1; + } + + setup(); + + while ((e = next_e ? next_e : xcb_wait_for_event(xconn))) { + next_e = xcb_poll_for_event(xconn); + if (suppress > 0) { + suppress = 0; + continue; + } + switch (e->response_type & ~0x80) { + case XCB_KEY_RELEASE: + if (suppress >= 0 && next_e && (next_e->response_type & ~0x80) == XCB_KEY_PRESS && + ((xcb_key_press_event_t *)next_e)->time == ((xcb_key_press_event_t *)e)->time) + suppress = 1; + break; + case XCB_KEY_PRESS: + key = ((xcb_key_press_event_t *)e)->detail; + mods = ((xcb_key_press_event_t *)e)->state & ~(_MOD_CAPS | _MOD_NUM); + for (i = 0; i < sizeof(hotkeys) / sizeof(*hotkeys); i++) { + if (key == hotkeys[i].key && mods == hotkeys[i].modifiers) { + suppress = hotkeys[i].repeatable ? -1 : 0; + if (hotkeys[i].repeatable == -1) + hotkeys[i].repeatable = spawn(hotkeys[i].command); + else if (!hotkeys[i].repeatable) + spawn(hotkeys[i].command); + break; + } + } + break; + } + } + + perror(argv0); + return 1; +} |