diff options
Diffstat (limited to 'tools')
-rw-r--r-- | tools/gsr-global-hotkeys/hotplug.c | 76 | ||||
-rw-r--r-- | tools/gsr-global-hotkeys/hotplug.h | 22 | ||||
-rw-r--r-- | tools/gsr-global-hotkeys/keyboard_event.c | 273 | ||||
-rw-r--r-- | tools/gsr-global-hotkeys/keyboard_event.h | 63 | ||||
-rw-r--r-- | tools/gsr-global-hotkeys/main.c | 264 | ||||
-rw-r--r-- | tools/gsr-window-name/main.c | 187 |
6 files changed, 464 insertions, 421 deletions
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 <string.h> + +/* POSIX */ +#include <unistd.h> +#include <sys/socket.h> + +/* LINUX */ +#include <linux/types.h> +#include <linux/netlink.h> + +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 <stdbool.h> + +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 <stdio.h> +#include <string.h> +#include <errno.h> +#include <stdbool.h> + +/* POSIX */ +#include <fcntl.h> +#include <unistd.h> +#include <dirent.h> +#include <sys/poll.h> + +/* LINUX */ +#include <linux/input.h> + +/* + 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 <stdbool.h> +#include <stdint.h> + +/* POSIX */ +#include <sys/poll.h> + +/* LINUX */ +#include <linux/input-event-codes.h> + +#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 <stdio.h> -#include <unistd.h> -#include <fcntl.h> -#include <string.h> -#include <errno.h> -#include <stdbool.h> -#include <poll.h> - -#include <libudev.h> -#include <libinput.h> -#include <libevdev/libevdev.h> -#include <xkbcommon/xkbcommon.h> +#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 <stdio.h> +#include <stdint.h> -typedef enum { - MODKEY_ALT = 1 << 0, - MODKEY_SUPER = 1 << 1, - MODKEY_CTRL = 1 << 2, - MODKEY_SHIFT = 1 << 3 -} modkeys; +/* POSIX */ +#include <unistd.h> 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; } diff --git a/tools/gsr-window-name/main.c b/tools/gsr-window-name/main.c deleted file mode 100644 index 8ebf1e0..0000000 --- a/tools/gsr-window-name/main.c +++ /dev/null @@ -1,187 +0,0 @@ -#include <X11/Xlib.h> -#include <X11/Xatom.h> -#include <X11/Xutil.h> - -#include <stdbool.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -typedef enum { - CAPTURE_TYPE_FOCUSED, - CAPTURE_TYPE_CURSOR -} capture_type; - -static bool window_has_atom(Display *dpy, Window window, Atom atom) { - Atom type; - unsigned long len, bytes_left; - int format; - unsigned char *properties = NULL; - if(XGetWindowProperty(dpy, window, atom, 0, 1024, False, AnyPropertyType, &type, &format, &len, &bytes_left, &properties) < Success) - return false; - - if(properties) - XFree(properties); - - return type != None; -} - -static bool window_is_user_program(Display *dpy, Window window) { - const Atom net_wm_state_atom = XInternAtom(dpy, "_NET_WM_STATE", False); - const Atom wm_state_atom = XInternAtom(dpy, "WM_STATE", False); - return window_has_atom(dpy, window, net_wm_state_atom) || window_has_atom(dpy, window, wm_state_atom); -} - -static Window get_window_at_cursor_position(Display *dpy) { - Window root_window = None; - Window window = None; - int dummy_i; - unsigned int dummy_u; - int cursor_pos_x = 0; - int cursor_pos_y = 0; - XQueryPointer(dpy, DefaultRootWindow(dpy), &root_window, &window, &dummy_i, &dummy_i, &cursor_pos_x, &cursor_pos_y, &dummy_u); - return window; -} - -static Window get_focused_window(Display *dpy, capture_type cap_type) { - const Atom net_active_window_atom = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", False); - Window focused_window = None; - - if(cap_type == CAPTURE_TYPE_FOCUSED) { - // Atom type = None; - // int format = 0; - // unsigned long num_items = 0; - // unsigned long bytes_left = 0; - // unsigned char *data = NULL; - // XGetWindowProperty(dpy, DefaultRootWindow(dpy), net_active_window_atom, 0, 1, False, XA_WINDOW, &type, &format, &num_items, &bytes_left, &data); - - // fprintf(stderr, "focused window: %p\n", (void*)data); - - // if(type == XA_WINDOW && num_items == 1 && data) - // return *(Window*)data; - - int revert_to = 0; - XGetInputFocus(dpy, &focused_window, &revert_to); - if(focused_window && focused_window != DefaultRootWindow(dpy) && window_is_user_program(dpy, focused_window)) - return focused_window; - } - - focused_window = get_window_at_cursor_position(dpy); - if(focused_window && focused_window != DefaultRootWindow(dpy) && window_is_user_program(dpy, focused_window)) - return focused_window; - - return None; -} - -static char* get_window_title(Display *dpy, Window window) { - const Atom net_wm_name_atom = XInternAtom(dpy, "_NET_WM_NAME", False); - const Atom wm_name_atom = XInternAtom(dpy, "_NET_WM_NAME", False); - const Atom utf8_string_atom = XInternAtom(dpy, "UTF8_STRING", False); - - Atom type = None; - int format = 0; - unsigned long num_items = 0; - unsigned long bytes_left = 0; - unsigned char *data = NULL; - XGetWindowProperty(dpy, window, net_wm_name_atom, 0, 1024, False, utf8_string_atom, &type, &format, &num_items, &bytes_left, &data); - - if(type == utf8_string_atom && format == 8 && data) - return (char*)data; - - type = None; - format = 0; - num_items = 0; - bytes_left = 0; - data = NULL; - XGetWindowProperty(dpy, window, wm_name_atom, 0, 1024, False, 0, &type, &format, &num_items, &bytes_left, &data); - - if((type == XA_STRING || type == utf8_string_atom) && data) - return (char*)data; - - return NULL; -} - -static const char* strip(const char *str, int *len) { - int str_len = strlen(str); - for(int i = 0; i < str_len; ++i) { - if(str[i] != ' ') { - str += i; - str_len -= i; - break; - } - } - - for(int i = str_len - 1; i >= 0; --i) { - if(str[i] != ' ') { - str_len = i + 1; - break; - } - } - - *len = str_len; - return str; -} - -static void print_str_strip(const char *str) { - int len = 0; - str = strip(str, &len); - printf("%.*s", len, str); -} - -static int x11_ignore_error(Display *dpy, XErrorEvent *error_event) { - (void)dpy; - (void)error_event; - return 0; -} - -static void usage(void) { - fprintf(stderr, "usage: gsr-window-name <focused|cursor>\n"); - fprintf(stderr, "options:\n"); - fprintf(stderr, " focused The class/name of the focused window is returned. If no window is focused then the window beneath the cursor is returned instead\n"); - fprintf(stderr, " cursor The class/name of the window beneath the cursor is returned\n"); - exit(1); -} - -int main(int argc, char **argv) { - if(argc != 2) - usage(); - - const char *cap_type_str = argv[1]; - capture_type cap_type = CAPTURE_TYPE_FOCUSED; - if(strcmp(cap_type_str, "focused") == 0) { - cap_type = CAPTURE_TYPE_FOCUSED; - } else if(strcmp(cap_type_str, "cursor") == 0) { - cap_type = CAPTURE_TYPE_CURSOR; - } else { - fprintf(stderr, "error: invalid option '%s', expected either 'focused' or 'cursor'\n", cap_type_str); - usage(); - } - - Display *dpy = XOpenDisplay(NULL); - if(!dpy) { - fprintf(stderr, "Error: failed to connect to the X11 server\n"); - exit(1); - } - - XSetErrorHandler(x11_ignore_error); - - const Window focused_window = get_focused_window(dpy, cap_type); - if(focused_window == None) - exit(2); - - // Window title is not always ideal (for example for a browser), but for games its pretty much required - char *window_title = get_window_title(dpy, focused_window); - if(window_title) { - print_str_strip(window_title); - exit(0); - } - - XClassHint class_hint = {0}; - XGetClassHint(dpy, focused_window, &class_hint); - if(class_hint.res_class) { - print_str_strip(class_hint.res_class); - exit(0); - } - - return 2; -} |