mirror of
https://repo.dec05eba.com/gpu-screen-recorder-ui
synced 2026-03-31 09:17:04 +09:00
Prevent focused application from receiving global hotkey keys on wayland as well (massive hack)
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user