diff options
Diffstat (limited to 'tools')
-rw-r--r-- | tools/gsr-global-hotkeys/hotplug.c | 2 | ||||
-rw-r--r-- | tools/gsr-global-hotkeys/keyboard_event.c | 112 | ||||
-rw-r--r-- | tools/gsr-global-hotkeys/keyboard_event.h | 17 | ||||
-rw-r--r-- | tools/gsr-global-hotkeys/main.c | 15 |
4 files changed, 123 insertions, 23 deletions
diff --git a/tools/gsr-global-hotkeys/hotplug.c b/tools/gsr-global-hotkeys/hotplug.c index 6c82929..ba3ef9c 100644 --- a/tools/gsr-global-hotkeys/hotplug.c +++ b/tools/gsr-global-hotkeys/hotplug.c @@ -24,7 +24,7 @@ bool hotplug_event_init(hotplug_event *self) { if(fd == -1) return false; /* Not root user */ - if(bind(fd, (void *)&nls, sizeof(struct sockaddr_nl))) { + if(bind(fd, (void*)&nls, sizeof(struct sockaddr_nl))) { close(fd); return false; } diff --git a/tools/gsr-global-hotkeys/keyboard_event.c b/tools/gsr-global-hotkeys/keyboard_event.c index 9cc518a..86b7248 100644 --- a/tools/gsr-global-hotkeys/keyboard_event.c +++ b/tools/gsr-global-hotkeys/keyboard_event.c @@ -14,6 +14,9 @@ /* LINUX */ #include <linux/input.h> +#include <linux/uinput.h> + +#define GSR_UI_VIRTUAL_KEYBOARD_NAME "gsr-ui virtual keyboard" /* We could get initial keyboard state with: @@ -21,7 +24,11 @@ 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) { +static bool keyboard_event_has_exclusive_grab(const keyboard_event *self) { + return self->uinput_fd > 0; +} + +static void keyboard_event_process_input_event_data(keyboard_event *self, const event_extra_data *extra_data, 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"); @@ -76,11 +83,19 @@ static void keyboard_event_process_input_event_data(keyboard_event *self, int fd if(meta_pressed) modifiers |= KEYBOARD_MODKEY_SUPER; - callback(event.code, modifiers, event.value, userdata); + if(!callback(event.code, modifiers, event.value, userdata)) + return; + break; } } } + + if(keyboard_event_has_exclusive_grab(self) && extra_data->grabbed) { + /* TODO: Error check? */ + if(write(self->uinput_fd, &event, sizeof(event)) != sizeof(event)) + fprintf(stderr, "Error: failed to write event data to virtual keyboard for exclusively grabbed device\n"); + } } /* Returns -1 if invalid format. Expected |dev_input_filepath| to be in format /dev/input/eventN */ @@ -96,7 +111,7 @@ static int get_dev_input_id_from_filepath(const char *dev_input_filepath) { 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) + if(self->event_extra_data[i].dev_input_id == dev_input_id) return true; } return false; @@ -120,7 +135,7 @@ static bool keyboard_event_try_add_device_if_keyboard(keyboard_event *self, cons unsigned long evbit = 0; ioctl(fd, EVIOCGBIT(0, sizeof(evbit)), &evbit); - if(evbit & (1 << EV_KEY)) { + if(strcmp(device_name, GSR_UI_VIRTUAL_KEYBOARD_NAME) != 0 && (evbit & (1 << EV_KEY))) { unsigned char key_bits[KEY_MAX/8 + 1] = {0}; ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(key_bits)), &key_bits); @@ -129,13 +144,24 @@ static bool keyboard_event_try_add_device_if_keyboard(keyboard_event *self, cons 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) { + bool grabbed = false; + if(keyboard_event_has_exclusive_grab(self)) { + grabbed = ioctl(fd, EVIOCGRAB, 1) != -1; + if(!grabbed) + fprintf(stderr, "Warning: failed to exclusively grab device %s. The focused application may receive keys used for global hotkeys\n", device_name); + } + //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->event_extra_data[self->num_event_polls] = (event_extra_data) { + .dev_input_id = dev_input_id, + .grabbed = grabbed + }; ++self->num_event_polls; return true; @@ -180,23 +206,79 @@ static void keyboard_event_remove_event(keyboard_event *self, int index) { 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->event_extra_data[j - 1] = self->event_extra_data[j]; } --self->num_event_polls; } -bool keyboard_event_init(keyboard_event *self, bool poll_stdout_error) { +/* Returns the fd to the uinput */ +static int setup_virtual_keyboard_input(const char *name) { + /* TODO: O_NONBLOCK? */ + int fd = open("/dev/uinput", O_WRONLY); + if(fd == -1) { + fd = open("/dev/input/uinput", O_WRONLY); + if(fd == -1) { + fprintf(stderr, "Warning: failed to setup virtual device for exclusive grab (failed to open /dev/uinput or /dev/input/uinput), error: %s\n", strerror(errno)); + return -1; + } + } + + bool success = true; + success &= (ioctl(fd, UI_SET_EVBIT, EV_KEY) != -1); + for(int i = 1; i < KEY_MAX; ++i) { + success &= (ioctl(fd, UI_SET_KEYBIT, i) != -1); + } + + int ui_version = 0; + success &= (ioctl(fd, UI_GET_VERSION, &ui_version) != -1); + + if(ui_version >= 5) { + struct uinput_setup usetup; + memset(&usetup, 0, sizeof(usetup)); + usetup.id.bustype = BUS_USB; + usetup.id.vendor = 0xdec0; + usetup.id.product = 0x5eba; + snprintf(usetup.name, sizeof(usetup.name), "%s", name); + success &= (ioctl(fd, UI_DEV_SETUP, &usetup) != -1); + } else { + struct uinput_user_dev uud; + memset(&uud, 0, sizeof(uud)); + snprintf(uud.name, UINPUT_MAX_NAME_SIZE, "%s", name); + if(write(fd, &uud, sizeof(uud)) != sizeof(uud)) + success = false; + } + + success &= (ioctl(fd, UI_DEV_CREATE) != -1); + if(!success) { + close(fd); + return -1; + } + + return fd; +} + +bool keyboard_event_init(keyboard_event *self, bool poll_stdout_error, bool exclusive_grab) { memset(self, 0, sizeof(*self)); self->stdout_event_index = -1; self->hotplug_event_index = -1; + if(exclusive_grab) { + // TODO: If this fails, try /dev/input/uinput instead + self->uinput_fd = setup_virtual_keyboard_input(GSR_UI_VIRTUAL_KEYBOARD_NAME); + if(self->uinput_fd <= 0) + fprintf(stderr, "Warning: failed to setup virtual keyboard input for exclusive grab. The focused application will receive keys used for global hotkeys\n"); + } + 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->event_extra_data[self->num_event_polls] = (event_extra_data) { + .dev_input_id = -1, + .grabbed = false + }; self->stdout_event_index = self->num_event_polls; ++self->num_event_polls; @@ -208,7 +290,10 @@ bool keyboard_event_init(keyboard_event *self, bool poll_stdout_error) { .events = POLLIN, .revents = 0 }; - self->dev_input_ids[self->num_event_polls] = -1; + self->event_extra_data[self->num_event_polls] = (event_extra_data) { + .dev_input_id = -1, + .grabbed = false + }; self->hotplug_event_index = self->num_event_polls; ++self->num_event_polls; @@ -228,6 +313,11 @@ bool keyboard_event_init(keyboard_event *self, bool poll_stdout_error) { } void keyboard_event_deinit(keyboard_event *self) { + if(self->uinput_fd > 0) { + close(self->uinput_fd); + self->uinput_fd = -1; + } + for(int i = 0; i < self->num_event_polls; ++i) { close(self->event_polls[i].fd); } @@ -264,10 +354,10 @@ void keyboard_event_poll_events(keyboard_event *self, int timeout_milliseconds, /* 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); + keyboard_event_process_input_event_data(self, &self->event_extra_data[i], self->event_polls[i].fd, callback, userdata); } } -bool keyboard_event_stdout_has_failed(keyboard_event *self) { +bool keyboard_event_stdout_has_failed(const 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 index 5084659..ca96cab 100644 --- a/tools/gsr-global-hotkeys/keyboard_event.h +++ b/tools/gsr-global-hotkeys/keyboard_event.h @@ -30,12 +30,18 @@ typedef enum { } 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 dev_input_id; + bool grabbed; +} event_extra_data; + +typedef struct { + struct pollfd event_polls[MAX_EVENT_POLLS]; /* Current size is |num_event_polls| */ + event_extra_data event_extra_data[MAX_EVENT_POLLS]; /* Current size is |num_event_polls| */ int num_event_polls; int stdout_event_index; int hotplug_event_index; + int uinput_fd; bool stdout_failed; hotplug_event hotplug_ev; @@ -51,13 +57,14 @@ typedef struct { } 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); +/* Return true to allow other applications to receive the key input (when using exclusive grab) */ +typedef bool (*key_callback)(uint32_t key, uint32_t modifiers, int press_status, void *userdata); -bool keyboard_event_init(keyboard_event *self, bool poll_stdout_error); +bool keyboard_event_init(keyboard_event *self, bool poll_stdout_error, bool exclusive_grab); 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); +bool keyboard_event_stdout_has_failed(const keyboard_event *self); #endif /* KEYBOARD_EVENT_H */ diff --git a/tools/gsr-global-hotkeys/main.c b/tools/gsr-global-hotkeys/main.c index 1b2dc4a..62d34cc 100644 --- a/tools/gsr-global-hotkeys/main.c +++ b/tools/gsr-global-hotkeys/main.c @@ -23,43 +23,46 @@ static global_hotkey global_hotkeys[NUM_GLOBAL_HOTKEYS] = { { .key = KEY_F10, .modifiers = KEYBOARD_MODKEY_ALT, .action = "replay_save" } }; -static void on_key_callback(uint32_t key, uint32_t modifiers, int press_status, void *userdata) { +static bool on_key_callback(uint32_t key, uint32_t modifiers, int press_status, void *userdata) { (void)userdata; if(press_status != 1) /* 1 == Pressed */ - return; + return true; for(int i = 0; i < NUM_GLOBAL_HOTKEYS; ++i) { if(key == global_hotkeys[i].key && modifiers == global_hotkeys[i].modifiers) { puts(global_hotkeys[i].action); fflush(stdout); + return false; } } + + return true; } int main(void) { const uid_t user_id = getuid(); if(geteuid() != 0) { if(setuid(0) == -1) { - fprintf(stderr, "error: failed to change user to root\n"); + fprintf(stderr, "Error: failed to change user to root\n"); return 1; } } keyboard_event keyboard_ev; - if(!keyboard_event_init(&keyboard_ev, true)) { + if(!keyboard_event_init(&keyboard_ev, true, true)) { fprintf(stderr, "Error: failed to setup hotplugging and no keyboard input devices were found\n"); setuid(user_id); return 1; } + fprintf(stderr, "Info: global hotkeys setup, waiting for hotkeys to be pressed\n"); + 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; } - - } keyboard_event_deinit(&keyboard_ev); |