ime: Delay release of last keypress

More closely simulate a human pressing by holding the last (or only) key in
key pressed for 30ms before releasing. The key is released automatically
after the timer expires or just in time before a subsequent injected key.
This commit is contained in:
Ben Jackson 2022-08-23 16:26:21 -07:00 committed by Pierre-Loup A. Griffais
parent 3303289afa
commit 40cbcd87ef

View file

@ -109,8 +109,12 @@ struct wlserver_input_method {
struct wlr_input_device keyboard_device; struct wlr_input_device keyboard_device;
std::deque<struct wlserver_input_method_key> keys; std::deque<struct wlserver_input_method_key> keys;
uint32_t next_keycode_index; uint32_t next_keycode_index;
int32_t held_keycode;
xkb_mod_mask_t held_modifier_mask;
struct wlr_keyboard_modifiers prev_mods;
struct wl_event_source *ime_reset_ime_keyboard_event_source; struct wl_event_source *ime_reset_ime_keyboard_event_source;
struct wl_event_source *ime_release_ime_keypress_event_source;
}; };
struct wlserver_input_method_manager { struct wlserver_input_method_manager {
@ -258,6 +262,58 @@ static struct xkb_keymap *generate_keymap(struct wlserver_input_method *ime)
return keymap; return keymap;
} }
static int release_key_if_needed(void *data)
{
struct wlserver_input_method *ime = (struct wlserver_input_method *)data;
struct wlr_seat *seat = ime->manager->server->wlr.seat;
if (ime->held_keycode >= 0)
wlr_seat_keyboard_notify_key(seat, 0, ime->held_keycode, WL_KEYBOARD_KEY_STATE_RELEASED);
ime->held_keycode = -1;
if (ime->held_modifier_mask)
{
if (ime->held_modifier_mask & WLR_MODIFIER_ALT)
wlr_seat_keyboard_notify_key(seat, 0, KEY_LEFTALT, WL_KEYBOARD_KEY_STATE_RELEASED);
if (ime->held_modifier_mask & WLR_MODIFIER_CTRL)
wlr_seat_keyboard_notify_key(seat, 0, KEY_LEFTCTRL, WL_KEYBOARD_KEY_STATE_RELEASED);
if (ime->held_modifier_mask & WLR_MODIFIER_SHIFT)
wlr_seat_keyboard_notify_key(seat, 0, KEY_LEFTSHIFT, WL_KEYBOARD_KEY_STATE_RELEASED);
wlr_seat_keyboard_notify_modifiers(seat, &ime->prev_mods);
}
ime->prev_mods = wlr_keyboard_modifiers{0};
ime->held_modifier_mask = 0;
return 0;
}
static void press_key(struct wlserver_input_method *ime, uint32_t keycode, struct wlr_keyboard_modifiers *pmods = nullptr)
{
struct wlr_seat *seat = ime->manager->server->wlr.seat;
release_key_if_needed(ime);
if (pmods)
{
if (seat->keyboard_state.keyboard != nullptr)
ime->prev_mods = seat->keyboard_state.keyboard->modifiers;
wlr_seat_keyboard_notify_modifiers(seat, pmods);
ime->held_modifier_mask = pmods->depressed & ~ime->prev_mods.depressed;
if (ime->held_modifier_mask & WLR_MODIFIER_SHIFT)
wlr_seat_keyboard_notify_key(seat, 0, KEY_LEFTSHIFT, WL_KEYBOARD_KEY_STATE_PRESSED);
if (ime->held_modifier_mask & WLR_MODIFIER_CTRL)
wlr_seat_keyboard_notify_key(seat, 0, KEY_LEFTCTRL, WL_KEYBOARD_KEY_STATE_PRESSED);
if (ime->held_modifier_mask & WLR_MODIFIER_ALT)
wlr_seat_keyboard_notify_key(seat, 0, KEY_LEFTALT, WL_KEYBOARD_KEY_STATE_PRESSED);
}
// Note: Xwayland doesn't care about the time field of the events
wlr_seat_keyboard_notify_key(seat, 0, keycode, WL_KEYBOARD_KEY_STATE_PRESSED);
ime->held_keycode = keycode;
wl_event_source_timer_update(ime->ime_reset_ime_keyboard_event_source, 30 /* ms */);
}
static bool try_type_keysym(struct wlserver_input_method *ime, xkb_keysym_t keysym) static bool try_type_keysym(struct wlserver_input_method *ime, xkb_keysym_t keysym)
{ {
struct wlr_seat *seat = ime->manager->server->wlr.seat; struct wlr_seat *seat = ime->manager->server->wlr.seat;
@ -291,37 +347,14 @@ static bool try_type_keysym(struct wlserver_input_method *ime, xkb_keysym_t keys
continue; continue;
} }
assert(keycode >= 8); release_key_if_needed(ime); // before keymap change
wlr_seat_set_keyboard(seat, device);
uint32_t keycodes[8] = {0};
size_t n = 0;
if (mask & WLR_MODIFIER_SHIFT) {
keycodes[n++] = KEY_LEFTSHIFT;
}
if (mask & WLR_MODIFIER_CTRL) {
keycodes[n++] = KEY_LEFTCTRL;
}
if (mask & WLR_MODIFIER_ALT) {
keycodes[n++] = KEY_LEFTALT;
}
keycodes[n++] = keycode - 8;
struct wlr_keyboard_modifiers prev_mods = {0};
if (seat->keyboard_state.keyboard != nullptr) {
prev_mods = seat->keyboard_state.keyboard->modifiers;
}
struct wlr_keyboard_modifiers mods = { struct wlr_keyboard_modifiers mods = {
.depressed = mask, .depressed = mask,
}; };
wlr_seat_set_keyboard(seat, device); assert(keycode >= 8);
wlr_seat_keyboard_notify_modifiers(seat, &mods); press_key(ime, keycode - 8, &mods);
for (size_t i = 0; i < n; i++) {
wlr_seat_keyboard_notify_key(seat, 0, keycodes[i], WL_KEYBOARD_KEY_STATE_PRESSED);
}
for (ssize_t i = n - 1; i >= 0; i--) {
wlr_seat_keyboard_notify_key(seat, 0, keycodes[i], WL_KEYBOARD_KEY_STATE_RELEASED);
}
wlr_seat_keyboard_notify_modifiers(seat, &prev_mods);
return true; return true;
} }
@ -364,12 +397,12 @@ static void type_text(struct wlserver_input_method *ime, const char *text)
xkb_keymap_unref(keymap); xkb_keymap_unref(keymap);
struct wlr_seat *seat = ime->manager->server->wlr.seat; struct wlr_seat *seat = ime->manager->server->wlr.seat;
release_key_if_needed(ime); // before keymap change
wlr_seat_set_keyboard(seat, &ime->keyboard_device); wlr_seat_set_keyboard(seat, &ime->keyboard_device);
// Note: Xwayland doesn't care about the time field of the events // Note: Xwayland doesn't care about the time field of the events
for (size_t i = 0; i < keycodes.size(); i++) { for (size_t i = 0; i < keycodes.size(); i++) {
wlr_seat_keyboard_notify_key(seat, 0, keycodes[i], WL_KEYBOARD_KEY_STATE_PRESSED); press_key(ime, keycodes[i]);
wlr_seat_keyboard_notify_key(seat, 0, keycodes[i], WL_KEYBOARD_KEY_STATE_RELEASED);
} }
// Reset keymap when we're idle for a while // Reset keymap when we're idle for a while
@ -400,11 +433,10 @@ static void perform_action(struct wlserver_input_method *ime, enum gamescope_inp
xkb_keymap_unref(keymap); xkb_keymap_unref(keymap);
struct wlr_seat *seat = ime->manager->server->wlr.seat; struct wlr_seat *seat = ime->manager->server->wlr.seat;
release_key_if_needed(ime); // before keymap change
wlr_seat_set_keyboard(seat, &ime->keyboard_device); wlr_seat_set_keyboard(seat, &ime->keyboard_device);
// Note: Xwayland doesn't care about the time field of the events press_key(ime, key.keycode);
wlr_seat_keyboard_notify_key(seat, 0, key.keycode, WL_KEYBOARD_KEY_STATE_PRESSED);
wlr_seat_keyboard_notify_key(seat, 0, key.keycode, WL_KEYBOARD_KEY_STATE_RELEASED);
// Reset keymap when we're idle for a while // Reset keymap when we're idle for a while
wl_event_source_timer_update(ime->ime_reset_ime_keyboard_event_source, 100 /* ms */); wl_event_source_timer_update(ime->ime_reset_ime_keyboard_event_source, 100 /* ms */);
@ -490,6 +522,7 @@ static int reset_ime_keyboard(void *data)
{ {
struct wlserver_input_method *ime = (struct wlserver_input_method *)data; struct wlserver_input_method *ime = (struct wlserver_input_method *)data;
release_key_if_needed(ime);
ime->keys.clear(); ime->keys.clear();
ime->next_keycode_index = 0; // preserve old behavior; could just let this keep going ime->next_keycode_index = 0; // preserve old behavior; could just let this keep going
@ -514,6 +547,9 @@ static void manager_handle_create_input_method(struct wl_client *client, struct
ime->manager = manager; ime->manager = manager;
ime->serial = 1; ime->serial = 1;
ime->next_keycode_index = 0; ime->next_keycode_index = 0;
ime->held_keycode = -1;
ime->held_modifier_mask = 0;
ime->prev_mods = wlr_keyboard_modifiers{0};
wlr_keyboard_init(&ime->keyboard, &keyboard_impl); wlr_keyboard_init(&ime->keyboard, &keyboard_impl);
wlr_input_device_init(&ime->keyboard_device, WLR_INPUT_DEVICE_KEYBOARD, &keyboard_device_impl, "ime", 0, 0); wlr_input_device_init(&ime->keyboard_device, WLR_INPUT_DEVICE_KEYBOARD, &keyboard_device_impl, "ime", 0, 0);
@ -525,6 +561,7 @@ static void manager_handle_create_input_method(struct wl_client *client, struct
gamescope_input_method_send_done(ime->resource, ime->serial); gamescope_input_method_send_done(ime->resource, ime->serial);
ime->ime_reset_ime_keyboard_event_source = wl_event_loop_add_timer(manager->server->event_loop, reset_ime_keyboard, ime); ime->ime_reset_ime_keyboard_event_source = wl_event_loop_add_timer(manager->server->event_loop, reset_ime_keyboard, ime);
ime->ime_release_ime_keypress_event_source = wl_event_loop_add_timer(manager->server->event_loop, release_key_if_needed, ime);
active_input_method = ime; active_input_method = ime;
} }