Skip to content
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

wayland: clear IME preedit only when necessary #4084

Merged
merged 1 commit into from
Jan 17, 2025

Conversation

tomcur
Copy link
Contributor

@tomcur tomcur commented Jan 17, 2025

When all we'll be doing is setting a new preedit, the preedit doesn't have to be explicitly cleared first. This change is perhaps debatable.

The direct reason for this is to make it easier to work around quirks/bugs: in Masonry we've found IBus appears to resend the IME preedit in response to Window::set_ime_cursor_area
(zwp_text_input_v3::set_cursor_rectangle). Because currently the preedit is first cleared, a new IME cursor area is sent, which again causes IBus to resend the preedit. This can loop for a while.

The Wayland protocol is mechanically quite prescriptive, it says for zwp_text_input_v3::done

  1. Replace existing preedit string with the cursor. 2. Delete requested surrounding text. 3. Insert commit string with the cursor at its end. 4. Calculate surrounding text to send. 5. Insert new preedit text in cursor position. 6. Place cursor inside preedit text.

Winit currently doesn't do surrounding text, so 2. and 4. can be ignored. In Winit's IME model, without a commit, sending just the Ime::Preedit event without explicitly clearing is arguably still equivalent to doing 1., 5., and 6.

  • Tested on all platforms changed
  • Added an entry to the changelog module if knowledge of this change could be valuable to users
  • Updated documentation to reflect any user-facing changes, including notes of platform-specific behavior
  • Created or updated an example program if it would help users understand this functionality

@tomcur tomcur requested a review from kchibisov as a code owner January 17, 2025 15:02
@kchibisov
Copy link
Member

See this #2478

@kchibisov
Copy link
Member

Could you post the WAYLAND_DEBUG=1 of what makes things confusing, maybe we can do something about it?

@tomcur
Copy link
Contributor Author

tomcur commented Jan 17, 2025

Yes, and #2484 also seems relevant.

This is part of what happens:

[2155555.556] zwp_text_input_v3#20.preedit_string("a b", 0, 3)
[2155555.568] zwp_text_input_v3#20.done(3)
[2155575.031]  -> zwp_text_input_v3#20.set_cursor_rectangle(462, 227, 115, 39)
[2155575.038]  -> zwp_text_input_v3#20.commit()
[2155584.433]  -> zwp_text_input_v3#20.set_cursor_rectangle(462, 227, 163, 39)
[2155584.438]  -> zwp_text_input_v3#20.commit()

After receiving zwp_text_input_v3#20.preedit_string("a b", 0, 3) and zwp_text_input_v3#20.done(3), Winit first generates Ime::Preedit("", None) which is immediately followed by Ime::Preedit("a b", Some((0, 3))). (Note this is when sending a new preedit, not a commit.) Depending on the exact timing, the client sends two IME cursor areas in response to the two IME events received from Winit.

The problem in the IBus case specifically is that it sends a new zwp_text_input_v3#20.preedit_string("a b", 0, 3) in response to zwp_text_input_v3#20.commit(). Winit again generates the two Ime::Preedit events (clearing first, setting second). As the client cannot look-ahead in the Winit events, it is difficult to know that the preedit will actually remain unchanged and that it doesn't have to set the new IME cursor area.

(I couldn't get IBus to work -- I could ask the person who notified me of this to show their debug logs, though. We should see it looping the above fragment over and over for some time.)

This is not directly a Winit issue, but rather is due to quirkiness of platforms and clients. This change makes it such that Winit only sends Ime::Preedit("a b", Some((0, 3))) in this case, which means the client can detect whether the preedit is unchanged.

Copy link
Member

@kchibisov kchibisov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I read it wrong initially, that should be good.

src/changelog/unreleased.md Outdated Show resolved Hide resolved
When all we'll be doing is setting a new preedit, the preedit doesn't
have to be explicitly cleared first. This change is perhaps debatable.

The direct reason for this is to make it easier to work around
quirks/bugs: in [Masonry](https://github.com/linebender/xilem/masonry)
we've found IBus appears to resend the IME preedit in response to
`Window::set_ime_cursor_area`
(`zwp_text_input_v3::set_cursor_rectangle`). Because currently the
preedit is first cleared, a new IME cursor area is sent, which again
causes IBus to resend the preedit. This can loop for a while.

The Wayland protocol is mechanically quite prescriptive,
it says for [`zwp_text_input_v3::done`](https://wayland.app/protocols/text-input-unstable-v3#zwp_text_input_v3:event:done)

> 1. Replace existing preedit string with the cursor. 2. Delete requested surrounding text. 3. Insert commit string with the cursor at its end. 4. Calculate surrounding text to send. 5. Insert new preedit text in cursor position. 6. Place cursor inside preedit text.

Winit currently doesn't do surrounding text, so 2. and 4. can be
ignored. In Winit's IME model, without a commit, sending just the
`Ime::Preedit` event without explicitly clearing is arguably still
equivalent to doing 1., 5., and 6.
@tomcur
Copy link
Contributor Author

tomcur commented Jan 17, 2025

Thanks!

@kchibisov kchibisov merged commit 77f1c73 into rust-windowing:master Jan 17, 2025
58 checks passed
@tomcur tomcur deleted the preedit-clear branch January 17, 2025 17:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

2 participants