chansrv: Fix stale engine handling and cross-thread ibus commit
A few follow-on fixes found while testing the ibus reconnect changes in a live session: - xrdp_input_unicode_init() failed outright if ibus had no default global engine yet at connect time, which is a normal state on a fresh session (nothing has chosen one yet), not an error. Since nothing else used the return value, this permanently killed unicode input for the whole session over a spurious check. - ibus can disable our engine instance (e.g. on a focus change) while leaving the global engine name as "XrdpIme", since those are tracked separately. xrdp_input_enable()'s fast path only checked the name, so it could skip re-asserting and leave xrdp_input_send_unicode() committing text through a disabled g_engine. Clear g_engine on disable and require it to be set for the fast path to apply. - ibus_engine_commit_text() was being called from chansrv's own thread, not the thread pumping the glib main loop that owns the engine's D-Bus connection (xrdp_input_main_loop). Marshal the actual commit through g_main_context_invoke() onto the correct thread. None of these are the full fix for intermittent dropped commits during real pinyin input testing - that's still open, with the current lead being ibus Reset calls interleaved with commit bursts, likely from the FocusOut/FocusIn churn caused by passing every raw keystroke through engine_process_key_event_cb. To be continued. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
+66
-11
@@ -53,8 +53,14 @@ xrdp_input_enable(void)
|
|||||||
* committing text to an engine that's no longer active. */
|
* committing text to an engine that's no longer active. */
|
||||||
desc = ibus_bus_get_global_engine(bus);
|
desc = ibus_bus_get_global_engine(bus);
|
||||||
name = desc ? ibus_engine_desc_get_name(desc) : NULL;
|
name = desc ? ibus_engine_desc_get_name(desc) : NULL;
|
||||||
if (name && !g_ascii_strcasecmp(name, "XrdpIme"))
|
if (name && !g_ascii_strcasecmp(name, "XrdpIme") && g_engine)
|
||||||
{
|
{
|
||||||
|
/* XrdpIme is the global engine AND our cached engine instance is
|
||||||
|
* still the enabled one - genuinely nothing to do. If g_engine
|
||||||
|
* is NULL here, ibus disabled our instance (see
|
||||||
|
* xrdp_input_ibus_engine_disable) without changing the global
|
||||||
|
* engine name, so fall through and reassert below to get a
|
||||||
|
* fresh, enabled instance. */
|
||||||
g_object_unref(desc);
|
g_object_unref(desc);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -83,6 +89,40 @@ xrdp_input_enable(void)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
xrdp_input_commit_text_cb(gpointer data)
|
||||||
|
{
|
||||||
|
gunichar chr = GPOINTER_TO_UINT(data);
|
||||||
|
|
||||||
|
/* Runs on the same thread/main context as xrdp_input_main_loop()
|
||||||
|
* (see the g_main_context_invoke() call below), so this re-check of
|
||||||
|
* g_engine is race-free against xrdp_input_ibus_engine_enable() /
|
||||||
|
* xrdp_input_ibus_engine_disable(), which are dispatched on that
|
||||||
|
* same thread.
|
||||||
|
*
|
||||||
|
* NOTE: engine->has_focus was tried here as a gate (skip/retry the
|
||||||
|
* commit until the engine reports focus) on the theory that ibus
|
||||||
|
* drops commits sent while unfocused. That made things strictly
|
||||||
|
* worse: has_focus read FALSE on every single attempt, even ones
|
||||||
|
* that would otherwise have succeeded, because IBusEngine does not
|
||||||
|
* appear to auto-maintain that field from the FocusIn/FocusOut
|
||||||
|
* D-Bus calls in this setup - nothing in this file ever writes to
|
||||||
|
* it, so it's not a trustworthy signal without our own focus-in/
|
||||||
|
* focus-out handlers actually tracking it. Do not reintroduce a
|
||||||
|
* has_focus check without first confirming it actually flips to
|
||||||
|
* TRUE (e.g. by logging it from a real focus-in signal handler). */
|
||||||
|
if (g_engine)
|
||||||
|
{
|
||||||
|
ibus_engine_commit_text(g_engine, ibus_text_new_from_unichar(chr));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LOG(LOG_LEVEL_ERROR, "xrdp_input_send_unicode: no active ibus engine to commit to");
|
||||||
|
}
|
||||||
|
|
||||||
|
return G_SOURCE_REMOVE;
|
||||||
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
xrdp_input_send_unicode(char32_t unicode)
|
xrdp_input_send_unicode(char32_t unicode)
|
||||||
{
|
{
|
||||||
@@ -93,8 +133,16 @@ xrdp_input_send_unicode(char32_t unicode)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
gunichar chr = unicode;
|
/* ibus_engine_commit_text() must run on the same thread that's
|
||||||
ibus_engine_commit_text(g_engine, ibus_text_new_from_unichar(chr));
|
* pumping the glib main loop the engine's D-Bus connection belongs
|
||||||
|
* to (the xrdp_input_main_loop thread) - that's also the thread
|
||||||
|
* that processes FocusIn/FocusOut for the engine. Calling it
|
||||||
|
* directly from here (chansrv's own thread) lets the commit race
|
||||||
|
* against that focus churn with no ordering guarantee, so ibus can
|
||||||
|
* silently drop it. g_main_context_invoke() marshals the actual
|
||||||
|
* commit onto the correct thread instead. */
|
||||||
|
g_main_context_invoke(NULL, xrdp_input_commit_text_cb,
|
||||||
|
GUINT_TO_POINTER((guint)unicode));
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -110,6 +158,16 @@ static void
|
|||||||
xrdp_input_ibus_engine_disable(IBusEngine *engine)
|
xrdp_input_ibus_engine_disable(IBusEngine *engine)
|
||||||
{
|
{
|
||||||
LOG(LOG_LEVEL_INFO, "xrdp_ibus_engine_disable: IM disabled");
|
LOG(LOG_LEVEL_INFO, "xrdp_ibus_engine_disable: IM disabled");
|
||||||
|
|
||||||
|
/* ibus can disable our engine instance (e.g. it loses focus) while
|
||||||
|
* still reporting "XrdpIme" as the global engine name, since that's
|
||||||
|
* a separate piece of bookkeeping. Clear g_engine so xrdp_input_enable()
|
||||||
|
* notices the mismatch instead of committing text to a disabled
|
||||||
|
* engine on the next keypress. */
|
||||||
|
if (engine == g_engine)
|
||||||
|
{
|
||||||
|
g_engine = NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@@ -314,14 +372,11 @@ xrdp_input_unicode_init(void)
|
|||||||
|
|
||||||
tc_thread_create(xrdp_input_main_loop, NULL);
|
tc_thread_create(xrdp_input_main_loop, NULL);
|
||||||
|
|
||||||
if (!ibus_bus_get_global_engine(bus))
|
/* No global engine may be set yet at this point (e.g. ibus hasn't
|
||||||
{
|
* picked a default on this fresh session, or the user simply never
|
||||||
/* The bus connection itself is fine (and is now owned by the
|
* had one configured) - that's a normal state, not a failure. There
|
||||||
* ibus main loop thread we just started), so leave it in place
|
* is nothing to remember here; xrdp_input_enable() already handles
|
||||||
* rather than tearing it down here. */
|
* a NULL/absent current engine correctly when it's actually needed. */
|
||||||
LOG(LOG_LEVEL_ERROR, "xrdp_ibus_init: failed to get origin global engine");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user