fix(cua-driver): make GNOME/KDE Wayland input actually land via libei (#2105, #1982)#2112
fix(cua-driver): make GNOME/KDE Wayland input actually land via libei (#2105, #1982)#2112ai-ag2026 wants to merge 7 commits into
Conversation
|
@ai-ag2026 is attempting to deploy a commit to the Cua Team on Vercel. A member of the Team first needs to authorize it. |
|
Important Review skippedAuto incremental reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
📝 WalkthroughWalkthroughAdds a portal/libei RemoteDesktop reachability probe integrated into Wayland health-check classification, and introduces a libei-based input fallback path (pointer clicks, scroll, drag, move, typing, hotkeys) used when the wlroots virtual-pointer manager is unavailable, gated by a new ChangesWayland libei/portal fallback
Estimated code review effort: 4 (Complex) | ~60 minutes Sequence Diagram(s)sequenceDiagram
participant HealthReport as check_wayland_backend
participant Probe as probe_portal_remote_desktop
participant Ashpd as ashpd RemoteDesktop
participant Classifier as classify_wayland_backend
HealthReport->>Probe: run probe (portal-libei feature)
Probe->>Ashpd: RemoteDesktop::new()
Ashpd-->>Probe: reachable / not-found error
Probe-->>HealthReport: portal_libei_backend bool
HealthReport->>Classifier: classify(virtual_pointer, portal_libei_backend, globals)
Classifier-->>HealthReport: pass / fail with hint
sequenceDiagram
participant Caller
participant WithLibeiFallback as with_libei_fallback
participant VptrImpl as click_vptr/scroll_vptr/...
participant LibeiImpl as libei_click/libei_scroll/...
Caller->>WithLibeiFallback: click/scroll/move/drag
WithLibeiFallback->>VptrImpl: try wlroots vptr operation
VptrImpl-->>WithLibeiFallback: NO_VPTR_MARKER error (if missing)
WithLibeiFallback->>LibeiImpl: fallback (portal-libei enabled)
LibeiImpl-->>Caller: result
Possibly related issues
Possibly related PRs
Suggested reviewers: Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@libs/cua-driver/rust/crates/platform-linux/src/health_report.rs`:
- Around line 522-524: The readiness check in RemoteDesktop::new() is too
shallow because it only confirms the portal proxy exists, not that the libei
input path is usable. Update the health probe around the existing rt.block_on
match so it exercises the full RemoteDesktop handshake by continuing through
create_session, select_devices, start, and connect_to_eis, or else change the
reported verdict/message to indicate only “RemoteDesktop portal reachable”
rather than full input backend readiness. Use the RemoteDesktop::new flow and
the surrounding health_report logic to locate the fix.
In `@libs/cua-driver/rust/crates/platform-linux/src/wayland/mod.rs`:
- Around line 1413-1495: The libei-backed input helpers are still treating
missing EIS capabilities as success, so requests can disappear silently. Update
the capability checks in the libei path so the relevant operations fail when the
negotiated device lacks PointerAbsolute, Button, Scroll, Text, or Keyboard, and
make sure the callers such as libei_click, libei_scroll, libei_move_absolute,
libei_type_text, and libei_press_key propagate that error instead of returning
Ok(()).
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 0177a824-f19a-47fb-bd81-b1ecd81b68cc
📒 Files selected for processing (2)
libs/cua-driver/rust/crates/platform-linux/src/health_report.rslibs/cua-driver/rust/crates/platform-linux/src/wayland/mod.rs
|
Addressed the two CodeRabbit findings in
Verification run in a clean cargo test -q -p platform-linux --features portal-libei health_report::tests -- --nocapture
cargo check -q -p cua-driver --features portal-libei
cargo check -q -p cua-driver
git diff --checkAll passed. Existing warnings are pre-existing in adjacent code. Vercel is still blocked by deploy authorization, not by this code path. |
trycua#2112 as filed only fixed doctor reporting + libei readiness; input still never reached libei on GNOME/Mutter. This makes click/scroll/move/keys actually land. Chain of fixes, each verified on a real Ubuntu 26.04 GNOME/Wayland session: 1. Routing gate: input tools gated the Wayland branch on `is_wayland()`, which requires DISPLAY unset. GNOME/KDE always run XWayland -> DISPLAY is always set -> libei path was dead, input fell to X11 XTEST (dropped by Mutter). Add `wayland_input_enabled()` (opted in + WAYLAND_DISPLAY, no DISPLAY-unset clause) and route the 9 input tool sites through it. Capture / list_windows keep `is_wayland()` (native enum needs wlr-foreign-toplevel, absent on Mutter). 2. EIS handshake: the client advertised ei_device v2 and omitted ei_pingpong and Ping handling, so Mutter closed the EIS connection immediately after our handshake finish() (one Handshake event, then read() error). Match reis 0.7's own examples: ei_device v1, advertise ei_pingpong, answer ei_connection Ping with done(). The handshake now negotiates devices through to Resumed. 3. Portal session lifetime (trycua#2105): open_eis_context dropped the ashpd proxy/session + tokio runtime before the calloop loop started, so GNOME/KDE tore the RemoteDesktop+EIS session (and the fd) down at once. Hold them for the libei worker lifetime; use a multi-thread(1) runtime so the zbus connection stays driven. 4. Command readiness: commands could run before seat->device->Resumed completed and fail "no EIS device negotiated yet". Queue commands and run them once a device is negotiated (READY_TIMEOUT bound so callers never hang). 5. Device selection: Mutter/KWin announce one device per capability group (relative pointer, absolute pointer, keyboard). Pick the device that carries the interface a command needs (device_with_interface) instead of "first virtual device". 6. Keyboard typing: Mutter doesn't advertise ei_text, so type_text via ei_text fails. Fall back to keycode typing via ei_keyboard (US-layout char->evdev + shift). Layout-correct typing (compositor xkb keymap) is a follow-up; non-US layouts mistype layout-specific punctuation. Verified: click lands (OK in ~3ms), type_text lands ("CUA_FIX_OK_5566" typed into a focused entry; letters/digits correct, '_' mistyped on a de keymap per the US-layout limitation above). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
@f-trycua — this PR (and #2111) are fork PRs, so their workflow runs (incl. the blocking This one now makes GNOME/KDE Wayland input actually land via libei — it fixes the EIS handshake Mutter was rejecting, the |
12fa9ab to
889a648
Compare
Fallback from wlroots virtual-pointer to the portal/libei backend when a compositor does not advertise zwlr_virtual_pointer, and teach doctor to treat a reachable RemoteDesktop portal as the GNOME/KDE input backend instead of reporting a wlroots-only failure.
trycua#2112 as filed only fixed doctor reporting + libei readiness; input still never reached libei on GNOME/Mutter. This makes click/scroll/move/keys actually land. Chain of fixes, each verified on a real Ubuntu 26.04 GNOME/Wayland session: 1. Routing gate: input tools gated the Wayland branch on `is_wayland()`, which requires DISPLAY unset. GNOME/KDE always run XWayland -> DISPLAY is always set -> libei path was dead, input fell to X11 XTEST (dropped by Mutter). Add `wayland_input_enabled()` (opted in + WAYLAND_DISPLAY, no DISPLAY-unset clause) and route the 9 input tool sites through it. Capture / list_windows keep `is_wayland()` (native enum needs wlr-foreign-toplevel, absent on Mutter). 2. EIS handshake: the client advertised ei_device v2 and omitted ei_pingpong and Ping handling, so Mutter closed the EIS connection immediately after our handshake finish() (one Handshake event, then read() error). Match reis 0.7's own examples: ei_device v1, advertise ei_pingpong, answer ei_connection Ping with done(). The handshake now negotiates devices through to Resumed. 3. Portal session lifetime (trycua#2105): open_eis_context dropped the ashpd proxy/session + tokio runtime before the calloop loop started, so GNOME/KDE tore the RemoteDesktop+EIS session (and the fd) down at once. Hold them for the libei worker lifetime; use a multi-thread(1) runtime so the zbus connection stays driven. 4. Command readiness: commands could run before seat->device->Resumed completed and fail "no EIS device negotiated yet". Queue commands and run them once a device is negotiated (READY_TIMEOUT bound so callers never hang). 5. Device selection: Mutter/KWin announce one device per capability group (relative pointer, absolute pointer, keyboard). Pick the device that carries the interface a command needs (device_with_interface) instead of "first virtual device". 6. Keyboard typing: Mutter doesn't advertise ei_text, so type_text via ei_text fails. Fall back to keycode typing via ei_keyboard (US-layout char->evdev + shift). Layout-correct typing (compositor xkb keymap) is a follow-up; non-US layouts mistype layout-specific punctuation. Verified: click lands (OK in ~3ms), type_text lands ("CUA_FIX_OK_5566" typed into a focused entry; letters/digits correct, '_' mistyped on a de keymap per the US-layout limitation above). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The keycode typing fallback (for compositors without ei_text, e.g. Mutter) assumed a US layout, so layout-specific characters mistyped on other keymaps — e.g. '_' -> '?' and swapped Y/Z on a German QWERTZ. Read the xkb keymap the ei_keyboard device delivers (Keymap event) and build a char -> (evdev keycode, shift) table from it via xkbcommon, so typing follows the user's actual layout. Falls back to the US-layout table when no keymap arrives. Verified on a real German (QWERTZ) GNOME/Wayland session: "test_YZ_yz_123" types verbatim (previously '_' and Y/Z were wrong). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The libei fallback previously hard-errored on drag, claiming no button-hold primitive. But ei_button exposes separate Press/Released states (already used for click), so a genuine press -> interpolated motion -> release drag is possible. Add a Drag command to the libei worker (motion_absolute to start, button Press, interpolate to end, button Release), a libei::drag() entry point, and route wayland::drag's libei fallback + the drag tool site through it on XWayland-co-present sessions. Verified on Ubuntu 26.04 GNOME/Wayland: dragging across a pre-filled entry selects the text (typing a replacement char then replaces the whole selection -- field went from "XXXXXXXXXX" to "Z"). mouse_button_down/up (button held across separate tool calls) and parallel_mouse_drag (MPX) remain deferred -- those need cross-call emulation state / multiple pointers, which single-shot drag doesn't. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adversarial review of the libei input path before maintainer review: - libei drag: resolve the drag END into the start device's region (was: reuse the start region offset for both, mis-mapping a cross-monitor drag). Clamp interpolation steps (<=500) so a huge count can't block the worker loop / starve EIS Ping handling. Clamp endpoints without normalize_click_xy's (0,0)->centre convention (a drag corner target is valid). - Command readiness is now per-command (cmd_ready): a keyboard-only session no longer makes keyboard commands wait 20s for an absolute-pointer device that never arrives (was one global ei_pointer_absolute gate). - Route hotkey, the cursor-glide targets, and move_cursor's real-warp through wayland_input_enabled() too -- they were left on is_wayland() and so silently took the (Mutter-ignored) X11 path / desynced the cursor overlay on GNOME/KDE. - Cap the xkb keymap size read from the ei_keyboard Keymap event (16 MiB) to avoid a multi-GB alloc on a buggy size. - Log (not silently drop) characters with no keycode in the active keymap (e.g. non-Latin/emoji) in the keycode typing fallback. Verified drag-select still lands after the changes. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ty only Address CodeRabbit: the `pass` verdict is driven by a shallow `RemoteDesktop::new()` proxy-reachability probe, not the full create_session → select_devices → start → connect_to_eis handshake (which is deliberately not run in `doctor`, to avoid a consent prompt on every health check). Spell that out in the message so a green `wayland_backend` isn't read as a guarantee that injection will succeed. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
6a8aa04 to
46c529e
Compare
|
Rebased this PR onto current Local verification after rebase: git diff --check origin/main...HEAD
cargo test -q -p platform-linux --features portal-libei health_report::tests -- --nocapture
cargo check -q -p cua-driver --features portal-libei
cargo check -q -p cua-driverAll passed. The Rust commands still emit the existing adjacent warnings, but no errors. |
Summary
Makes synthetic input actually land on GNOME/Mutter (and KDE/KWin) Wayland.
The first revision of this PR fixed doctor reporting + libei readiness but not
dispatch — input still silently no-oped on the compositors it claimed to
support. This revision fixes the whole chain, verified end-to-end on a real
Ubuntu 26.04 GNOME/Wayland session (
clicklands in ~3 ms;type_textlandsin a focused entry).
What was broken (root causes, in order)
is_wayland(),which requires
DISPLAYunset. GNOME/KDE always run XWayland →DISPLAYset →
is_wayland()false → input fell to X11 XTEST (dropped by Mutter),while
doctorreportedwayland_backend: pass. Silent false-success.ei_devicev2, omittedei_pingpong, and never answeredPing. Mutter closed the EIS connectionimmediately after our
finish()(confirmed: oneHandshakeevent, thenread()errors).open_eis_contextdropped theashpd proxy/session + runtime before the EIS loop ran.
Resumed.type_textviaei_textfails.Changes
wayland_input_enabled()predicate + 9 input call sites (capture /list_windowskeep
is_wayland()— native enum needswlr-foreign-toplevel, absent on Mutter).ei_devicev1, advertiseei_pingpong, answerPing— matchesreis 0.7
examples/type-text.rs.RemoteDesktopsession + runtime for the worker lifetime(multi-thread(1) runtime so the zbus connection stays driven).
input_ready+ timeout).device_with_interface(...)selects the device carrying the needed interface.ei_keyboardwhenei_textis absent, mappedthrough the compositor's actual xkb keymap (read from the
ei_keyboardKeymapevent viaxkbcommon) so typing is layout-correct; a US-layout tableis the fallback when no keymap arrives.
drag(press → interpolated motion → release) viaei_buttonPress/Released — text selection / drag-and-drop / sliders.
Validation (Ubuntu 26.04, Mutter, Wayland;
--features portal-libei)finish())click/scroll/moveclickOK ~3 ms)press_keytype_text(incl. layout-specific chars)test_YZ_yz_123typed verbatim)drag(select / drag-and-drop)XXXXXXXXXX→Z)wayland_backendHonest deferrals (documented, not silently broken)
ei_text: used when the compositor advertises it (libei 1.6+); Mutterdoesn't, hence the keycode + xkb-keymap fallback.
mouse_button_down/mouse_button_up,the persistent-cursor
mouse_drag) andparallel_mouse_drag(multi-pointer /MPX) stay on the X11 path — they need cross-call emulation state / multiple
pointers the single-shot libei command model doesn't provide, so on native
Wayland windows they no-op (they still work on XWayland windows). Single-shot
drag(above) does work via libei.hotkeynow routes via libei; true modifierchords (Ctrl+C, …) aren't wired into the libei keyboard yet and error loudly
rather than silently mis-firing.
substitute is
delivery_mode:"foreground".Refs
Closes the runtime half of #1982 / #2105. Pairs with #2111 (publishes the
portal-libeiartifact users need). Builds on #1966 / #2008 (libei machinery).