Skip to content

waylandim: unblock surrounding-text updates on Hyprland - #1689

Closed
sebkouba wants to merge 1 commit into
fcitx:masterfrom
sebkouba:codex/wayland-v2-state-progress
Closed

sebkouba wants to merge 1 commit into
fcitx:masterfrom
sebkouba:codex/wayland-v2-state-progress

Conversation

@sebkouba

Copy link
Copy Markdown

On Hyprland 0.56.2 with Electron 43.6.0 using text-input-v3, editing a textarea with fcitx5's keyboard input method can leave surrounding text unavailable to add-ons.

Chromium queues field updates until it receives a matching done. Hyprland sends that reply when the input method commits. Forwarding keys alone can leave the exchange stalled.

This proposes publishing the current preedit state after done when focused, unless a commit was already sent for that serial. It reuses the existing preedit path and skips inactive contexts.

The protocol does not require acknowledging every done; feedback on whether fcitx5 is the right place to resolve this interaction is welcome.

Validation:

  • Same upstream source without this patch: unavailable text; with it and no preload workaround: text, Unicode, caret and selection updates work.
  • Live GTK keyboard input, focus switching and composing é checked.
  • All 51 existing CTest tests pass. The interoperability reproduction was tested manually; no automated regression test is included.

@coderabbitai

coderabbitai Bot commented Sep 22, 2026

Copy link
Copy Markdown

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@wengxt

wengxt commented Sep 22, 2026

Copy link
Copy Markdown
Member

TBH, this seems to be a compositor bug that should be fixed on hyprland side. I'm not really against the change though, because it seems to be align with what I do in kwin.

https://github.com/KDE/kwin/blob/1ebd28f22075b707bb44343b5de4dd1094dd70c3/src/wayland/textinput_v3.cpp#L505

@wengxt

wengxt commented Sep 22, 2026

Copy link
Copy Markdown
Member

If you want to get it merged please get it out of draft so I know it's ready

@wengxt

wengxt commented Sep 23, 2026

Copy link
Copy Markdown
Member

I did some test. I don't think this really helps. You'd better file a bug at chromium.

wengxt added a commit that referenced this pull request Sep 23, 2026
certain compositor may not send done. As this the behavior that kwin
implemented in v3, it should be generally safe to update current preedit
on focus in.

Close #1689
wengxt added a commit that referenced this pull request Sep 23, 2026
certain compositor may not send done. As this the behavior that kwin
implemented in v3, it should be generally safe to update current preedit
on focus in.

Close #1689
@sebkouba

Copy link
Copy Markdown
Author

Thanks for looking at this, and for #1690. I reran the same Electron 43.6.0 textarea test on Hyprland 0.56.2 with the preload workaround disabled. The textarea itself worked in all three runs; I was checking whether its updated surrounding text reached fcitx.

So #1690 helps with activation, but it doesn’t resolve the case this PR addresses. I agree with your earlier point that Hyprland’s relay is the better place for the underlying fix: it currently sends the client’s done when the input method commits, and a forwarding-only path may never do that. I’ll take the reproduction there. Chromium’s behavior is also worth reporting, but I don’t think #1690 should close #1689 on the basis of this test.

I kept this PR in draft while checking whether it actually helped. The comparison confirms it does for this case. If you’re open to keeping the fcitx change as a compatibility workaround, I’ll mark it ready for review. I can share the reduced protocol traces and test harness.

@wengxt

wengxt commented Sep 23, 2026

Copy link
Copy Markdown
Member

I don't think your & mine has much difference, except your might check last commit serial, which could only make it commit less, instead of more (note I updated 1690 once please use the latested one).

So upon activate, the log I saw is like this:

Chrome

[07:17:38.799297] zwp_text_input_v3#52.enter(wl_surface#36)
[07:17:38.799304]  -> zwp_text_input_v3#52.enable()
[07:17:38.799308]  -> zwp_text_input_v3#52.set_content_type(0, 5)
[07:17:38.799312]  -> zwp_text_input_v3#52.commit()
[07:17:38.805201] zwp_text_input_v3#52.done(2)
[07:17:38.805216]  -> zwp_text_input_v3#52.set_cursor_rectangle(122, 55, 1, 18)
[07:17:38.805221]  -> zwp_text_input_v3#52.commit()
[07:17:38.806814] zwp_text_input_v3#52.done(3)
[07:17:44.429854]  -> zwp_text_input_v3#52.disable()
[07:17:44.429858]  -> zwp_text_input_v3#52.commit()
[07:17:44.430842] zwp_text_input_v3#52.leave(wl_surface#36)
[07:17:47.134613] {Default Queue} zwp_input_method_v2#14.activate()
[07:17:47.134619] {Default Queue} zwp_input_method_v2#14.text_change_cause(0)
[07:17:47.134622] {Default Queue} zwp_input_method_v2#14.done()
[07:17:47.134630] {Default Queue}  -> zwp_input_method_v2#14.grab_keyboard(new id zwp_input_method_keyboard_grab_v2#24)
[07:17:47.134689] {Default Queue}  -> zwp_input_method_v2#14.commit(9)
[07:17:47.134692] {Default Queue} zwp_input_method_v2#14.text_change_cause(0)
[07:17:47.134695] {Default Queue} zwp_input_method_v2#14.done()
[07:17:47.134698] {Default Queue}  -> zwp_input_method_v2#14.commit(10)
[07:17:47.134700] {Default Queue} zwp_input_method_v2#14.text_change_cause(0)
[07:17:47.134701] {Default Queue} zwp_input_method_v2#14.done()
[07:17:47.134705] {Default Queue}  -> zwp_input_method_v2#14.commit(11)
[07:17:47.134706] {Default Queue} zwp_input_method_v2#14.text_change_cause(0)
[07:17:47.134708] {Default Queue} zwp_input_method_v2#14.done()
[07:17:47.134711] {Default Queue}  -> zwp_input_method_v2#14.commit(12)

No surrounding update at all.

I think it's still on compositor side. Actually, chromium seems doesn't want to send unchanged surrounding, while hyprland doesn't send the cached surrounding upon activation.

Actually for chrome you can easily see its initial sequence of event is like this:

[07:23:02.172527]  -> zwp_text_input_v3#52.set_surrounding_text("", 0, 0)
[07:23:02.172530]  -> zwp_text_input_v3#52.commit()
[07:23:02.173008] zwp_text_input_v3#52.enter(wl_surface#36)
[07:23:02.173011]  -> zwp_text_input_v3#52.enable()
[07:23:02.173012]  -> zwp_text_input_v3#52.set_content_type(0, 5)
[07:23:02.173014]  -> zwp_text_input_v3#52.commit()
[07:23:02.178131] zwp_text_input_v3#52.done(2)
[07:23:02.178133]  -> zwp_text_input_v3#52.set_cursor_rectangle(122, 55, 1, 18)
[07:23:02.178136]  -> zwp_text_input_v3#52.commit()
[07:23:02.182519] zwp_text_input_v3#52.done(3)
[07:23:02.924214]  -> zwp_text_input_v3#52.disable()
[07:23:02.924218]  -> zwp_text_input_v3#52.commit()
[07:23:02.925237] zwp_text_input_v3#52.leave(wl_surface#36)

The set surrounding is before the first enable, but fcitx doesn't get any surrounding later.

In this case, seems the most problematic one is chrome. compositor doesn't have responsibliity to hold any "last send" surrounding after disable.

        Values set with this request are double-buffered. They will get applied
        on the next zwp_text_input_v3.commit request, and stay valid until the
        next committed enable or disable request.

@wengxt

wengxt commented Sep 23, 2026

Copy link
Copy Markdown
Member

Which means, chromium is responsible to set the surrounding together with the text-input-v3.enable(), Otherwise the value is invalidated automatically.

@wengxt

wengxt commented Sep 23, 2026

Copy link
Copy Markdown
Member

Filed the bug, FYI https://issues.chromium.org/issues/565066842

@wengxt

wengxt commented Sep 23, 2026

Copy link
Copy Markdown
Member

Comparing to qt's text-input-v3, or gtk, the issue is obvious. The inital surrounding should be come together with activate.

[07:37:42.265237] {Default Queue} zwp_input_method_v2#14.activate()
[07:37:42.265272] {Default Queue} zwp_input_method_v2#14.surrounding_text("fdsfadsfasdf", 12, 12)
[07:37:42.265303] {Default Queue} zwp_input_method_v2#14.text_change_cause(0)
[07:37:42.265311] {Default Queue} zwp_input_method_v2#14.content_type(7, 0)
[07:37:42.265342] {Default Queue} zwp_input_method_v2#14.done()
[07:37:42.265359] {Default Queue}  -> zwp_input_method_v2#14.grab_keyboard(new id zwp_input_method_keyboard_grab_v2#24)
[07:37:42.265491] {Default Queue}  -> zwp_input_method_v2#14.commit(3537)

@wengxt

wengxt commented Sep 23, 2026

Copy link
Copy Markdown
Member

Ok I see the difference here, you're trying to have commit on every done (if needed)

So two separate issues here, one is chrome never send surrounding upon focus-in. No way to workaround it.

Second would be the some compositor doesn't "actively" reply text-input-v3.done for commit if input method doesn't do so. I guess it somewhat related to the fact that kwin/mutter has to handle these because zwp_v1 or ibus dbus protocol doesn't have 1:1 match for this commit done thing.

So it's no surprise that it's mainly on input method v2 compositor.

The if last != serial doesn't help much here if you want to save useless calls, because serial is increment upon the done event. It will always hold.

wengxt added a commit that referenced this pull request Sep 23, 2026
certain compositor may not send done. As this the behavior that kwin
implemented in v3, it should be generally safe to update current preedit
on focus in.

Close #1689
wengxt added a commit that referenced this pull request Sep 23, 2026
certain compositor may not send done. As this the behavior that kwin
implemented in v3, it should be generally safe to update current preedit
on focus in.

Close #1689
@wengxt wengxt closed this in #1690 Sep 23, 2026
wengxt added a commit that referenced this pull request Sep 23, 2026
certain compositor may not send done. As this the behavior that kwin
implemented in v3, it should be generally safe to update current preedit
on focus in.

Close #1689
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants