diff --git a/src/core/seat/input-method-relay.cpp b/src/core/seat/input-method-relay.cpp index a341ed376..bd731a666 100644 --- a/src/core/seat/input-method-relay.cpp +++ b/src/core/seat/input-method-relay.cpp @@ -60,11 +60,20 @@ wf::input_method_relay::input_method_relay() // We ignore such commit requests so it doesn't have any affect on the // new window. Even when the previous window isn't preediting when // switching focus, it doesn't have any bad effect to the new window anyway. - if (focus_just_changed) + // + // Preediting can be disabled (selectively or globally) because + // application bugs were observed. In this case, we see only commits + // but no preedit strings, so we need care the timing. + static std::chrono::milliseconds focus_change_duration{100}; + if (last_focus_changed.has_value()) { - LOGI("focus_just_changed, ignore input method commit"); - focus_just_changed = false; - return; + auto elapsed = std::chrono::steady_clock::now() - *last_focus_changed; + last_focus_changed.reset(); + if (elapsed < focus_change_duration) + { + LOGI("focus just changed, ignore input method commit"); + return; + } } auto *text_input = find_focused_text_input(); @@ -136,6 +145,7 @@ wf::input_method_relay::input_method_relay() on_grab_keyboard_destroy.set_callback([&] (void *data) { + this->pressed_keys.clear(); on_grab_keyboard_destroy.disconnect(); keyboard_grab = nullptr; }); @@ -221,6 +231,22 @@ bool wf::input_method_relay::should_grab(wlr_keyboard *kbd) return !is_im_sent(kbd); } +bool wf::input_method_relay::check_superfluous_release(uint32_t key, uint32_t state) +{ + if (state == WL_KEYBOARD_KEY_STATE_PRESSED) + { + this->pressed_keys.insert(key); + return false; + } else if (auto it = this->pressed_keys.find(key);it != this->pressed_keys.end()) + { + this->pressed_keys.erase(it); + return false; + } else + { + return true; + } +} + bool wf::input_method_relay::is_im_sent(wlr_keyboard *kbd) { struct wlr_virtual_keyboard_v1 *virtual_keyboard = wlr_input_device_get_virtual_keyboard(&kbd->base); @@ -261,6 +287,11 @@ bool wf::input_method_relay::handle_key(struct wlr_keyboard *kbd, uint32_t time, return false; } + if (check_superfluous_release(key, state)) + { + return false; + } + wlr_input_method_keyboard_grab_v2_set_keyboard(keyboard_grab, kbd); wlr_input_method_keyboard_grab_v2_send_key(keyboard_grab, time, key, state); return true; diff --git a/src/core/seat/input-method-relay.hpp b/src/core/seat/input-method-relay.hpp index 1d6864755..a0c99abae 100644 --- a/src/core/seat/input-method-relay.hpp +++ b/src/core/seat/input-method-relay.hpp @@ -8,6 +8,7 @@ #include #include +#include namespace wf { @@ -22,7 +23,7 @@ class input_method_relay on_input_method_new, on_input_method_commit, on_input_method_destroy, on_grab_keyboard, on_grab_keyboard_destroy, on_new_popup_surface; wlr_input_method_keyboard_grab_v2 *keyboard_grab = nullptr; - bool focus_just_changed = false; + std::optional> last_focus_changed; text_input *find_focusable_text_input(); void set_focus(wlr_surface*); @@ -37,10 +38,12 @@ class input_method_relay set_focus(nullptr); } - focus_just_changed = true; + last_focus_changed = std::chrono::steady_clock::now(); }; bool should_grab(wlr_keyboard*); + std::multiset pressed_keys; + bool check_superfluous_release(uint32_t key, uint32_t state); public: diff --git a/src/core/seat/keyboard.cpp b/src/core/seat/keyboard.cpp index 449082c79..7482eaa10 100644 --- a/src/core/seat/keyboard.cpp +++ b/src/core/seat/keyboard.cpp @@ -38,8 +38,9 @@ void wf::keyboard_t::setup_listeners() } seat->priv->set_keyboard(this); - if (!handle_keyboard_key(ev->time_msec, ev->keycode, - ev->state) && (mode == input_event_processing_mode_t::FULL)) + auto is_im_sent = wf::get_core_impl().im_relay->is_im_sent(handle); + if ((is_im_sent || !handle_keyboard_key(ev->keycode, ev->state)) && + (mode == input_event_processing_mode_t::FULL)) { if (ev->state == WL_KEYBOARD_KEY_STATE_PRESSED) { @@ -57,7 +58,16 @@ void wf::keyboard_t::setup_listeners() } } - if (seat->priv->keyboard_focus) + // don't send IM sent keys to plugin grabs + if (!seat->priv->is_grab) + { + if (wf::get_core_impl().im_relay->handle_key(handle, ev->time_msec, ev->keycode, ev->state)) + { + return; + } + } + + if (seat->priv->keyboard_focus && !(seat->priv->is_grab && is_im_sent)) { seat->priv->keyboard_focus->keyboard_interaction() .handle_keyboard_key(wf::get_core().seat.get(), *ev); @@ -287,19 +297,13 @@ bool wf::keyboard_t::has_only_modifiers() return true; } -bool wf::keyboard_t::handle_keyboard_key(uint32_t time, uint32_t key, uint32_t state) +bool wf::keyboard_t::handle_keyboard_key(uint32_t key, uint32_t state) { using namespace std::chrono; auto& input = wf::get_core_impl().input; auto& seat = wf::get_core_impl().seat; - if (wf::get_core_impl().im_relay->is_im_sent(handle)) - { - mod_binding_key = 0; - return false; - } - bool handled_in_plugin = false; auto mod = mod_from_key(key); input->locked_mods = this->get_locked_mods(); @@ -328,11 +332,6 @@ bool wf::keyboard_t::handle_keyboard_key(uint32_t time, uint32_t key, uint32_t s handled_in_plugin |= wf::get_core().bindings->handle_key( wf::keybinding_t{get_modifiers(), key}, mod_binding_key); - - if (!handled_in_plugin) - { - handled_in_plugin |= wf::get_core_impl().im_relay->handle_key(handle, time, key, state); - } } else { if (mod_binding_key != 0) @@ -349,11 +348,6 @@ bool wf::keyboard_t::handle_keyboard_key(uint32_t time, uint32_t key, uint32_t s } } - if (!handled_in_plugin) - { - handled_in_plugin |= wf::get_core_impl().im_relay->handle_key(handle, time, key, state); - } - mod_binding_key = 0; } diff --git a/src/core/seat/keyboard.hpp b/src/core/seat/keyboard.hpp index e982148b0..9e4c4fda1 100644 --- a/src/core/seat/keyboard.hpp +++ b/src/core/seat/keyboard.hpp @@ -50,7 +50,7 @@ class keyboard_t std::chrono::steady_clock::time_point mod_binding_start; - bool handle_keyboard_key(uint32_t time, uint32_t key, uint32_t state); + bool handle_keyboard_key(uint32_t key, uint32_t state); /** Get the current locked mods */ uint32_t get_locked_mods(); diff --git a/src/core/seat/seat-impl.hpp b/src/core/seat/seat-impl.hpp index 2c00c250a..f668bb31a 100644 --- a/src/core/seat/seat-impl.hpp +++ b/src/core/seat/seat-impl.hpp @@ -57,6 +57,7 @@ struct seat_t::impl void set_keyboard_focus(wf::scene::node_ptr keyboard_focus); wf::scene::node_ptr keyboard_focus; + bool is_grab = false; // Keys sent to the current keyboard focus std::multiset pressed_keys; void transfer_grab(wf::scene::node_ptr new_focus); diff --git a/src/core/seat/seat.cpp b/src/core/seat/seat.cpp index 40ee3ad64..b47b32bec 100644 --- a/src/core/seat/seat.cpp +++ b/src/core/seat/seat.cpp @@ -500,6 +500,7 @@ void wf::seat_t::impl::transfer_grab(wf::scene::node_ptr grab_node) } this->keyboard_focus = grab_node; + this->is_grab = true; grab_node->keyboard_interaction().handle_keyboard_enter(wf::get_core().seat.get()); wf::keyboard_focus_changed_signal data; @@ -521,6 +522,7 @@ void wf::seat_t::impl::set_keyboard_focus(wf::scene::node_ptr new_focus) } this->keyboard_focus = new_focus; + this->is_grab = false; if (new_focus) { new_focus->keyboard_interaction().handle_keyboard_enter(wf::get_core().seat.get());