Skip to content

Cap Linux support#1604

Draft
richiemcilroy wants to merge 19 commits intomainfrom
cursor/cap-linux-support-8d37
Draft

Cap Linux support#1604
richiemcilroy wants to merge 19 commits intomainfrom
cursor/cap-linux-support-8d37

Conversation

@richiemcilroy
Copy link
Member

Add initial Linux support to enable compilation and screen/audio recording.


Open in Cursor Open in Web

cursoragent and others added 7 commits February 16, 2026 12:50
- cap-project: Add Linux variant to Platform enum
- cap-cursor-info: Add CursorShapeLinux enum with X11 cursor mappings
- scap-targets: Implement Linux display/window enumeration via X11/XRandR
- cap-cursor-capture: Add Linux cursor position normalization paths

Co-authored-by: Richie McIlroy <richiemcilroy@users.noreply.github.com>
- cap-camera: Add Linux V4L2 camera backend with device enumeration
- cap-camera-ffmpeg: Add Linux frame-to-FFmpeg conversion
- Remove cfg gate that excluded Linux from camera crate

Co-authored-by: Richie McIlroy <richiemcilroy@users.noreply.github.com>
- screen_capture/linux.rs: FFmpeg x11grab screen capture with PulseAudio system audio
- capture_pipeline.rs: Linux MakeCapturePipeline using FFmpeg muxers
- output_pipeline/linux.rs: Linux NativeCameraFrame type
- studio_recording.rs: Linux segment pipeline creation paths
- instant_recording.rs: Linux instant recording paths
- cursor.rs: Linux cursor capture via XFixes
- screenshot.rs: Linux scale factor
- scap-ffmpeg: Linux video frame type
- timestamp: Linux from_cpal fallback
- feeds/camera.rs: Split Windows/Linux camera setup

Co-authored-by: Richie McIlroy <richiemcilroy@users.noreply.github.com>
- windows.rs: Add Linux window creation, dark mode detection, display intersect
- thumbnails/mod.rs: Add Linux thumbnail stubs (no-op for now)
- diagnostics.rs: Add Linux system diagnostics collection
- Install webkit2gtk-4.1-dev for Tauri Linux support
- Add Linux InProgressRecording window builder

Co-authored-by: Richie McIlroy <richiemcilroy@users.noreply.github.com>
- diagnostics.rs: Add Linux system diagnostics module
- windows.rs: Add Linux dark mode detection, window builders, display intersect
- screenshot.rs: Add Linux scale factor
- Fix libstdc++ linking for whisper-rs on Linux
- Format all Rust code

Co-authored-by: Richie McIlroy <richiemcilroy@users.noreply.github.com>
- Call avdevice_register_all() before x11grab input format lookup
- Fix display URL format for x11grab (use :N.0 format)
- Recording benchmark verified: 1920x1080 H.264 MP4 output working
- Benchmark: 5s recording, 2.9ms stop/finalize time

Co-authored-by: Richie McIlroy <richiemcilroy@users.noreply.github.com>
- Remove cancellation token that killed capture thread prematurely
- Use channel closure for natural thread termination
- Use Instant-based timestamps for correct frame timing
- Benchmark results: 422 frames in 5s, 1920x1080 H.264 @ 60fps
- Valid MP4 output verified with ffprobe

Co-authored-by: Richie McIlroy <richiemcilroy@users.noreply.github.com>
@cursor
Copy link

cursor bot commented Feb 16, 2026

Cursor Agent can help with this pull request. Just @cursor in comments and I'll start working on changes in this branch.
Learn more about Cursor Agents

cursoragent and others added 6 commits February 16, 2026 13:37
- Add Linux bundle configuration (deb/appimage/rpm) in tauri.conf.json
- Add Linux platform module (platform/linux.rs)
- Add Linux window exclusion support (window_exclusion.rs)
- Add Vulkan feature for wgpu-hal on Linux (rendering/Cargo.toml)
- Fix dead code warnings in scap-targets Linux impl
- Format all code

Co-authored-by: Richie McIlroy <richiemcilroy@users.noreply.github.com>
Fixes unused import warning on Linux in rendering decoder

Co-authored-by: Richie McIlroy <richiemcilroy@users.noreply.github.com>
Co-authored-by: Richie McIlroy <richiemcilroy@users.noreply.github.com>
Co-authored-by: Richie McIlroy <richiemcilroy@users.noreply.github.com>
- Remove unused imports (FFmpegVideoFrame, VideoInfo, SinkExt, tracing)
- Fix unused variable warnings (elapsed → _elapsed)
- Gate camera-only builder behind cfg(not(linux))
- Suppress dead_code warnings on Linux platform stubs
- Remove unused glob re-export of linux platform module

Co-authored-by: Richie McIlroy <richiemcilroy@users.noreply.github.com>
- Read V4L2 device names from sysfs for better camera display names
- Check /sys/class/video4linux for valid capture devices
- Scan up to 64 video devices instead of 16
- Fix system audio timestamp to use SystemTime directly
- Remove unused Timestamp::from_duration method

Co-authored-by: Richie McIlroy <richiemcilroy@users.noreply.github.com>
&mut pid_prop,
);
if st == 0 && !pid_prop.is_null() && ni > 0 {
let p = *(pid_prop as *const u32);

Check failure

Code scanning / CodeQL

Access of invalid pointer High

This operation dereferences a pointer that may be
invalid
.

Copilot Autofix

AI 4 days ago

In general, any pointer returned from XGetWindowProperty must only be dereferenced after confirming that: (1) the call succeeded, (2) the returned pointer is non‑null, (3) the reported number of items is sufficient for the intended read, and (4) the property type/format matches what we expect for the target Rust type. After using the data, we must call XFree exactly once and never dereference the pointer again.

For this specific code, _NET_WM_PID is defined as a CARDINAL (32‑bit) property, so we should verify that at == x11::xlib::XA_CARDINAL and af == 32, and that ni >= 1, before treating pid_prop as a pointer to a u32. We should also convert the raw pointer into a Rust slice using std::slice::from_raw_parts, which both makes the length explicit and avoids manually dereferencing an arbitrary pointer. A robust change, without altering observable behavior, is therefore:

  • Change the dereference block to a nested if that:
    • checks st == 0, !pid_prop.is_null(), ni >= 1, at == x11::xlib::XA_CARDINAL, and af == 32;
    • uses std::slice::from_raw_parts(pid_prop as *const u32, ni as usize) to create a slice;
    • reads slice[0] as the PID;
    • always calls XFree once when pid_prop is non‑null, regardless of whether we ended up using the value.
  • Ensure we don’t use pid_prop after it has been freed.

These changes are entirely localized to the let pid = { ... } block around lines 608–635 in crates/scap-targets/src/platform/linux.rs and require no new imports or helper functions.

Suggested changeset 1
crates/scap-targets/src/platform/linux.rs

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/crates/scap-targets/src/platform/linux.rs b/crates/scap-targets/src/platform/linux.rs
--- a/crates/scap-targets/src/platform/linux.rs
+++ b/crates/scap-targets/src/platform/linux.rs
@@ -626,9 +626,22 @@
                     &mut pid_prop,
                 );
                 if st == 0 && !pid_prop.is_null() && ni > 0 {
-                    let p = *(pid_prop as *const u32);
-                    x11::xlib::XFree(pid_prop as *mut _);
-                    p
+                    // Validate that the returned property has the expected type and format
+                    let pid = if at == x11::xlib::XA_CARDINAL as u64 && af == 32 {
+                        // Safe because:
+                        // - `pid_prop` is non-null and comes from XGetWindowProperty
+                        // - `ni` is the number of 32-bit items
+                        let slice = unsafe {
+                            std::slice::from_raw_parts(pid_prop as *const u32, ni as usize)
+                        };
+                        slice.get(0).copied().unwrap_or(0)
+                    } else {
+                        0
+                    };
+                    unsafe {
+                        x11::xlib::XFree(pid_prop as *mut _);
+                    }
+                    pid
                 } else {
                     0
                 }
EOF
@@ -626,9 +626,22 @@
&mut pid_prop,
);
if st == 0 && !pid_prop.is_null() && ni > 0 {
let p = *(pid_prop as *const u32);
x11::xlib::XFree(pid_prop as *mut _);
p
// Validate that the returned property has the expected type and format
let pid = if at == x11::xlib::XA_CARDINAL as u64 && af == 32 {
// Safe because:
// - `pid_prop` is non-null and comes from XGetWindowProperty
// - `ni` is the number of 32-bit items
let slice = unsafe {
std::slice::from_raw_parts(pid_prop as *const u32, ni as usize)
};
slice.get(0).copied().unwrap_or(0)
} else {
0
};
unsafe {
x11::xlib::XFree(pid_prop as *mut _);
}
pid
} else {
0
}
Copilot is powered by AI and may make mistakes. Always verify output.
cursoragent and others added 6 commits February 16, 2026 14:07
Adds X11 display check and device availability reporting for Linux
in the recording test runner example.

Co-authored-by: Richie McIlroy <richiemcilroy@users.noreply.github.com>
The cpal Stream was being dropped at the end of create_system_audio_source(),
immediately stopping audio capture. Now the Stream is wrapped in an Arc and
stored in the SystemAudioSourceConfig, then moved into the async task that
forwards audio frames, keeping it alive for the entire recording duration.

Also implements a proper AudioSource trait for Linux SystemAudioSource
(matching the Windows pattern) instead of using the generic ChannelAudioSource.

Co-authored-by: Richie McIlroy <richiemcilroy@users.noreply.github.com>
Replace blank-frame stub with actual FFmpeg video4linux2 capture:
- Opens /dev/videoN via FFmpeg's v4l2 input format
- Decodes MJPEG/YUYV/raw frames from the device
- Converts to RGB24 via FFmpeg's swscale
- Passes real frames to callback at configured FPS
- Attempts FFmpeg-based format querying before falling back to defaults
- Handles stop signal for clean shutdown

Co-authored-by: Richie McIlroy <richiemcilroy@users.noreply.github.com>
Capture thumbnails using XGetImage on the X11 root window:
- Display thumbnails: capture full display region via XGetImage
- Window thumbnails: translate window coords to root and capture region
- Convert BGRA XImage data to RGBA, resize to thumbnail dimensions
- Encode as base64 PNG for the capture target picker UI
- Add x11_window_id() accessor to Linux WindowImpl
- Runs capture in spawn_blocking to avoid blocking the async runtime

Co-authored-by: Richie McIlroy <richiemcilroy@users.noreply.github.com>
Read _NET_WM_ICON X11 property to extract window application icons:
- Parse ARGB pixel data from the property (multiple sizes)
- Select the largest icon up to 256x256
- Convert ARGB to RGBA and encode as PNG
- Returns the icon data for display in the window picker

Co-authored-by: Richie McIlroy <richiemcilroy@users.noreply.github.com>
Map XFixes cursor name strings to CursorShapeLinux variants:
- Read cursor name from XFixesCursorImage struct
- Map ~40 common X11 cursor names (left_ptr, xterm, hand2, etc.)
- to their corresponding CursorShapeLinux enum variants
- Covers standard cursors: arrow, text, pointer, wait, crosshair,
  resize variants, grab/grabbing, help, progress, context-menu, etc.

Co-authored-by: Richie McIlroy <richiemcilroy@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

Comments