feat(text_injection): add RemoteDesktop portal injection backend (#885)

* feat(text_injection): add RemoteDesktop portal injection backend

Wayland offers exactly one sanctioned key-injection path:
org.freedesktop.portal.RemoteDesktop's NotifyKeyboardKeysym. It reaches
native clients on compositors with no input-method-v2 bridge, works from
inside the Flatpak sandbox without /dev/uinput or a helper daemon, and
delivers keysyms so non-US layouts survive -- unlike ydotool's evdev
keycodes.

RemoteDesktopPortal is a synchronous client over a dedicated worker
thread holding a private bus connection and GLib context, which keeps
Request::Response delivery deterministic. Sessions are created lazily on
first use so startup probing only reads the interface version; v2 portal
sessions persist a restore token (mode 0600 under data_dir) so the
permission prompt appears once.

Backend selection now prefers the portal on Wayland: it is probed at
runtime ahead of ydotool/wtype/xdotool in autodetection, is pinnable via
VOCALINUX_FORCE_BACKEND=portal or text_injection.backend, and demotes to
the previous order on runtime failure. Flatpak still needs --device=all
for evdev hotkeys, but uinput is only a fallback path now.

Closes #750

* fix(text_injection): resolve Greptile findings on RemoteDesktop portal injection

- Move persist_mode/restore_token to SelectDevices options (portal v2
  sends them there, not CreateSession); a bad token is ignored rather
  than sinking the session, so drop the stale-token retry paths that
  could issue a second Start after a user cancel
- Track delivered units on RemoteDesktopPortalError so Wayland
  fallbacks retry only the undelivered tail instead of retyping the
  prefix (text chars, backspace taps, shortcut steps)
- Give queued portal jobs a single deadline budget across
  CreateSession/SelectDevices/Start waits; on submit timeout the job
  is cancelled via a Gio.Cancellable plus abort checkpoints so the
  worker cannot keep emitting keys after the caller gave up
- Close sessions whose Start never succeeded
- Release held shortcut modifiers on mid-step failure
- Skip the portal probe when an installed wtype/ydotool backend is
  explicitly pinned
- Type annotations on the new signatures and touched tests

---------

Co-authored-by: Jatin K Malik <jatinkrmalik@gmail.com>
This commit is contained in:
devin-ai-integration[bot]
2026-09-29 23:55:27 -07:00
committed by GitHub
co-authored by Jatin K Malik
parent 24fddc4344
commit f36c3f6ae5
7 changed files with 1733 additions and 25 deletions
+14 -6
View File
@@ -244,13 +244,19 @@ Pin the backend explicitly in `~/.config/vocalinux/config.json`:
|---|---|
| `auto` | Autodetect (default; autodetection may select IBus) |
| `ibus` | IBus input method; on Wayland, bypasses compositor checks and may silently do nothing in native Wayland apps |
| `portal` | RemoteDesktop portal (Wayland; the sandboxed path, asks for permission once, works under Flatpak) |
| `wtype` | wtype virtual keyboard (Wayland) |
| `ydotool` | ydotool uinput (Wayland; needs `ydotoold`) |
| `xdotool` | xdotool (X11). On Wayland it only turns IBus off -- the Wayland tool is still picked automatically |
The setting takes effect on the next start. `auto` leaves normal autodetection
in place and may select IBus. An explicit non-IBus pin (`wtype`, `ydotool`, or
`xdotool`) skips IBus selection.
in place and may select IBus. An explicit non-IBus pin (`portal`, `wtype`,
`ydotool`, or `xdotool`) skips IBus selection.
On Wayland, when IBus is not selected, autodetection tries the RemoteDesktop
portal first -- it is the only injection path Wayland officially supports and
it needs no uinput device or helper daemon -- then `ydotool`, `wtype`, and
finally `xdotool` under XWayland.
On X11 the injection tool is `xdotool` regardless of which non-`ibus` value you
pin, so `xdotool` is the name to use there when IBus is unreliable in a
@@ -271,7 +277,9 @@ it does not prove the backend was available or that text reached the focused
application. Later logs identify a fallback when a pin was not applied.
If a pinned tool is unavailable, Vocalinux warns and continues with its normal
fallback selection. `ydotool` also needs a usable `/dev/uinput` and a working
`ydotoold` setup. `xdotool` types into X11/XWayland windows, not native Wayland
windows. A live test in the target application is still the final confirmation
that text is delivered.
fallback selection. `portal` needs a desktop implementing the
`org.freedesktop.portal.RemoteDesktop` interface (GNOME and KDE Plasma do;
most wlroots compositors do not). `ydotool` also needs a usable `/dev/uinput`
and a working `ydotoold` setup. `xdotool` types into X11/XWayland windows,
not native Wayland windows. A live test in the target application is still the
final confirmation that text is delivered.