From 41aa7e1a7d7979ecfb426334a5c4ffab0fda13e7 Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Mon, 6 Jan 2025 16:30:54 +0900 Subject: [PATCH] IME: fix stuck Ctrl when pressed Ctrl+F in Firefox with Fcitx5 683f67b7 introduced another regression that the modifier state (Ctrl) is stuck when Ctrl+F is pressed in some applications like Firefox while Fcitx5 is running. This caused mouse scrolls to zoom in/out the UI. Let me explain the cause in detail. When Ctrl+F is pressed, an input box is opened in the application and Fcitx5 creates a new virtual keyboard (VK), whose initial modifiers is empty. Then prior to 683f67b7, the key/modifiers events flowed like this: - The compositor detects F key-release - Modifiers (Ctrl pressed) are notified via _set_keyboard() - F key-release is forwarded to IM - IM sends modifiers (Ctrl) back to the compositor via VK - **The modifiers on VK is updated (empty->Ctrl)** - **Modifers (Ctrl) are notified to the app** - IM sends F key-release back to the compositor via VK - F key-release is notified to the app - The compositor detects Ctrl key-release - Ctrl key-release is forwarded to IM - Modifiers (empty) are forwarded to IM - IM sends Ctrl key-release back to the compsitor via VK - Ctrl key-release is notified to IM - IM sends modifiers (empty) back to the compositor via VK - **The modifiers on VK is updated again (Ctrl->empty)** - **Modifiers (empty) are notified to the app** Thus, the final modifiers (empty) is notified to the application as expected. However, after 683f67b7, the key/modifiers events flowed like this: - The compositor detects F key-release - F key-release is directly notified to the app - The modifiers (Ctrl) is also notified to the app - The compositor detects Ctrl key-release - Ctrl key-release is directly notified to the app - Modifiers (empty) are forwarded to IM - IM sends modifiers (empty) back to the compositor via VK - **Modifier on VK is not updated (empty->empty)** - **The compositor ignores it** So the final modifier (empty) is never notified to the application, which causes stuck Ctrl modifier. This commit fixes this by not forwarding the modifiers when it hasn't been updated since it was forwarded previously. So after this commit, the key/modifiers events flow like this: - The compositor detects F key-release - F key-release is directly notified to the app - The modifiers (Ctrl) is also notified to the app - The compositor detects Ctrl key-release - Ctrl key-release is directly notified to the app - The modifiers are directly notified to the app because the modifiers (empty) are the same as the last forwarded modifier (empty). --- include/input/ime.h | 3 +++ src/input/ime.c | 17 ++++++++++++++++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/include/input/ime.h b/include/input/ime.h index 2840474a..01ea8ecd 100644 --- a/include/input/ime.h +++ b/include/input/ime.h @@ -21,7 +21,10 @@ struct input_method_relay { struct wl_list text_inputs; /* struct text_input.link */ struct wlr_input_method_v2 *input_method; struct wlr_surface *focused_surface; + struct lab_set forwarded_pressed_keys; + struct wlr_keyboard_modifiers forwarded_modifiers; + /* * Text-input which is enabled by the client and communicating with * input-method. diff --git a/src/input/ime.c b/src/input/ime.c index a6610ca0..dfaf0db4 100644 --- a/src/input/ime.c +++ b/src/input/ime.c @@ -58,11 +58,25 @@ input_method_keyboard_grab_forward_modifiers(struct keyboard *keyboard) { struct wlr_input_method_keyboard_grab_v2 *keyboard_grab = get_keyboard_grab(keyboard); + + struct wlr_keyboard_modifiers *forwarded_modifiers = + &keyboard->base.seat->input_method_relay->forwarded_modifiers; + struct wlr_keyboard_modifiers *modifiers = + &keyboard->wlr_keyboard->modifiers; + + if (forwarded_modifiers->depressed == modifiers->depressed + && forwarded_modifiers->latched == modifiers->latched + && forwarded_modifiers->locked == modifiers->locked + && forwarded_modifiers->group == modifiers->group) { + return false; + } + if (keyboard_grab) { + *forwarded_modifiers = keyboard->wlr_keyboard->modifiers; wlr_input_method_keyboard_grab_v2_set_keyboard(keyboard_grab, keyboard->wlr_keyboard); wlr_input_method_keyboard_grab_v2_send_modifiers(keyboard_grab, - &keyboard->wlr_keyboard->modifiers); + modifiers); return true; } else { return false; @@ -346,6 +360,7 @@ handle_input_method_grab_keyboard(struct wl_listener *listener, void *data) } relay->forwarded_pressed_keys = (struct lab_set){0}; + relay->forwarded_modifiers = (struct wlr_keyboard_modifiers){0}; relay->keyboard_grab_destroy.notify = handle_keyboard_grab_destroy; wl_signal_add(&keyboard_grab->events.destroy, -- 2.52.0