Use scap library to implement screensharing on X11 (#27807)

While `scap` does have support for Wayland and Windows, but haven't seen
screensharing work properly there yet. So for now just adding support
for X11 screensharing.

WIP branches for enabling wayland and windows support:

* https://github.com/zed-industries/zed/tree/wayland-screenshare
* https://github.com/zed-industries/zed/tree/windows-screenshare


Release Notes:

- Added support for screensharing on X11 (Linux)

---------

Co-authored-by: Conrad <conrad@zed.dev>
Co-authored-by: Mikayla <mikayla@zed.dev>
Co-authored-by: Junkui Zhang <364772080@qq.com>
This commit is contained in:
Michael Sloan
2025-04-04 21:31:03 +00:00
committed by GitHub
co-authored by Conrad Mikayla Junkui Zhang
parent 7bc62de267
commit c2afc2271b
20 changed files with 624 additions and 49 deletions
@@ -1,13 +1,16 @@
use std::cell::RefCell;
use std::rc::Rc;
use anyhow::anyhow;
use calloop::{EventLoop, LoopHandle};
use futures::channel::oneshot;
use util::ResultExt;
use crate::platform::linux::LinuxClient;
use crate::platform::{LinuxCommon, PlatformWindow};
use crate::{AnyWindowHandle, CursorStyle, DisplayId, PlatformDisplay, WindowParams};
use crate::{
AnyWindowHandle, CursorStyle, DisplayId, PlatformDisplay, ScreenCaptureSource, WindowParams,
};
pub struct HeadlessClientState {
pub(crate) _loop_handle: LoopHandle<'static, HeadlessClient>,
@@ -63,6 +66,21 @@ impl LinuxClient for HeadlessClient {
None
}
fn is_screen_capture_supported(&self) -> bool {
false
}
fn screen_capture_sources(
&self,
) -> oneshot::Receiver<anyhow::Result<Vec<Box<dyn ScreenCaptureSource>>>> {
let (mut tx, rx) = oneshot::channel();
tx.send(Err(anyhow!(
"Headless mode does not support screen capture."
)))
.ok();
rx
}
fn active_window(&self) -> Option<AnyWindowHandle> {
None
}
+10 -3
View File
@@ -28,6 +28,7 @@ use crate::{
Pixels, Platform, PlatformDisplay, PlatformTextSystem, PlatformWindow, Point, Result,
ScreenCaptureSource, Task, WindowAppearance, WindowParams, px,
};
#[cfg(any(feature = "wayland", feature = "x11"))]
pub(crate) const SCROLL_LINES: f32 = 3.0;
@@ -50,6 +51,10 @@ pub trait LinuxClient {
#[allow(unused)]
fn display(&self, id: DisplayId) -> Option<Rc<dyn PlatformDisplay>>;
fn primary_display(&self) -> Option<Rc<dyn PlatformDisplay>>;
fn is_screen_capture_supported(&self) -> bool;
fn screen_capture_sources(
&self,
) -> oneshot::Receiver<Result<Vec<Box<dyn ScreenCaptureSource>>>>;
fn open_window(
&self,
@@ -230,12 +235,14 @@ impl<P: LinuxClient + 'static> Platform for P {
self.displays()
}
fn is_screen_capture_supported(&self) -> bool {
self.is_screen_capture_supported()
}
fn screen_capture_sources(
&self,
) -> oneshot::Receiver<Result<Vec<Box<dyn ScreenCaptureSource>>>> {
let (mut tx, rx) = oneshot::channel();
tx.send(Err(anyhow!("screen capture not implemented"))).ok();
rx
self.screen_capture_sources()
}
fn active_window(&self) -> Option<AnyWindowHandle> {
@@ -7,6 +7,7 @@ use std::{
time::{Duration, Instant},
};
use anyhow::anyhow;
use calloop::{
EventLoop, LoopHandle,
timer::{TimeoutAction, Timer},
@@ -14,7 +15,7 @@ use calloop::{
use calloop_wayland_source::WaylandSource;
use collections::HashMap;
use filedescriptor::Pipe;
use futures::channel::oneshot;
use http_client::Url;
use smallvec::SmallVec;
use util::ResultExt;
@@ -85,7 +86,8 @@ use crate::{
FileDropEvent, ForegroundExecutor, KeyDownEvent, KeyUpEvent, Keystroke, LinuxCommon, Modifiers,
ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseExitEvent, MouseMoveEvent,
MouseUpEvent, NavigationDirection, Pixels, PlatformDisplay, PlatformInput, Point, SCROLL_LINES,
ScaledPixels, ScrollDelta, ScrollWheelEvent, Size, TouchPhase, WindowParams, point, px, size,
ScaledPixels, ScreenCaptureSource, ScrollDelta, ScrollWheelEvent, Size, TouchPhase,
WindowParams, point, px, size,
};
/// Used to convert evdev scancode to xkb scancode
@@ -633,6 +635,24 @@ impl LinuxClient for WaylandClient {
None
}
fn is_screen_capture_supported(&self) -> bool {
false
}
fn screen_capture_sources(
&self,
) -> oneshot::Receiver<anyhow::Result<Vec<Box<dyn ScreenCaptureSource>>>> {
// TODO: Get screen capture working on wayland. Be sure to try window resizing as that may
// be tricky.
//
// start_scap_default_target_source()
let (sources_tx, sources_rx) = oneshot::channel();
sources_tx
.send(Err(anyhow!("Wayland screen capture not yet implemented.")))
.ok();
sources_rx
}
fn open_window(
&self,
handle: AnyWindowHandle,
+15 -4
View File
@@ -1,3 +1,4 @@
use crate::platform::scap_screen_capture::scap_screen_sources;
use core::str;
use std::{
cell::RefCell,
@@ -8,13 +9,13 @@ use std::{
time::{Duration, Instant},
};
use anyhow::Context as _;
use calloop::{
EventLoop, LoopHandle, RegistrationToken,
generic::{FdWrapper, Generic},
};
use anyhow::Context as _;
use collections::HashMap;
use futures::channel::oneshot;
use http_client::Url;
use smallvec::SmallVec;
use util::ResultExt;
@@ -59,8 +60,8 @@ use crate::platform::{
use crate::{
AnyWindowHandle, Bounds, ClipboardItem, CursorStyle, DisplayId, FileDropEvent, Keystroke,
Modifiers, ModifiersChangedEvent, MouseButton, Pixels, Platform, PlatformDisplay,
PlatformInput, Point, RequestFrameOptions, ScaledPixels, ScrollDelta, Size, TouchPhase,
WindowParams, X11Window, modifiers_from_xinput_info, point, px,
PlatformInput, Point, RequestFrameOptions, ScaledPixels, ScreenCaptureSource, ScrollDelta,
Size, TouchPhase, WindowParams, X11Window, modifiers_from_xinput_info, point, px,
};
/// Value for DeviceId parameters which selects all devices.
@@ -1327,6 +1328,16 @@ impl LinuxClient for X11Client {
))
}
fn is_screen_capture_supported(&self) -> bool {
true
}
fn screen_capture_sources(
&self,
) -> oneshot::Receiver<anyhow::Result<Vec<Box<dyn ScreenCaptureSource>>>> {
scap_screen_sources(&self.0.borrow().common.foreground_executor)
}
fn open_window(
&self,
handle: AnyWindowHandle,