2a77f5f3e8
Replace the shared-global-context design with one where chansrv's thread never touches libibus directly. All IBus objects (bus, engine, factory, component) are now owned exclusively by a private GMainContext/GMainLoop created inside the IBus thread; chansrv hands off unicode codepoints through a thread-safe GAsyncQueue and wakes the loop, instead of calling ibus_engine_commit_text() from the wrong thread or relying on ibus_main()/ibus_quit(), which turned out to operate on a single loop shared by the whole process rather than one per connection. Fixes along the way, found by testing against a live ibus-daemon and a real focused GTK app rather than just reading the diff: - xrdp_engine_enable()/disable() now take a real reference on g_engine (g_object_ref/unref) instead of caching a bare pointer. The old code unreffed g_engine on teardown without ever having reffed it - IBusEngine derives from GInitiallyUnowned, so that was releasing a reference it never owned. - A custom GSource drains the unicode queue, gated on the engine actually existing (engine creation is async once XrdpIme is selected), so a character queued before the engine is ready isn't lost - it commits as soon as the "enable" signal fires, instead of racing a one-shot commit against that async setup. - ibus_engine_commit_text() releases its IBusText argument itself (it's floating, documented behavior for that specific call) - an earlier draft of this rewrite added an extra g_object_unref() after it, which would have been a double-release. - bus (and anything else that opens a GDBusConnection) must be created after the private GMainContext is pushed as thread-default, not before - otherwise its async I/O silently binds to the global default context, which nothing here iterates, and signals like "disconnected" simply never fire. - xrdp_input_unicode_init() now blocks on a condvar until the IBus thread actually signals ready (or exits), instead of returning immediately and hoping the engine shows up in time. - unicode_init()/destroy() no longer touch bus/g_engine from chansrv's own thread while the IBus thread may still be running against them; destroy() schedules real teardown on the IBus thread and joins it via a condvar before returning. Known limitation, confirmed empirically rather than assumed: this does NOT make reconnect-after-daemon-restart (#3230) actually work. ibus_bus_new() returns a process-wide singleton in this libibus version - two calls in the same process with no disconnect involved return the identical pointer - so once its connection dies, every later call just hands back the same dead object; ibus_bus_is_connected() on it stays permanently false, confirmed not to be a transient state via repeated retries with delay. The "disconnected" handler still tears the thread down cleanly so a later xrdp_input_unicode_init() fails fast instead of hanging or operating on stale state, but a real fix would mean bypassing IBusBus for a raw GDBusConnection to ibus-daemon, which isn't justified given how rarely the daemon actually restarts mid-session. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>