From ebd8c2726b8caac6adc00cf15c5631e51d05ba1f Mon Sep 17 00:00:00 2001 From: dec05eba Date: Mon, 16 Dec 2024 02:21:38 +0100 Subject: Rewrite linux global hotkey to not depend on any libraries (also allows it to work on non-systemd systems(?)), remove unused gsr-window-name --- tools/gsr-global-hotkeys/hotplug.c | 76 +++++++++ tools/gsr-global-hotkeys/hotplug.h | 22 +++ tools/gsr-global-hotkeys/keyboard_event.c | 273 ++++++++++++++++++++++++++++++ tools/gsr-global-hotkeys/keyboard_event.h | 63 +++++++ tools/gsr-global-hotkeys/main.c | 264 ++++------------------------- 5 files changed, 464 insertions(+), 234 deletions(-) create mode 100644 tools/gsr-global-hotkeys/hotplug.c create mode 100644 tools/gsr-global-hotkeys/hotplug.h create mode 100644 tools/gsr-global-hotkeys/keyboard_event.c create mode 100644 tools/gsr-global-hotkeys/keyboard_event.h (limited to 'tools/gsr-global-hotkeys') diff --git a/tools/gsr-global-hotkeys/hotplug.c b/tools/gsr-global-hotkeys/hotplug.c new file mode 100644 index 0000000..6c82929 --- /dev/null +++ b/tools/gsr-global-hotkeys/hotplug.c @@ -0,0 +1,76 @@ +#include "hotplug.h" + +/* C stdlib */ +#include + +/* POSIX */ +#include +#include + +/* LINUX */ +#include +#include + +bool hotplug_event_init(hotplug_event *self) { + memset(self, 0, sizeof(*self)); + + struct sockaddr_nl nls = { + .nl_family = AF_NETLINK, + .nl_pid = getpid(), + .nl_groups = -1 + }; + + const int fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT); + if(fd == -1) + return false; /* Not root user */ + + if(bind(fd, (void *)&nls, sizeof(struct sockaddr_nl))) { + close(fd); + return false; + } + + self->fd = fd; + return true; +} + +void hotplug_event_deinit(hotplug_event *self) { + if(self->fd > 0) { + close(self->fd); + self->fd = -1; + } +} + +int hotplug_event_steal_fd(hotplug_event *self) { + const int fd = self->fd; + self->fd = -1; + return fd; +} + +/* TODO: This assumes SUBSYSTEM= is output before DEVNAME=, is that always true? */ +static void hotplug_event_parse_netlink_data(hotplug_event *self, const char *line, hotplug_device_added_callback callback, void *userdata) { + const char *at_symbol = strchr(line, '@'); + if(at_symbol) { + self->event_is_add = strncmp(line, "add@", 4) == 0; + self->subsystem_is_input = false; + } else if(self->event_is_add) { + if(strcmp(line, "SUBSYSTEM=input") == 0) + self->subsystem_is_input = true; + + if(self->subsystem_is_input && strncmp(line, "DEVNAME=", 8) == 0) + callback(line+8, userdata); + } +} + +/* Netlink uevent structure is documented here: https://web.archive.org/web/20160127215232/https://www.kernel.org/doc/pending/hotplug.txt */ +void hotplug_event_process_event_data(hotplug_event *self, int fd, hotplug_device_added_callback callback, void *userdata) { + const int bytes_read = read(fd, self->event_data, sizeof(self->event_data)); + int data_index = 0; + if(bytes_read <= 0) + return; + + /* Hotplug data ends with a newline and a null terminator */ + while(data_index < bytes_read) { + hotplug_event_parse_netlink_data(self, self->event_data + data_index, callback, userdata); + data_index += strlen(self->event_data + data_index) + 1; /* Skip null terminator as well */ + } +} diff --git a/tools/gsr-global-hotkeys/hotplug.h b/tools/gsr-global-hotkeys/hotplug.h new file mode 100644 index 0000000..665485a --- /dev/null +++ b/tools/gsr-global-hotkeys/hotplug.h @@ -0,0 +1,22 @@ +#ifndef HOTPLUG_H +#define HOTPLUG_H + +/* C stdlib */ +#include + +typedef struct { + int fd; + bool event_is_add; + bool subsystem_is_input; + char event_data[1024]; +} hotplug_event; + +typedef void (*hotplug_device_added_callback)(const char *devname, void *userdata); + +bool hotplug_event_init(hotplug_event *self); +void hotplug_event_deinit(hotplug_event *self); + +int hotplug_event_steal_fd(hotplug_event *self); +void hotplug_event_process_event_data(hotplug_event *self, int fd, hotplug_device_added_callback callback, void *userdata); + +#endif /* HOTPLUG_H */ diff --git a/tools/gsr-global-hotkeys/keyboard_event.c b/tools/gsr-global-hotkeys/keyboard_event.c new file mode 100644 index 0000000..9cc518a --- /dev/null +++ b/tools/gsr-global-hotkeys/keyboard_event.c @@ -0,0 +1,273 @@ +#include "keyboard_event.h" + +/* C stdlib */ +#include +#include +#include +#include + +/* POSIX */ +#include +#include +#include +#include + +/* LINUX */ +#include + +/* + We could get initial keyboard state with: + unsigned char key_states[KEY_MAX/8 + 1]; + ioctl(fd, EVIOCGKEY(sizeof(key_states)), key_states), but ignore that for now +*/ + +static void keyboard_event_process_input_event_data(keyboard_event *self, int fd, key_callback callback, void *userdata) { + struct input_event event; + if(read(fd, &event, sizeof(event)) != sizeof(event)) { + fprintf(stderr, "Error: failed to read input event data\n"); + return; + } + + // value = 1 == key pressed + //if(event.type == EV_KEY && event.code == KEY_A && event.value == 1) { + //fprintf(stderr, "fd: %d, type: %d, pressed %d, value: %d\n", fd, event.type, event.code, event.value); + //} + + if(event.type == EV_KEY) { + switch(event.code) { + case KEY_LEFTSHIFT: + self->lshift_button_state = event.value >= 1 ? KEYBOARD_BUTTON_PRESSED : KEYBOARD_BUTTON_RELEASED; + break; + case KEY_RIGHTSHIFT: + self->rshift_button_state = event.value >= 1 ? KEYBOARD_BUTTON_PRESSED : KEYBOARD_BUTTON_RELEASED; + break; + case KEY_LEFTCTRL: + self->lctrl_button_state = event.value >= 1 ? KEYBOARD_BUTTON_PRESSED : KEYBOARD_BUTTON_RELEASED; + break; + case KEY_RIGHTCTRL: + self->rctrl_button_state = event.value >= 1 ? KEYBOARD_BUTTON_PRESSED : KEYBOARD_BUTTON_RELEASED; + break; + case KEY_LEFTALT: + self->lalt_button_state = event.value >= 1 ? KEYBOARD_BUTTON_PRESSED : KEYBOARD_BUTTON_RELEASED; + break; + case KEY_RIGHTALT: + self->ralt_button_state = event.value >= 1 ? KEYBOARD_BUTTON_PRESSED : KEYBOARD_BUTTON_RELEASED; + break; + case KEY_LEFTMETA: + self->lmeta_button_state = event.value >= 1 ? KEYBOARD_BUTTON_PRESSED : KEYBOARD_BUTTON_RELEASED; + break; + case KEY_RIGHTMETA: + self->rmeta_button_state = event.value >= 1 ? KEYBOARD_BUTTON_PRESSED : KEYBOARD_BUTTON_RELEASED; + break; + default: { + const bool shift_pressed = self->lshift_button_state == KEYBOARD_BUTTON_PRESSED || self->rshift_button_state == KEYBOARD_BUTTON_PRESSED; + const bool ctrl_pressed = self->lctrl_button_state == KEYBOARD_BUTTON_PRESSED || self->rctrl_button_state == KEYBOARD_BUTTON_PRESSED; + const bool alt_pressed = self->lalt_button_state == KEYBOARD_BUTTON_PRESSED || self->ralt_button_state == KEYBOARD_BUTTON_PRESSED; + const bool meta_pressed = self->lmeta_button_state == KEYBOARD_BUTTON_PRESSED || self->rmeta_button_state == KEYBOARD_BUTTON_PRESSED; + //fprintf(stderr, "pressed key: %d, state: %d, shift: %s, ctrl: %s, alt: %s, meta: %s\n", event.code, event.value, + // shift_pressed ? "yes" : "no", ctrl_pressed ? "yes" : "no", alt_pressed ? "yes" : "no", meta_pressed ? "yes" : "no"); + uint32_t modifiers = 0; + if(shift_pressed) + modifiers |= KEYBOARD_MODKEY_SHIFT; + if(ctrl_pressed) + modifiers |= KEYBOARD_MODKEY_CTRL; + if(alt_pressed) + modifiers |= KEYBOARD_MODKEY_ALT; + if(meta_pressed) + modifiers |= KEYBOARD_MODKEY_SUPER; + + callback(event.code, modifiers, event.value, userdata); + break; + } + } + } +} + +/* Returns -1 if invalid format. Expected |dev_input_filepath| to be in format /dev/input/eventN */ +static int get_dev_input_id_from_filepath(const char *dev_input_filepath) { + if(strncmp(dev_input_filepath, "/dev/input/event", 16) != 0) + return -1; + + int dev_input_id = -1; + if(sscanf(dev_input_filepath + 16, "%d", &dev_input_id) == 1) + return dev_input_id; + return -1; +} + +static bool keyboard_event_has_event_with_dev_input_fd(keyboard_event *self, int dev_input_id) { + for(int i = 0; i < self->num_event_polls; ++i) { + if(self->dev_input_ids[i] == dev_input_id) + return true; + } + return false; +} + +static bool keyboard_event_try_add_device_if_keyboard(keyboard_event *self, const char *dev_input_filepath) { + const int dev_input_id = get_dev_input_id_from_filepath(dev_input_filepath); + if(dev_input_id == -1) + return false; + + if(keyboard_event_has_event_with_dev_input_fd(self, dev_input_id)) + return false; + + const int fd = open(dev_input_filepath, O_RDONLY); + if(fd == -1) + return false; + + char device_name[256]; + device_name[0] = '\0'; + ioctl(fd, EVIOCGNAME(sizeof(device_name)), device_name); + + unsigned long evbit = 0; + ioctl(fd, EVIOCGBIT(0, sizeof(evbit)), &evbit); + if(evbit & (1 << EV_KEY)) { + unsigned char key_bits[KEY_MAX/8 + 1] = {0}; + ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(key_bits)), &key_bits); + + /* Test if device supports KEY_A since not all devices that support EV_KEY are keyboards, for example even a power button is an EV_KEY */ + const int key_test = KEY_A; + const bool supports_key_events = key_bits[key_test/8] & (1 << (key_test % 8)); + if(supports_key_events) { + if(self->num_event_polls < MAX_EVENT_POLLS) { + //fprintf(stderr, "%s (%s) supports key inputs\n", dev_input_filepath, device_name); + self->event_polls[self->num_event_polls] = (struct pollfd) { + .fd = fd, + .events = POLLIN, + .revents = 0 + }; + self->dev_input_ids[self->num_event_polls] = dev_input_id; + + ++self->num_event_polls; + return true; + } else { + fprintf(stderr, "Warning: the maximum number of keyboard devices have been registered. The newly added keyboard will be ignored\n"); + } + } + } + + close(fd); + return false; +} + +static bool keyboard_event_add_dev_input_devices(keyboard_event *self) { + DIR *dir = opendir("/dev/input"); + if(!dir) { + fprintf(stderr, "error: failed to open /dev/input, error: %s\n", strerror(errno)); + return false; + } + + char dev_input_filepath[1024]; + for(;;) { + struct dirent *entry = readdir(dir); + if(!entry) + break; + + if(strncmp(entry->d_name, "event", 5) != 0) + continue; + + snprintf(dev_input_filepath, sizeof(dev_input_filepath), "/dev/input/%s", entry->d_name); + keyboard_event_try_add_device_if_keyboard(self, dev_input_filepath); + } + + closedir(dir); + return true; +} + +static void keyboard_event_remove_event(keyboard_event *self, int index) { + if(index < 0 || index >= self->num_event_polls) + return; + + close(self->event_polls[index].fd); + for(int j = index + 1; j < self->num_event_polls; ++j) { + self->event_polls[j - 1] = self->event_polls[j]; + self->dev_input_ids[j - 1] = self->dev_input_ids[j]; + } + --self->num_event_polls; +} + +bool keyboard_event_init(keyboard_event *self, bool poll_stdout_error) { + memset(self, 0, sizeof(*self)); + self->stdout_event_index = -1; + self->hotplug_event_index = -1; + + if(poll_stdout_error) { + self->event_polls[self->num_event_polls] = (struct pollfd) { + .fd = STDOUT_FILENO, + .events = 0, + .revents = 0 + }; + self->dev_input_ids[self->num_event_polls] = -1; + + self->stdout_event_index = self->num_event_polls; + ++self->num_event_polls; + } + + if(hotplug_event_init(&self->hotplug_ev)) { + self->event_polls[self->num_event_polls] = (struct pollfd) { + .fd = hotplug_event_steal_fd(&self->hotplug_ev), + .events = POLLIN, + .revents = 0 + }; + self->dev_input_ids[self->num_event_polls] = -1; + + self->hotplug_event_index = self->num_event_polls; + ++self->num_event_polls; + } else { + fprintf(stderr, "Warning: failed to setup hotplugging\n"); + } + + keyboard_event_add_dev_input_devices(self); + + /* Neither hotplugging nor any keyboard devices were found. We will never listen to keyboard events so might as well fail */ + if(self->num_event_polls == 0) { + keyboard_event_deinit(self); + return false; + } + + return true; +} + +void keyboard_event_deinit(keyboard_event *self) { + for(int i = 0; i < self->num_event_polls; ++i) { + close(self->event_polls[i].fd); + } + self->num_event_polls = 0; + + hotplug_event_deinit(&self->hotplug_ev); +} + +static void on_device_added_callback(const char *devname, void *userdata) { + keyboard_event *keyboard_ev = userdata; + char dev_input_filepath[1024]; + snprintf(dev_input_filepath, sizeof(dev_input_filepath), "/dev/%s", devname); + keyboard_event_try_add_device_if_keyboard(keyboard_ev, dev_input_filepath); +} + +void keyboard_event_poll_events(keyboard_event *self, int timeout_milliseconds, key_callback callback, void *userdata) { + if(poll(self->event_polls, self->num_event_polls, timeout_milliseconds) <= 0) + return; + + for(int i = 0; i < self->num_event_polls; ++i) { + if(i == self->stdout_event_index && self->event_polls[i].revents & (POLLHUP|POLLERR)) + self->stdout_failed = true; + + if(self->event_polls[i].revents & POLLHUP) { /* TODO: What if this is the hotplug fd? */ + keyboard_event_remove_event(self, i); + --i; /* Repeat same index since the current element has been removed */ + continue; + } + + if(!(self->event_polls[i].revents & POLLIN)) + continue; + + if(i == self->hotplug_event_index) + /* Device is added to end of |event_polls| so it's ok to add while iterating it via index */ + hotplug_event_process_event_data(&self->hotplug_ev, self->event_polls[i].fd, on_device_added_callback, self); + else + keyboard_event_process_input_event_data(self, self->event_polls[i].fd, callback, userdata); + } +} + +bool keyboard_event_stdout_has_failed(keyboard_event *self) { + return self->stdout_failed; +} diff --git a/tools/gsr-global-hotkeys/keyboard_event.h b/tools/gsr-global-hotkeys/keyboard_event.h new file mode 100644 index 0000000..5084659 --- /dev/null +++ b/tools/gsr-global-hotkeys/keyboard_event.h @@ -0,0 +1,63 @@ +#ifndef KEYBOARD_EVENT_H +#define KEYBOARD_EVENT_H + +/* Read keyboard input from linux /dev/input/eventN devices, with hotplug support */ + +#include "hotplug.h" + +/* C stdlib */ +#include +#include + +/* POSIX */ +#include + +/* LINUX */ +#include + +#define MAX_EVENT_POLLS 32 + +typedef enum { + KEYBOARD_MODKEY_ALT = 1 << 0, + KEYBOARD_MODKEY_SUPER = 1 << 1, + KEYBOARD_MODKEY_CTRL = 1 << 2, + KEYBOARD_MODKEY_SHIFT = 1 << 3 +} keyboard_modkeys; + +typedef enum { + KEYBOARD_BUTTON_RELEASED, + KEYBOARD_BUTTON_PRESSED +} keyboard_button_state; + +typedef struct { + struct pollfd event_polls[MAX_EVENT_POLLS]; /* Current size is |num_event_polls| */ + int dev_input_ids[MAX_EVENT_POLLS]; /* Current size is |num_event_polls| */ + int num_event_polls; + + int stdout_event_index; + int hotplug_event_index; + bool stdout_failed; + + hotplug_event hotplug_ev; + + keyboard_button_state lshift_button_state; + keyboard_button_state rshift_button_state; + keyboard_button_state lctrl_button_state; + keyboard_button_state rctrl_button_state; + keyboard_button_state lalt_button_state; + keyboard_button_state ralt_button_state; + keyboard_button_state lmeta_button_state; + keyboard_button_state rmeta_button_state; +} keyboard_event; + +/* |key| is a KEY_ from linux/input-event-codes.h. |modifiers| is a bitmask of keyboard_modkeys. |press_status| is 0 for released, 1 for pressed and 2 for repeat */ +typedef void (*key_callback)(uint32_t key, uint32_t modifiers, int press_status, void *userdata); + +bool keyboard_event_init(keyboard_event *self, bool poll_stdout_error); +void keyboard_event_deinit(keyboard_event *self); + +/* If |timeout_milliseconds| is -1 then wait until an event is received */ +void keyboard_event_poll_events(keyboard_event *self, int timeout_milliseconds, key_callback callback, void *userdata); +bool keyboard_event_stdout_has_failed(keyboard_event *self); + +#endif /* KEYBOARD_EVENT_H */ diff --git a/tools/gsr-global-hotkeys/main.c b/tools/gsr-global-hotkeys/main.c index 2823487..1be7f2d 100644 --- a/tools/gsr-global-hotkeys/main.c +++ b/tools/gsr-global-hotkeys/main.c @@ -1,28 +1,11 @@ -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include +#include "keyboard_event.h" -typedef struct { - struct xkb_context *xkb_context; - struct xkb_keymap *xkb_keymap; - struct xkb_state *xkb_state; -} key_mapper; +/* C stdlib */ +#include +#include -typedef enum { - MODKEY_ALT = 1 << 0, - MODKEY_SUPER = 1 << 1, - MODKEY_CTRL = 1 << 2, - MODKEY_SHIFT = 1 << 3 -} modkeys; +/* POSIX */ +#include typedef struct { uint32_t key; @@ -32,186 +15,28 @@ typedef struct { #define NUM_GLOBAL_HOTKEYS 6 static global_hotkey global_hotkeys[NUM_GLOBAL_HOTKEYS] = { - { .key = XKB_KEY_z, .modifiers = MODKEY_ALT, .action = "show_hide" }, - { .key = XKB_KEY_F9, .modifiers = MODKEY_ALT, .action = "record" }, - { .key = XKB_KEY_F7, .modifiers = MODKEY_ALT, .action = "pause" }, - { .key = XKB_KEY_F8, .modifiers = MODKEY_ALT, .action = "stream" }, - { .key = XKB_KEY_F10, .modifiers = MODKEY_ALT | MODKEY_SHIFT, .action = "replay_start" }, - { .key = XKB_KEY_F10, .modifiers = MODKEY_ALT, .action = "replay_save" } + { .key = KEY_Z, .modifiers = KEYBOARD_MODKEY_ALT, .action = "show_hide" }, + { .key = KEY_F9, .modifiers = KEYBOARD_MODKEY_ALT, .action = "record" }, + { .key = KEY_F7, .modifiers = KEYBOARD_MODKEY_ALT, .action = "pause" }, + { .key = KEY_F8, .modifiers = KEYBOARD_MODKEY_ALT, .action = "stream" }, + { .key = KEY_F10, .modifiers = KEYBOARD_MODKEY_ALT | KEYBOARD_MODKEY_SHIFT, .action = "replay_start" }, + { .key = KEY_F10, .modifiers = KEYBOARD_MODKEY_ALT, .action = "replay_save" } }; -static int open_restricted(const char *path, int flags, void *user_data) { - (void)user_data; - int fd = open(path, flags); - if(fd < 0) - fprintf(stderr, "error: failed to open %s, error: %s\n", path, strerror(errno)); - return fd < 0 ? -errno : fd; -} - -static void close_restricted(int fd, void *user_data) { - (void)user_data; - close(fd); -} - -static const struct libinput_interface interface = { - .open_restricted = open_restricted, - .close_restricted = close_restricted, -}; - -static bool is_mod_key(xkb_keycode_t xkb_key_code) { - return xkb_key_code >= XKB_KEY_Shift_L && xkb_key_code <= XKB_KEY_Hyper_R; -} - -typedef struct { - const char *modname; - modkeys key; -} modname_to_modkey_map; - -static uint32_t xkb_state_to_modifiers(struct xkb_state *xkb_state) { - const modname_to_modkey_map modifier_keys[] = { - { .modname = XKB_MOD_NAME_ALT, .key = MODKEY_ALT }, - { .modname = XKB_MOD_NAME_LOGO, .key = MODKEY_SUPER }, - { .modname = XKB_MOD_NAME_SHIFT, .key = MODKEY_SHIFT }, - { .modname = XKB_MOD_NAME_CTRL, .key = MODKEY_CTRL } - }; - - uint32_t modifiers = 0; - for(int i = 0; i < 4; ++i) { - if(xkb_state_mod_name_is_active(xkb_state, modifier_keys[i].modname, XKB_STATE_MODS_EFFECTIVE) > 0) - modifiers |= modifier_keys[i].key; - } - return modifiers; -} - -#define KEY_CODE_EV_TO_XKB(key) ((key) + 8) - -static int print_key_event(struct libinput_event *event, key_mapper *mapper) { - struct libinput_event_keyboard *keyboard = libinput_event_get_keyboard_event(event); - const uint32_t key_code = libinput_event_keyboard_get_key(keyboard); - enum libinput_key_state state_code = libinput_event_keyboard_get_key_state(keyboard); - - const xkb_keycode_t xkb_key_code = KEY_CODE_EV_TO_XKB(key_code); - xkb_state_update_key(mapper->xkb_state, xkb_key_code, state_code == LIBINPUT_KEY_STATE_PRESSED ? XKB_KEY_DOWN : XKB_KEY_UP); - xkb_keysym_t xkb_key_sym = xkb_state_key_get_one_sym(mapper->xkb_state, xkb_key_code); - // char main_key[128]; - // main_key[0] = '\0'; +static void on_key_callback(uint32_t key, uint32_t modifiers, int press_status, void *userdata) { + (void)userdata; + if(press_status != 1) /* 1 == Pressed */ + return; - // if(xkb_state_mod_name_is_active(mapper->xkb_state, XKB_MOD_NAME_LOGO, XKB_STATE_MODS_EFFECTIVE) > 0) - // strcat(main_key, "Super+"); - // if(xkb_state_mod_name_is_active(mapper->xkb_state, XKB_MOD_NAME_CTRL, XKB_STATE_MODS_EFFECTIVE) > 0) - // strcat(main_key, "Ctrl+"); - // if(xkb_state_mod_name_is_active(mapper->xkb_state, XKB_MOD_NAME_ALT, XKB_STATE_MODS_EFFECTIVE) > 0 && strcmp(main_key, "Meta") != 0) - // strcat(main_key, "Alt+"); - // if(xkb_state_mod_name_is_active(mapper->xkb_state, XKB_MOD_NAME_SHIFT, XKB_STATE_MODS_EFFECTIVE) > 0) - // strcat(main_key, "Shift+"); - - // if(!is_mod_key(xkb_key_sym)) { - // char reg_key[64]; - // reg_key[0] = '\0'; - // xkb_keysym_get_name(xkb_key_sym, reg_key, sizeof(reg_key)); - // strcat(main_key, reg_key); - // } - - if(state_code != LIBINPUT_KEY_STATE_PRESSED) - return 0; - - const uint32_t current_modifiers = xkb_state_to_modifiers(mapper->xkb_state); for(int i = 0; i < NUM_GLOBAL_HOTKEYS; ++i) { - if(xkb_key_sym == global_hotkeys[i].key && current_modifiers == global_hotkeys[i].modifiers) { + if(key == global_hotkeys[i].key && modifiers == global_hotkeys[i].modifiers) { puts(global_hotkeys[i].action); fflush(stdout); - break; } } - - return 0; -} - -static int handle_events(struct libinput *libinput, key_mapper *mapper) { - int result = -1; - struct libinput_event *event; - - if(libinput_dispatch(libinput) < 0) - return result; - - while((event = libinput_get_event(libinput)) != NULL) { - if(libinput_event_get_type(event) == LIBINPUT_EVENT_KEYBOARD_KEY) - print_key_event(event, mapper); - - libinput_event_destroy(event); - result = 0; - } - - return result; -} - -static int run_mainloop(struct libinput *libinput, key_mapper *mapper) { - struct pollfd fds[2] = { - { - .fd = libinput_get_fd(libinput), - .events = POLLIN, - .revents = 0 - }, - { - .fd = STDOUT_FILENO, - .events = 0, - .revents = 0 - } - }; - - if(handle_events(libinput, mapper) != 0) { - fprintf(stderr, "error: didn't receive device added events. Is this program not running as root?\n"); - return -1; - } - - while(poll(fds, 2, -1) >= 0) { - if(fds[0].revents & POLLIN) - handle_events(libinput, mapper); - if(fds[1].revents & (POLLHUP|POLLERR)) - break; - } - - return 0; -} - -static bool mapper_refresh_keymap(key_mapper *mapper) { - if(mapper->xkb_keymap != NULL) { - xkb_keymap_unref(mapper->xkb_keymap); - mapper->xkb_keymap = NULL; - } - - // TODO: - struct xkb_rule_names names = { - NULL, NULL, - NULL,//keymap_is_default(mapper->layout) ? NULL : mapper->layout, - NULL,//keymap_is_default(mapper->variant) ? NULL : mapper->variant, - NULL - }; - mapper->xkb_keymap = xkb_keymap_new_from_names(mapper->xkb_context, &names, XKB_KEYMAP_COMPILE_NO_FLAGS); - if(mapper->xkb_keymap == NULL) { - fprintf(stderr, "error: failed to create XKB keymap.\n"); - return false; - } - - if(mapper->xkb_state != NULL) { - xkb_state_unref(mapper->xkb_state); - mapper->xkb_state = NULL; - } - - mapper->xkb_state = xkb_state_new(mapper->xkb_keymap); - if(mapper->xkb_state == NULL) { - fprintf(stderr, "error: failed to create XKB state.\n"); - return false; - } - - return true; } int main(void) { - int result = 0; - struct udev *udev = NULL; - struct libinput *libinput = NULL; - const uid_t user_id = getuid(); if(geteuid() != 0) { if(setuid(0) == -1) { @@ -220,53 +45,24 @@ int main(void) { } } - udev = udev_new(); - if(!udev) { - fprintf(stderr, "error: udev_new failed\n"); - result = 1; - goto done; - } - - libinput = libinput_udev_create_context(&interface, NULL, udev); - if(!libinput) { - fprintf(stderr, "error: libinput_udev_create_context failed\n"); - result = 1; - goto done; - } - - if(libinput_udev_assign_seat(libinput, "seat0") != 0) { - fprintf(stderr, "error: libinput_udev_assign_seat with seat0 failed\n"); - result = 1; - goto done; + keyboard_event keyboard_ev; + if(!keyboard_event_init(&keyboard_ev, true)) { + fprintf(stderr, "Error: failed to setup hotplugging and no keyboard input devices were found\n"); + setuid(user_id); + return 1; } - key_mapper mapper; - mapper.xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); - if(!mapper.xkb_context) { - fprintf(stderr, "error: xkb_context_new failed\n"); - result = 1; - goto done; - } + for(;;) { + keyboard_event_poll_events(&keyboard_ev, -1, on_key_callback, NULL); + if(keyboard_event_stdout_has_failed(&keyboard_ev)) { + fprintf(stderr, "Info: stdout closed (parent process likely closed this process), exiting...\n"); + break; + } - if(!mapper_refresh_keymap(&mapper)) { - fprintf(stderr, "error: key mapper failed\n"); - result = 1; - goto done; - } - if(run_mainloop(libinput, &mapper) < 0) { - fprintf(stderr, "error: failed to start main loop\n"); - result = 1; - goto done; } - done: - if(libinput) - libinput_unref(libinput); - - if(udev) - udev_unref(udev); - + keyboard_event_deinit(&keyboard_ev); setuid(user_id); - return result; + return 0; } -- cgit v1.2.3