-
Notifications
You must be signed in to change notification settings - Fork 186
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Some input method fixes #2111
Some input method fixes #2111
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm, now that you moved the Note I find the whole IM stuff quite confusing so far because it seems everything is a mess (not on Wayfire's side but the protocols & clients & how they work together), so maybe my intuition is wrong .. Also, do you have any references as to how gnome implements this or kwin? Do they also have so many checks and workarounds for the various brokenness in the protocols? I am kinda hoping there is an 'easy' solution to all the troubles that you've tried to fix here. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
That's exactly why again. Take the Firefox case as example:
The answer is super simple: they don't. GNOME uses D-Bus, and kwin uses input method v1 (ref). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Wayland at its finest :((
Ok, so repeating the flow of events as to how I see it to avoid misunderstandings:
I would say that the actual problem is that we erased L from the set in step 4. The set is supposed to contain all pressed, but not released keys for the current focus. Since the key was forwarded to the IM and not to the client, then L should have never been removed from the set. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Yes, it works nicely! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh no. With text input focused, super+some key to switch focus will leave super pressed... There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It seems to be some bad interactions with the IM. I did some change to fcitx5 and this seemed to no longer happen. |
||
{ | ||
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; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am confused, isn't mod_binding_key supposed to be reset on im keys? With this change, it no longer is reset. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm restoring the whole There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok but the modifier binding key should be reset even for grabbed events. It is used to detect when a modifier binding is pressed (e.g super pressed and released shortly after). Any keys in between reset the modifier binding, grabbed or not. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It should be reset later. It was here just because we returned early here. |
||
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; | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am highly sceptical of such approaches as you might have noticed .. what makes grab nodes special here? Why is this not a problem for other nodes (normal client surfaces)? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Because it intercepts the client's processing. Consider the following user events:
The switcher plugin activates at event 2. Now the text input is unfocused, so the input releases all pressed keys, sending keyup tab and keyup alt. The latter causes switcher to think the user has released the alt key. With 49d1218 the switcher case seems to be fixed, but there may be other complex interactions with other plugins. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I think I am starting to see what is going on, but why on earth does fcitx5 think it needs to generate these key releases? It is not supposed to do so, at least I don't think why it would need that. I'm suspecting that here we're really adding a workaround for an fcitx5 bug. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe we could just ignore all IM virtual keyboard events (whether to grab or not) until we switch focus? Just like we ignore commits until the focused text input switches properly. Because like now imagine alt-tab was not a shortcut which opens a grab, but immediately would switch and give focus to the next window. During normal interaction, that would be (this is what an imaginary plugin MIGHT do, I'm not saying we have a plugin which does this atm :)):
So what fcitx5 does would be wrong in this case, because fcitx5 generates additional release events between 2 and 3 (according to your description, if I understood it correctly) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
It seems to workaround a sway bug....fcitx/fcitx5@6f92a0c |
||
// Keys sent to the current keyboard focus | ||
std::multiset<uint32_t> pressed_keys; | ||
void transfer_grab(wf::scene::node_ptr new_focus); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I looked at the protocol and I believe what we are doing here is not a real fix for the issue. The protocol includes serials which is what we really need here. So here's the deal as far as I understand it:
When we switch focus or do something of the sort, we can send a
done
event. This generates a new serial number, serial numbers are counted from 0 to infinity and are incremented for each done event. I briefly looked through wlroots implementation but it might be wrong though (because they reset input_method->current_serial on each commit, which might actually have outdated information!). So we might need to keep track of it ourselves.For each commit from the text input, we receive the last serial that the input method has received so far (wlroots stores that in input_method->current_serial before emitting the commit event). So this means: until the input method has actually received our new configuration, it will send a commit with an outdated serial.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, I tried to add a
done
event afterset_focus
. It sends a commit with a correct serial. It doesn't make any difference whether we've changed focus. The input method receives a sequence ofdone
events and counts it correctly.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did you also try to check whether the serial belongs to the current focus or is an older event in the commit handler?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually there is already a
done
event when switching focus, and so by adding anotherdone
event, the input method replies with twocommit
s, with the same serial.What do you mean by
the serial belongs to the current focus
? The serial is just a counter ofdone
events. (I'm readingWAYLAND_DEBUG
here.)