Skip to content

feat(injection): add selectable text injection backends - #402

Closed
sabbari wants to merge 1 commit into
VocaHQ:mainfrom
sabbari:pr/text-injection-backend
Closed

feat(injection): add selectable text injection backends#402
sabbari wants to merge 1 commit into
VocaHQ:mainfrom
sabbari:pr/text-injection-backend

Conversation

@sabbari

@sabbari sabbari commented Apr 16, 2026

Copy link
Copy Markdown

Summary

  • add a configurable text injection backend setting in the GTK settings dialog
  • expose the existing backend choices: auto, ibus, xdotool, wtype, and ydotool
  • apply backend changes immediately by rebuilding the active injector at runtime

Motivation

On X11, text injection could still route through IBus whenever it initialized successfully, even in applications where IBus was not reliable in practice, such as VS Code and some terminal contexts. This made injection behavior hard to control and hard to debug, especially because an accepted IBus request did not necessarily mean that text was actually inserted in the focused application.

Notes

  • explicit backend selections now fail clearly when they are incompatible with the current session type
  • this is especially useful on X11 setups where xdotool works better than IBus in some apps

Add a persisted text injection backend setting and expose all existing backend choices in the GTK settings dialog so users can switch between auto, IBus, xdotool, wtype, and ydotool without editing config by hand.

Make explicit backend selections strict about session compatibility, wire the saved preference into startup, and reload the active TextInjector immediately when the setting changes so switching to xdotool on X11 takes effect in the current session instead of only after restart.
@jatinkrmalik

Copy link
Copy Markdown
Member

@sabbari Thanks for this PR, I think this is useful and I’d like to get it in before the next release. I don’t have push access to your fork, so could you please rebase this branch on the latest main and resolve the merge conflicts?

Once it’s updated, I’ll prioritize the review and merge.

@jatinkrmalik jatinkrmalik self-assigned this May 7, 2026
@jatinkrmalik jatinkrmalik added the stale The pull request is stale and/or has merge conflicts. label May 7, 2026
@jatinkrmalik jatinkrmalik assigned sabbari and unassigned jatinkrmalik May 7, 2026
jatinkrmalik pushed a commit that referenced this pull request Jul 28, 2026
…land runs (#614)

* feat(text-injection): use IBus on unbridged compositors when ibus-wayland 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 (#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 #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 #476 and #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 #607

* test(text-injection): cover bridge override and forced-backend branches

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

Copy link
Copy Markdown
Member

Hey @sabbari, closing this in favor of what's already on main plus #649.

I asked for a rebase in May so we could get this into a release, but the branch hasn't moved. It's conflicting, lint has been red since April (black), and the test matrix never ran.

main already has VOCALINUX_FORCE_BACKEND (ibus / wtype / ydotool / auto) in TextInjector._forced_backend(). #649 is the persistent text_injection.backend setting, env-overrides-config, default auto. That's the narrower version of this PR (no Settings combo, no runtime rebuild).

The extra work here also doesn't switch dictation: update_text_injection_backend replaces tray.text_injector, but main() still holds the old injector on text_system / ActionHandler. And _configure_ibus() uses IBusTextInjector(auto_activate=True), which we'd have to rewrite against scoped injection (auto_activate=False) anyway.

The one thing #649 left out on purpose is pinning xdotool. If that's still needed (X11 #476, Firefox IBus), let's add it on #649. Please don't rebase this branch. Thanks for the original work.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

stale The pull request is stale and/or has merge conflicts.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants