aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMattias Andrée <maandree@kth.se>2018-01-18 22:37:57 +0100
committerMattias Andrée <maandree@kth.se>2018-01-18 22:37:57 +0100
commit49ce929095e6f979a40593683db94ef0d404c6a1 (patch)
treea3c07d460de618c086871b446ccc4cabe93b1671
downloadxkbdbind-49ce929095e6f979a40593683db94ef0d404c6a1.tar.gz
xkbdbind-49ce929095e6f979a40593683db94ef0d404c6a1.tar.bz2
xkbdbind-49ce929095e6f979a40593683db94ef0d404c6a1.tar.xz
First commit
Signed-off-by: Mattias Andrée <maandree@kth.se>
-rw-r--r--.gitignore4
-rw-r--r--LICENSE15
-rw-r--r--Makefile17
-rw-r--r--README5
-rw-r--r--config.h6
-rw-r--r--xkbdbind.c164
6 files changed, 211 insertions, 0 deletions
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 <maandree@kth.se>
+
+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 <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;
+}