IM Engine never gets a notification that surrounding text changed after a widget or window gets focus
I use wayland (fedora 36 up to date) so the input client used by gtk applications is gtk's own native imwayland, not ibus' clients. Gtk3 version installed is 3.24.34 (and gtk4 4.6.6).
I'm developing an IM engine for IBus that does speech to text. I noticed that the IBus engine never gets any notification that surrounding text is different when a new tab is opened, when there is a switch between tabs within the same application (like gedit) or when you switch from one application to another. The only notification that surrounding text changed the IM engine gets is when you type something or commit text.
Steps to reproduce
-
Open an application that allows text editing (let's say gnome-builder) and type some text.
-
Open gedit (or gnome-text-editor for gtk4)
-
Open gedit and type some text.
-
Open a new tab in gedit.
-
Open gedit and type some text
-
Open a new tab and type some text
-
Switch to the old tab
Current behavior
IM Engine never gets a signal that surrounding text is different.
Expected outcome
IM engine should have a notification that surrounding text is no longer the same (and possibly that there is no text when opening a new tab in gedit).
Version information
See above
Additional information
I tried to find the origin of the problem and after reading Gtk's code and doing some testing, two things seem strange (to me at least but I'm likely very wrong).
In modules/input/imwayland.c, when a widget gets focus, enable() is called with the GtkIMContextWayland object associated with the widget to enable this new context. In this function, you find that g_signal_emit_by_name (global->current, "retrieve-surrounding", &result) is called to update the surrounding text.
The result of the above is gtk_im_context_wayland_set_surrounding() is in turn called with the current surrounding text of the widget which is tested like this before any notification is sent to mutter / gnome-shell / ibus: if (context_wayland->surrounding.text && text && (len < 0 || len == strlen (context_wayland->surrounding.text)) && strncmp (context_wayland->surrounding.text, text, len) == 0 && context_wayland->surrounding.cursor_idx == cursor_index && context_wayland->surrounding.anchor_idx == cursor_index) return;
Now I can't see (but maybe I don't understand) how it can not return since the widget was previously focused out and therefore the text did not change (or it might have in some rare situations) so the old and new surrounding texts are bound to be the same and no notification gets ever sent. But I'm probably missing something here.
I tried to work around this but I got stumped in enable() because of serialization. I tried to call notify_surrounding_text() instead of g_signal_emit_by_name (global->current, "retrieve-surrounding", &result) to just send the cached value of the GtkIMContextWayland object. Except this does not work because, disable() is called (with the widget that previously had the focus) just before enable(). But commit_state() called in disable() has not yet returned (text_input_done() has not been called yet) when enable() is called and therefore global->serial and global->done_serial mismatch which in turn prevents notify_surrounding_text() from sending any message to mutter. It seems this is also true for notify_content_type(), notify_cursor_location() that are also called in enable().
Let me know if you need more information, where I'm wrong or if I can test anything. Thanks