-
Notifications
You must be signed in to change notification settings - Fork 179
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
Conversation
IM sent keys got sent to grab nodes after returning from handle_keyboard_key. This caused e.g. the switcher plugin to deactivate early, or not deactivate.
…s event to IM e.g. in Firefox, place focus anywhere not a text input, press Ctrl-L to focus the urlbar, l would be pressed for Firefox continuously without this patch because we got two release events for one press event.
{ | ||
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 comment
The 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 comment
The reason will be displayed to describe this comment to others. Learn more.
I'm restoring the whole wf::keyboard_t::handle_keyboard_key
function to previous state and moving the IM code to the on_key
callback.
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.
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 comment
The 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.
@@ -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 comment
The 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 comment
The reason will be displayed to describe this comment to others. Learn more.
Because it intercepts the client's processing. Consider the following user events:
- keydown alt
- keydown tab
- keyup tab
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 comment
The reason will be displayed to describe this comment to others. Learn more.
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.
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 comment
The 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 :)):
- press alt, press tab
- old window loses keyboard focus (no need for release events)
- new window get keyboard focus and is told that alt/tab are pressed on enter
- user releases alt, tab -> new window gets the release events
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 comment
The reason will be displayed to describe this comment to others. Learn more.
why on earth does fcitx5 think it needs to generate these key releases?
It seems to workaround a sway bug....fcitx/fcitx5@6f92a0c
@@ -221,6 +222,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 comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, now that you moved the handle_key()
call to seat.cpp, doesn't the seat already do this filtering of keys for you automatically? Isn't dbd98d3#diff-206c23fa3adf45f1a58ee9a5865dc0dec21bd2ba72a2441fb41d3fa88794cb6aR45-R59 executed for the keys here, so why need to check here again?
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 comment
The reason will be displayed to describe this comment to others. Learn more.
so why need to check here again?
That's exactly why again. Take the Firefox case as example:
- No text input in focus. The user sends keydown ctrl and keydown l.
- Firefox focuses the address as requested. Text input in focus now.
- The user sends keyup l.
- Input method passes through the keyup l, and sends via virtual keyboard to the compositor.
- In the code you linked, this second keyup l is ignored because from the compositor's view it's already been released.
- Firefox never receives the keyup l event, and so auto repeat kicks in and a lot of l is inserted.
Also, do you have any references as to how gnome implements this or kwin?
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 comment
The reason will be displayed to describe this comment to others. Learn more.
The answer is super simple: they don't. GNOME uses D-Bus, and kwin uses input method v1 (ref).
Wayland at its finest :((
5. In the code you linked, this second keyup l is ignored because from the compositor's view it's already been released.
Ok, so repeating the flow of events as to how I see it to avoid misunderstandings:
- Keydown ctrl, keydown L
- Focus text input
- IM grabs keyboard
- keyup L -> we go to the code I linked -> L is erased from the set -> L is sent to the IM, not to the client
- IM sends virtual keyup L to compensate -> we go to the linked code -> L is not in the set, so it is dropped
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 comment
The reason will be displayed to describe this comment to others. Learn more.
Since the key was forwarded to the IM and not to the client, then L should have never been removed from the set.
Yes, it works nicely!
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.
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 comment
The 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.
// 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()) |
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 after set_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 of done
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.
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.
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 another done
event, the input method replies with two commit
s, with the same serial.
What do you mean by the serial belongs to the current focus
? The serial is just a counter of done
events. (I'm reading WAYLAND_DEBUG
here.)
Scrap that, I asked on #wlroots, this is indeed how it is all supposed to work .. Some of my points above still stand though ;) |
I'm closing this pr as the code has changed significantly. I'd like the change to be preserved as history rather than overridden. |
I'm sorry to bother you on this thing again but bugs have been found in previous days.