feat(linux): enable Wayland window/display recording with PipeWire cursor metadata and evdev clicks - #2420
Conversation
| #[cfg(target_os = "linux")] | ||
| let current_keys = if let Some(evdev) = &evdev_listener { | ||
| let k = evdev.get_keys(); | ||
| if !k.is_empty() || device_state.get_keys().is_empty() { | ||
| k | ||
| } else { | ||
| device_state.get_keys() | ||
| } | ||
| } else { | ||
| device_state.get_keys() | ||
| }; |
There was a problem hiding this comment.
Keys leak outside recorded window When recording a Wayland window, the evdev listener reads keys from all accessible input devices, but this path does not check whether the selected window has focus. If the user types in another application, those keystrokes can be saved in the recording’s keyboard events, including sensitive input.
How this was verified: Device-wide key events flow through get_keys() into the recording’s keyboard-event writer without a window-scope check.
Prompt To Fix With AI
This is a comment left during a code review.
Path: crates/recording/src/cursor.rs
Line: 542-552
Comment:
**Keys leak outside recorded window** When recording a Wayland window, the evdev listener reads keys from all accessible input devices, but this path does not check whether the selected window has focus. If the user types in another application, those keystrokes can be saved in the recording’s keyboard events, including sensitive input.
**How this was verified:** Device-wide key events flow through `get_keys()` into the recording’s keyboard-event writer without a window-scope check.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.| let mouse_buttons: [bool; 6] = if let Some(evdev) = &evdev_listener { | ||
| let in_window = target.window.is_none() | ||
| || crate::sources::screen_capture::WAYLAND_CURSOR_POSITION | ||
| .read() | ||
| .is_some(); | ||
| if in_window { | ||
| evdev.get_buttons() | ||
| } else { | ||
| [false; 6] | ||
| } |
There was a problem hiding this comment.
X11 window clicks disappear For an X11 window recording with an accessible evdev device, this gate accepts mouse buttons only when Wayland cursor metadata is present. An X11 capture does not provide that metadata, so clicks are omitted even when the pointer is in the recorded window.
Prompt To Fix With AI
This is a comment left during a code review.
Path: crates/recording/src/cursor.rs
Line: 498-507
Comment:
**X11 window clicks disappear** For an X11 window recording with an accessible evdev device, this gate accepts mouse buttons only when Wayland cursor metadata is present. An X11 capture does not provide that metadata, so clicks are omitted even when the pointer is in the recorded window.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.| let target_width = ensure_even(size.width); | ||
| let target_height = ensure_even(size.height); | ||
| if target_width > 0 && target_height > 0 { | ||
| let actual_info = VideoInfo::from_raw_ffmpeg( | ||
| pixel_format, | ||
| target_width, | ||
| target_height, | ||
| state.video_info.fps(), | ||
| ); | ||
| state.video_info = actual_info; | ||
| state.notify_ready(actual_info); |
There was a problem hiding this comment.
Resized frames reach old encoder If PipeWire negotiates a new format after a window resize, this updates the frame dimensions, but the muxer was initialized with the dimensions from the first negotiation. Frames at the new size can then reach an encoder expecting the old size, causing encoding failures or lost frames.
Prompt To Fix With AI
This is a comment left during a code review.
Path: crates/recording/src/sources/screen_capture/linux.rs
Line: 752-762
Comment:
**Resized frames reach old encoder** If PipeWire negotiates a new format after a window resize, this updates the frame dimensions, but the muxer was initialized with the dimensions from the first negotiation. Frames at the new size can then reach an encoder expecting the old size, causing encoding failures or lost frames.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.| if wayland { | ||
| return false; | ||
| } |
There was a problem hiding this comment.
Wayland clean capture disabled This early return disables clean capture for Wayland display and area recordings that previously used it, so the application’s own windows can remain visible in recordings. The existing test still expects clean capture for both targets on Wayland and will fail.
Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/desktop-gpui/src/app_windows.rs
Line: 2797-2799
Comment:
**Wayland clean capture disabled** This early return disables clean capture for Wayland display and area recordings that previously used it, so the application’s own windows can remain visible in recordings. The existing test still expects clean capture for both targets on Wayland and will fail.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.| let event = unsafe { | ||
| &*(buf.as_ptr().add(i * event_size) as *const InputEvent) | ||
| }; |
There was a problem hiding this comment.
Input events read unaligned The read buffer is a byte array, which is not guaranteed to meet
InputEvent’s alignment requirement. Casting its address to an InputEvent reference is undefined behavior and can fault on strict-alignment Linux targets.
Prompt To Fix With AI
This is a comment left during a code review.
Path: crates/recording/src/evdev_input.rs
Line: 130-132
Comment:
**Input events read unaligned** The read buffer is a byte array, which is not guaranteed to meet `InputEvent`’s alignment requirement. Casting its address to an `InputEvent` reference is undefined behavior and can fault on strict-alignment Linux targets.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.| .detach(); | ||
| return; | ||
| } | ||
| let source = resolve_cap_project_path(&source).unwrap_or(source); |
There was a problem hiding this comment.
Selected MP4 becomes bundle This resolves a selected file to its containing
.cap project before checking whether the file is an MP4. Selecting an MP4 inside a bundle therefore imports the entire bundle instead of the selected video.
Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/desktop-gpui/src/editor_clips.rs
Line: 1576
Comment:
**Selected MP4 becomes bundle** This resolves a selected file to its containing `.cap` project before checking whether the file is an MP4. Selecting an MP4 inside a bundle therefore imports the entire bundle instead of the selected video.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.…rsor metadata and evdev click capture
a79d74c to
7369cb7
Compare
Fixes #2419
Overview
This PR addresses several pain points for Linux Wayland users, enabling high-fidelity window recording (especially non-16:9 aspect ratios like mobile emulators), restoring cursor & click telemetry on Wayland, and fixing
.capproject bundle imports.Key Changes
1. PipeWire Window Format & Aspect Ratio Negotiation
WaylandCaptureInitoneshot notification to wait for PipeWire's negotiated format before configuring the output pipeline, passing actual window dimensions (target_width,target_height) to FFmpeg rather than forcing display resolution.capture_pipeline.rs, preventing non-16:9 windows from stretching into 16:9.2. DMA-BUF Memory Synchronization
DMA_BUF_IOCTL_SYNC(START/END) viaDmaBufGuardto ensure CPU cache coherency when reading GPU buffers, preventing tearing and stale buffer reads.3. Wayland Cursor & Click Capture
SPA_META_Cursor: Extracted normalized cursor position from PipeWire buffer metadata instead of stubbing cursor events on Wayland.evdev: ImplementedEvdevInputListenerincrates/recording/src/evdev_input.rsto read mouse buttons and keystrokes directly from/dev/input/event*devices, with graceful fallback todevice_state.4.
.capBundle Import Resolution (editor_clips.rs&platform.rs).capdirectories instead of selecting them.resolve_cap_project_pathwhich traversespath.ancestors()to locate the project root (checking forrecording-meta.json), allowing users to pick any file inside the bundle (orrecording-meta.json) to import the project.jsonand wildcard filters to the portal open panel.Testing
.capprojects via the file chooser dialog.The PR is not safe to merge until the input-scope, X11 click, resized-window, clean-capture, and unsafe event-read issues are addressed.
Findings
Fix with agent prompt
Summary
The PR adds negotiated-size Wayland recording, PipeWire cursor metadata, evdev input capture, and
.capbundle import resolution. Review found regressions in input scoping, X11 clicks, resized-window encoding, and Wayland clean capture, plus an unsafe evdev read.Reviews (1) · Last reviewed commit: "feat(linux): enable Wayland window/displ..."