Skip to content

feat(text-injection): use IBus on unbridged compositors when ibus-wayland runs - #614

Merged
jatinkrmalik merged 2 commits into
VocaHQ:mainfrom
eiseleb47:feature/ibus-wayland-bridge-probe
Jul 28, 2026
Merged

feat(text-injection): use IBus on unbridged compositors when ibus-wayland runs#614
jatinkrmalik merged 2 commits into
VocaHQ:mainfrom
eiseleb47:feature/ibus-wayland-bridge-probe

Conversation

@eiseleb47

Copy link
Copy Markdown
Contributor

Closes #607.

What

_IBUS_UNBRIDGED_COMPOSITORS assumes nothing sits between the compositor and ibus-daemon. Since IBus 1.5.32 that is no longer safe: 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 drops to a slower path for no reason.

This adds a live pgrep -x ibus-wayland probe 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:

  • Strictly additive. With no bridge running, behaviour is byte-for-byte what it is today.
  • Self-disabling. A live probe rather than configuration, so it degrades back to the denylist by itself if the bridge dies or was never started. There is no way to assert a bridge that isn't there.
  • Cost is one pgrep per TextInjector construction, not per injection.

Evidence

Hyprland 0.56.1, IBus 1.5.34:

$ wayland-info | grep -E 'text_input|input_method'
interface: 'zwp_text_input_manager_v1',    version: 1, name: 20
interface: 'zwp_text_input_manager_v3',    version: 1, name: 21
interface: 'zwp_input_method_manager_v2',  version: 1, name: 28

$ strings /usr/lib/ibus/ibus-wayland | grep zwp_input_method
zwp_input_method_manager_v2
zwp_input_method_v2
...

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 20 defaults = 40 ms/char).

Caveat worth documenting either way: 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 — 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 to auto with 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_wtype blanket-mocks subprocess.run to returncode=0, which the probe reads as "bridge found".
  • test_unbridged_wayland_skips_ibus_when_engine_is_xkb shells 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 the VOCALINUX_FORCE_BACKEND parsing matrix. No new flake8 findings and no test failures unique to this branch against main.

…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
@github-actions github-actions Bot added app Core Python application (src, packaging) tests Test suite changes labels Jul 28, 2026
@codecov

codecov Bot commented Jul 28, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 85.85%. Comparing base (4fad897) to head (c9501d1).
⚠️ Report is 1 commits behind head on main.

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              
Files with missing lines Coverage Δ
src/vocalinux/text_injection/text_injector.py 88.67% <100.00%> (+0.47%) ⬆️
🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

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.
@VocaHQ VocaHQ deleted a comment from cursor Bot Jul 28, 2026

@jatinkrmalik jatinkrmalik left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@jatinkrmalik
jatinkrmalik merged commit 161129c into VocaHQ:main Jul 28, 2026
15 checks passed
@cursor cursor Bot mentioned this pull request Jul 28, 2026
6 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

app Core Python application (src, packaging) tests Test suite changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FEATURE] Allow IBus on wlroots compositors when ibus-wayland (input-method-v2) is running

2 participants