From 49ce929095e6f979a40593683db94ef0d404c6a1 Mon Sep 17 00:00:00 2001 From: Mattias Andrée Date: Thu, 18 Jan 2018 22:37:57 +0100 Subject: First commit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Mattias Andrée --- .gitignore | 4 ++ LICENSE | 15 ++++++ Makefile | 17 +++++++ README | 5 ++ config.h | 6 +++ xkbdbind.c | 164 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 211 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 Makefile create mode 100644 README create mode 100644 config.h create mode 100644 xkbdbind.c diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..66d9ded --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +*~ +\#*\# +*.o +/xkbdbind diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..b339294 --- /dev/null +++ b/LICENSE @@ -0,0 +1,15 @@ +ISC License + +© 2018 Mattias Andrée + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..6eb1f43 --- /dev/null +++ b/Makefile @@ -0,0 +1,17 @@ +.POSIX: + +CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=700 +CFLAGS = -std=c99 -Wall -Wextra -pedantic -O2 $(CPPFLAGS) +LDFLAGS = -s -lxcb -lxcb-keysyms + +all: xkbdbind + +xkbdbind: xkbdbind.c config.h + $(CC) -o $@ $@.c $(CFLAGS) $(LDFLAGS) + +clean: + -rm -f -- xkbdbind *.o + +.SUFFIXES: + +.PHONY: all clean diff --git a/README b/README new file mode 100644 index 0000000..4a09bd5 --- /dev/null +++ b/README @@ -0,0 +1,5 @@ +Depends on + + libxcb + xcb-util-keysyms + xproto diff --git a/config.h b/config.h new file mode 100644 index 0000000..4c91e07 --- /dev/null +++ b/config.h @@ -0,0 +1,6 @@ +#define MOD MOD_SUPER + +static struct hotkey hotkeys[] = { + {XK_p, MOD, 0, CMD("dmenu_run", "-fn", "fixed-8")}, + {XK_F4, MOD | MOD_SHIFT, 0, CMD("xkill")}, +}; 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 +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#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; +} -- cgit v1.2.3-70-g09d2