feat(text-injection): use IBus on unbridged compositors when ibus-wayland runs - #614
Merged
jatinkrmalik merged 2 commits intoJul 28, 2026
Merged
Conversation
…land runs The `_IBUS_UNBRIDGED_COMPOSITORS` denylist assumes nothing sits between the compositor and ibus-daemon. Since IBus 1.5.32 that is no longer a safe assumption: IBus ships `ibus-wayland`, which implements `zwp_input_method_v2` and relays commits to native Wayland clients speaking text-input-v3. Every compositor on the denylist exposes `zwp_input_method_manager_v2`, so with the bridge running they are bridged, and skipping IBus falls back to a slower path unnecessarily. Add a live `pgrep -x ibus-wayland` probe and let it override the denylist. Scoped to the denylist branch only, so GNOME and the KDE VirtualKeyboard path (VocaHQ#574) are untouched. Behaviour is unchanged when the bridge is not running, and because the check is a live probe rather than configuration it degrades back to the denylist by itself if the bridge dies or was never started. Cost is one pgrep per TextInjector construction, not per injection. Verified on Hyprland 0.56.1 with IBus 1.5.34: dictation into Electron apps (VSCodium, Electron 39/41) and kitty commits through IBus, 303 chars in ~200ms versus 16.8s for 415 chars via `ydotool type`. Also adds VOCALINUX_FORCE_BACKEND (ibus|wtype|ydotool|auto) as discussed in VocaHQ#607. Autodetection has to infer whether IBus commits actually reach the focused app and cannot verify it -- commit_text() reports success even when the text is dropped -- so an escape hatch is useful when the inference is wrong, and it makes the two paths A/B-testable without editing code. Unknown values fall back to auto with a warning rather than silently pinning a backend. Related to VocaHQ#476 and VocaHQ#402; happy to drop this from the PR if it should follow their design instead. Two existing tests asserted the unbridged outcome without pinning the probe -- one blanket-mocks subprocess.run to returncode=0, the other shells out for real -- so both now state the "no bridge" precondition explicitly rather than depending on the host. Note for users: Chromium only opens a zwp_text_input_v3 seat when launched with --enable-wayland-ime; without it the bridge runs and the app still receives nothing. Closes VocaHQ#607
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #614 +/- ##
==========================================
+ Coverage 85.78% 85.85% +0.06%
==========================================
Files 35 35
Lines 6375 6405 +30
Branches 1035 1041 +6
==========================================
+ Hits 5469 5499 +30
Misses 683 683
Partials 223 223
🚀 New features to boost your workflow:
|
Codecov flagged the new branches in _check_dependencies as uncovered (74% patch, below the 80% target). Adds four tests to TestCheckDependencies: - unbridged compositor + ibus-wayland running selects IBus - VOCALINUX_FORCE_BACKEND=wtype pins wtype where IBus would be chosen - VOCALINUX_FORCE_BACKEND=ydotool pins ydotool even with wtype present - VOCALINUX_FORCE_BACKEND=ibus bypasses the reachability guards, with neither a daemon nor a bridge available Every line added by this PR is now covered.
jatinkrmalik
approved these changes
Jul 28, 2026
jatinkrmalik
left a comment
Member
There was a problem hiding this comment.
Nice. Keeping the ibus-wayland probe inside the denylist branch is the right shape so the KDE path stays alone, and leaving force-backend in here is fine. Tests cover the host-dependent cases well. LGTM.
6 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #607.
What
_IBUS_UNBRIDGED_COMPOSITORSassumes nothing sits between the compositor and ibus-daemon. Since IBus 1.5.32 that is no longer safe: IBus shipsibus-wayland, which implementszwp_input_method_v2and relays commits to native Wayland clients speaking text-input-v3. Every compositor on the denylist exposeszwp_input_method_manager_v2, so with the bridge running they are bridged — and skipping IBus drops to a slower path for no reason.This adds a live
pgrep -x ibus-waylandprobe and lets it override the denylist.Scope
The probe is checked only inside the denylist branch. GNOME and the KDE VirtualKeyboard path (#574) are untouched — there is a test asserting the probe is never even consulted for bridged desktops.
Properties:
pgrepperTextInjectorconstruction, not per injection.Evidence
Hyprland 0.56.1, IBus 1.5.34:
With the bridge started, dictation commits through IBus in VSCodium, Claude Desktop, another Electron 41 app and kitty — 303 chars in ~200 ms, versus 16.8 s for 415 chars via
ydotool type(-d 20 -H 20defaults = 40 ms/char).Caveat worth documenting either way: Chromium only opens a
zwp_text_input_v3seat when launched with--enable-wayland-ime. Without it the bridge runs and the app still receives nothing — possibly part of why IBus has looked unusable with Electron.VOCALINUX_FORCE_BACKEND
Folded in per your note on #607. Accepts
ibus/wtype/ydotool/auto; unknown values fall back toautowith a warning rather than silently pinning a backend.The motivation is that autodetection has to infer whether IBus commits reach the focused app and cannot verify it —
commit_text()reports success even when the text is dropped — so an escape hatch is useful when the inference is wrong, and it makes the two paths A/B-testable without editing code.This overlaps #476 / #402. Happy to drop it from this PR if you would rather it follow the design there — the probe stands alone and I would rather that not wait on it.
Two existing tests changed
Both asserted the unbridged outcome without pinning the probe, so both were implicitly host-dependent once a probe exists:
test_cosmic_skips_ibus_and_uses_wtypeblanket-mockssubprocess.runtoreturncode=0, which the probe reads as "bridge found".test_unbridged_wayland_skips_ibus_when_engine_is_xkbshells out for real, so it depended on whether the machine running the suite had the bridge up.Both now state the "no bridge" precondition explicitly. Their intent is unchanged.
Tests
10 added: denylisted compositors with and without the bridge, probe not consulted for bridged desktops, probe argv, probe resilience to missing/hung
pgrep, and theVOCALINUX_FORCE_BACKENDparsing matrix. No new flake8 findings and no test failures unique to this branch againstmain.