From 7588280915f0a2651eef5028377c8a7e29b5a88a Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Thu, 10 Jul 2025 14:02:00 -0700 Subject: [PATCH] Windows screen sharing (#34223) Release Notes: - N/A --------- Co-authored-by: localcc Co-authored-by: Peter Tripp --- Cargo.lock | 4 +- Cargo.toml | 2 +- crates/gpui/Cargo.toml | 4 +- crates/gpui/src/platform.rs | 9 +++- crates/gpui/src/platform/windows.rs | 3 ++ crates/gpui/src/platform/windows/platform.rs | 6 +-- crates/livekit_client/Cargo.toml | 4 +- crates/livekit_client/src/lib.rs | 30 +++---------- .../src/livekit_client/playback.rs | 42 ++++++++++++------- tooling/workspace-hack/Cargo.toml | 4 ++ 10 files changed, 57 insertions(+), 51 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 111ed89b12..163cba778a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -14097,7 +14097,7 @@ dependencies = [ [[package]] name = "scap" version = "0.0.8" -source = "git+https://github.com/zed-industries/scap?rev=08f0a01417505cc0990b9931a37e5120db92e0d0#08f0a01417505cc0990b9931a37e5120db92e0d0" +source = "git+https://github.com/zed-industries/scap?rev=28dd306ff2e3374404936dec778fc1e975b8dd12#28dd306ff2e3374404936dec778fc1e975b8dd12" dependencies = [ "anyhow", "cocoa 0.25.0", @@ -19693,7 +19693,9 @@ dependencies = [ "wasmtime-cranelift", "wasmtime-environ", "winapi", + "windows 0.61.1", "windows-core 0.61.0", + "windows-future", "windows-numerics", "windows-sys 0.48.0", "windows-sys 0.52.0", diff --git a/Cargo.toml b/Cargo.toml index 0a87bf35b8..fd5cbff545 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -546,7 +546,7 @@ rustc-demangle = "0.1.23" rustc-hash = "2.1.0" rustls = { version = "0.23.26" } rustls-platform-verifier = "0.5.0" -scap = { git = "https://github.com/zed-industries/scap", rev = "08f0a01417505cc0990b9931a37e5120db92e0d0", default-features = false } +scap = { git = "https://github.com/zed-industries/scap", rev = "28dd306ff2e3374404936dec778fc1e975b8dd12", default-features = false } schemars = { version = "1.0", features = ["indexmap2"] } semver = "1.0" serde = { version = "1.0", features = ["derive", "rc"] } diff --git a/crates/gpui/Cargo.toml b/crates/gpui/Cargo.toml index b7cf6dd388..4e718b4428 100644 --- a/crates/gpui/Cargo.toml +++ b/crates/gpui/Cargo.toml @@ -150,6 +150,9 @@ metal.workspace = true [target.'cfg(any(target_os = "linux", target_os = "freebsd", target_os = "macos"))'.dependencies] pathfinder_geometry = "0.5" +[target.'cfg(any(target_os = "linux", target_os = "freebsd", target_os = "windows"))'.dependencies] +scap = { workspace = true, optional = true } + [target.'cfg(any(target_os = "linux", target_os = "freebsd"))'.dependencies] # Always used flume = "0.11" @@ -168,7 +171,6 @@ cosmic-text = { version = "0.14.0", optional = true } font-kit = { git = "https://github.com/zed-industries/font-kit", rev = "5474cfad4b719a72ec8ed2cb7327b2b01fd10568", features = [ "source-fontconfig-dlopen", ], optional = true } -scap = { workspace = true, optional = true } calloop = { version = "0.13.0" } filedescriptor = { version = "0.8.2", optional = true } diff --git a/crates/gpui/src/platform.rs b/crates/gpui/src/platform.rs index 1ad933dac1..0250e59a9b 100644 --- a/crates/gpui/src/platform.rs +++ b/crates/gpui/src/platform.rs @@ -26,8 +26,13 @@ mod windows; #[cfg(all( feature = "screen-capture", - any(target_os = "linux", target_os = "freebsd"), - any(feature = "wayland", feature = "x11"), + any( + target_os = "windows", + all( + any(target_os = "linux", target_os = "freebsd"), + any(feature = "wayland", feature = "x11"), + ) + ) ))] pub(crate) mod scap_screen_capture; diff --git a/crates/gpui/src/platform/windows.rs b/crates/gpui/src/platform/windows.rs index 462ed2158a..4bdf42080d 100644 --- a/crates/gpui/src/platform/windows.rs +++ b/crates/gpui/src/platform/windows.rs @@ -26,4 +26,7 @@ pub(crate) use wrapper::*; pub(crate) use windows::Win32::Foundation::HWND; +#[cfg(feature = "screen-capture")] +pub(crate) type PlatformScreenCaptureFrame = scap::frame::Frame; +#[cfg(not(feature = "screen-capture"))] pub(crate) type PlatformScreenCaptureFrame = (); diff --git a/crates/gpui/src/platform/windows/platform.rs b/crates/gpui/src/platform/windows/platform.rs index a8212307ad..f69a802da0 100644 --- a/crates/gpui/src/platform/windows/platform.rs +++ b/crates/gpui/src/platform/windows/platform.rs @@ -434,16 +434,14 @@ impl Platform for WindowsPlatform { #[cfg(feature = "screen-capture")] fn is_screen_capture_supported(&self) -> bool { - false + true } #[cfg(feature = "screen-capture")] fn screen_capture_sources( &self, ) -> oneshot::Receiver>>> { - let (mut tx, rx) = oneshot::channel(); - tx.send(Err(anyhow!("screen capture not implemented"))).ok(); - rx + crate::platform::scap_screen_capture::scap_screen_sources(&self.foreground_executor) } fn active_window(&self) -> Option { diff --git a/crates/livekit_client/Cargo.toml b/crates/livekit_client/Cargo.toml index 319dc76d48..a0c11d46e6 100644 --- a/crates/livekit_client/Cargo.toml +++ b/crates/livekit_client/Cargo.toml @@ -25,7 +25,7 @@ async-trait.workspace = true collections.workspace = true cpal.workspace = true futures.workspace = true -gpui = { workspace = true, features = ["screen-capture", "x11", "wayland"] } +gpui = { workspace = true, features = ["screen-capture", "x11", "wayland", "windows-manifest"] } gpui_tokio.workspace = true http_client_tls.workspace = true image.workspace = true @@ -45,7 +45,7 @@ livekit = { rev = "d2eade7a6b15d6dbdb38ba12a1ff7bf07fcebba4", git = "https://git "__rustls-tls" ] } -[target.'cfg(any(target_os = "linux", target_os = "freebsd"))'.dependencies] +[target.'cfg(any(target_os = "linux", target_os = "freebsd", target_os = "windows"))'.dependencies] scap.workspace = true [target.'cfg(target_os = "macos")'.dependencies] diff --git a/crates/livekit_client/src/lib.rs b/crates/livekit_client/src/lib.rs index d6074f6edb..f94181b8f8 100644 --- a/crates/livekit_client/src/lib.rs +++ b/crates/livekit_client/src/lib.rs @@ -3,36 +3,16 @@ use collections::HashMap; mod remote_video_track_view; pub use remote_video_track_view::{RemoteVideoTrackView, RemoteVideoTrackViewEvent}; -#[cfg(not(any( - test, - feature = "test-support", - any(all(target_os = "windows", target_env = "gnu"), target_os = "freebsd") -)))] +#[cfg(not(any(test, feature = "test-support", target_os = "freebsd")))] mod livekit_client; -#[cfg(not(any( - test, - feature = "test-support", - any(all(target_os = "windows", target_env = "gnu"), target_os = "freebsd") -)))] +#[cfg(not(any(test, feature = "test-support", target_os = "freebsd")))] pub use livekit_client::*; -#[cfg(any( - test, - feature = "test-support", - any(all(target_os = "windows", target_env = "gnu"), target_os = "freebsd") -))] +#[cfg(any(test, feature = "test-support", target_os = "freebsd"))] mod mock_client; -#[cfg(any( - test, - feature = "test-support", - any(all(target_os = "windows", target_env = "gnu"), target_os = "freebsd") -))] +#[cfg(any(test, feature = "test-support", target_os = "freebsd"))] pub mod test; -#[cfg(any( - test, - feature = "test-support", - any(all(target_os = "windows", target_env = "gnu"), target_os = "freebsd") -))] +#[cfg(any(test, feature = "test-support", target_os = "freebsd"))] pub use mock_client::*; #[derive(Debug, Clone)] diff --git a/crates/livekit_client/src/livekit_client/playback.rs b/crates/livekit_client/src/livekit_client/playback.rs index 3f6b3fd7db..7e36314c12 100644 --- a/crates/livekit_client/src/livekit_client/playback.rs +++ b/crates/livekit_client/src/livekit_client/playback.rs @@ -585,10 +585,10 @@ fn video_frame_buffer_from_webrtc(buffer: Box) -> Option) -> Option Option Option> { - use libwebrtc::native::yuv_helper::argb_to_nv12; + use libwebrtc::native::yuv_helper::{abgr_to_nv12, argb_to_nv12}; use livekit::webrtc::prelude::NV12Buffer; match frame.0 { scap::frame::Frame::BGRx(frame) => { @@ -638,6 +639,22 @@ fn video_frame_buffer_to_webrtc(frame: ScreenCaptureFrame) -> Option { + let mut buffer = NV12Buffer::new(frame.width as u32, frame.height as u32); + let (stride_y, stride_uv) = buffer.strides(); + let (data_y, data_uv) = buffer.data_mut(); + abgr_to_nv12( + &frame.data, + frame.width as u32 * 4, + data_y, + stride_y, + data_uv, + stride_uv, + frame.width, + frame.height, + ); + Some(buffer) + } scap::frame::Frame::YUVFrame(yuvframe) => { let mut buffer = NV12Buffer::with_strides( yuvframe.width as u32, @@ -659,11 +676,6 @@ fn video_frame_buffer_to_webrtc(frame: ScreenCaptureFrame) -> Option Option> { - None as Option> -} - trait DeviceChangeListenerApi: Stream + Sized { fn new(input: bool) -> Result; } diff --git a/tooling/workspace-hack/Cargo.toml b/tooling/workspace-hack/Cargo.toml index 2a242329e2..d4019ab854 100644 --- a/tooling/workspace-hack/Cargo.toml +++ b/tooling/workspace-hack/Cargo.toml @@ -574,7 +574,9 @@ tokio-socks = { version = "0.5", features = ["futures-io"] } tokio-stream = { version = "0.1", features = ["fs"] } tower = { version = "0.5", default-features = false, features = ["timeout", "util"] } winapi = { version = "0.3", default-features = false, features = ["cfg", "commapi", "consoleapi", "errhandlingapi", "evntrace", "fileapi", "handleapi", "impl-debug", "impl-default", "in6addr", "inaddr", "ioapiset", "knownfolders", "minwinbase", "minwindef", "namedpipeapi", "ntsecapi", "objbase", "processenv", "processthreadsapi", "shlobj", "std", "synchapi", "sysinfoapi", "timezoneapi", "winbase", "windef", "winerror", "winioctl", "winnt", "winreg", "winsock2", "winuser"] } +windows = { version = "0.61", features = ["Foundation_Metadata", "Foundation_Numerics", "Graphics_Capture", "Graphics_DirectX_Direct3D11", "Graphics_Imaging", "Media_Core", "Media_MediaProperties", "Media_Transcoding", "Security_Cryptography", "Storage_Search", "Storage_Streams", "System_Threading", "UI_ViewManagement", "Wdk_System_SystemServices", "Win32_Devices_Display", "Win32_Globalization", "Win32_Graphics_Direct2D_Common", "Win32_Graphics_Direct3D", "Win32_Graphics_Direct3D11", "Win32_Graphics_DirectWrite", "Win32_Graphics_Dwm", "Win32_Graphics_Dxgi_Common", "Win32_Graphics_Gdi", "Win32_Graphics_Imaging_D2D", "Win32_Networking_WinSock", "Win32_Security_Credentials", "Win32_Storage_FileSystem", "Win32_System_Com_StructuredStorage", "Win32_System_Console", "Win32_System_DataExchange", "Win32_System_IO", "Win32_System_LibraryLoader", "Win32_System_Memory", "Win32_System_Ole", "Win32_System_Pipes", "Win32_System_SystemInformation", "Win32_System_SystemServices", "Win32_System_Threading", "Win32_System_Variant", "Win32_System_WinRT_Direct3D11", "Win32_System_WinRT_Graphics_Capture", "Win32_UI_Controls", "Win32_UI_HiDpi", "Win32_UI_Input_Ime", "Win32_UI_Input_KeyboardAndMouse", "Win32_UI_Shell_Common", "Win32_UI_Shell_PropertiesSystem", "Win32_UI_WindowsAndMessaging"] } windows-core = { version = "0.61" } +windows-future = { version = "0.2" } windows-numerics = { version = "0.2" } windows-sys-73dcd821b1037cfd = { package = "windows-sys", version = "0.59", features = ["Wdk_Foundation", "Wdk_Storage_FileSystem", "Win32_Globalization", "Win32_NetworkManagement_IpHelper", "Win32_Networking_WinSock", "Win32_Security_Authentication_Identity", "Win32_Security_Credentials", "Win32_Security_Cryptography", "Win32_Storage_FileSystem", "Win32_System_Com", "Win32_System_Console", "Win32_System_Diagnostics_Debug", "Win32_System_IO", "Win32_System_Ioctl", "Win32_System_Kernel", "Win32_System_LibraryLoader", "Win32_System_Memory", "Win32_System_Performance", "Win32_System_Pipes", "Win32_System_Registry", "Win32_System_SystemInformation", "Win32_System_SystemServices", "Win32_System_Threading", "Win32_System_Time", "Win32_System_WindowsProgramming", "Win32_UI_Input_KeyboardAndMouse", "Win32_UI_Shell", "Win32_UI_WindowsAndMessaging"] } windows-sys-b21d60becc0929df = { package = "windows-sys", version = "0.52", features = ["Wdk_Foundation", "Wdk_Storage_FileSystem", "Wdk_System_IO", "Win32_Foundation", "Win32_Networking_WinSock", "Win32_Security_Authorization", "Win32_Storage_FileSystem", "Win32_System_Console", "Win32_System_IO", "Win32_System_Memory", "Win32_System_Pipes", "Win32_System_SystemServices", "Win32_System_Threading", "Win32_System_WindowsProgramming"] } @@ -599,7 +601,9 @@ tokio-socks = { version = "0.5", features = ["futures-io"] } tokio-stream = { version = "0.1", features = ["fs"] } tower = { version = "0.5", default-features = false, features = ["timeout", "util"] } winapi = { version = "0.3", default-features = false, features = ["cfg", "commapi", "consoleapi", "errhandlingapi", "evntrace", "fileapi", "handleapi", "impl-debug", "impl-default", "in6addr", "inaddr", "ioapiset", "knownfolders", "minwinbase", "minwindef", "namedpipeapi", "ntsecapi", "objbase", "processenv", "processthreadsapi", "shlobj", "std", "synchapi", "sysinfoapi", "timezoneapi", "winbase", "windef", "winerror", "winioctl", "winnt", "winreg", "winsock2", "winuser"] } +windows = { version = "0.61", features = ["Foundation_Metadata", "Foundation_Numerics", "Graphics_Capture", "Graphics_DirectX_Direct3D11", "Graphics_Imaging", "Media_Core", "Media_MediaProperties", "Media_Transcoding", "Security_Cryptography", "Storage_Search", "Storage_Streams", "System_Threading", "UI_ViewManagement", "Wdk_System_SystemServices", "Win32_Devices_Display", "Win32_Globalization", "Win32_Graphics_Direct2D_Common", "Win32_Graphics_Direct3D", "Win32_Graphics_Direct3D11", "Win32_Graphics_DirectWrite", "Win32_Graphics_Dwm", "Win32_Graphics_Dxgi_Common", "Win32_Graphics_Gdi", "Win32_Graphics_Imaging_D2D", "Win32_Networking_WinSock", "Win32_Security_Credentials", "Win32_Storage_FileSystem", "Win32_System_Com_StructuredStorage", "Win32_System_Console", "Win32_System_DataExchange", "Win32_System_IO", "Win32_System_LibraryLoader", "Win32_System_Memory", "Win32_System_Ole", "Win32_System_Pipes", "Win32_System_SystemInformation", "Win32_System_SystemServices", "Win32_System_Threading", "Win32_System_Variant", "Win32_System_WinRT_Direct3D11", "Win32_System_WinRT_Graphics_Capture", "Win32_UI_Controls", "Win32_UI_HiDpi", "Win32_UI_Input_Ime", "Win32_UI_Input_KeyboardAndMouse", "Win32_UI_Shell_Common", "Win32_UI_Shell_PropertiesSystem", "Win32_UI_WindowsAndMessaging"] } windows-core = { version = "0.61" } +windows-future = { version = "0.2" } windows-numerics = { version = "0.2" } windows-sys-73dcd821b1037cfd = { package = "windows-sys", version = "0.59", features = ["Wdk_Foundation", "Wdk_Storage_FileSystem", "Win32_Globalization", "Win32_NetworkManagement_IpHelper", "Win32_Networking_WinSock", "Win32_Security_Authentication_Identity", "Win32_Security_Credentials", "Win32_Security_Cryptography", "Win32_Storage_FileSystem", "Win32_System_Com", "Win32_System_Console", "Win32_System_Diagnostics_Debug", "Win32_System_IO", "Win32_System_Ioctl", "Win32_System_Kernel", "Win32_System_LibraryLoader", "Win32_System_Memory", "Win32_System_Performance", "Win32_System_Pipes", "Win32_System_Registry", "Win32_System_SystemInformation", "Win32_System_SystemServices", "Win32_System_Threading", "Win32_System_Time", "Win32_System_WindowsProgramming", "Win32_UI_Input_KeyboardAndMouse", "Win32_UI_Shell", "Win32_UI_WindowsAndMessaging"] } windows-sys-b21d60becc0929df = { package = "windows-sys", version = "0.52", features = ["Wdk_Foundation", "Wdk_Storage_FileSystem", "Wdk_System_IO", "Win32_Foundation", "Win32_Networking_WinSock", "Win32_Security_Authorization", "Win32_Storage_FileSystem", "Win32_System_Console", "Win32_System_IO", "Win32_System_Memory", "Win32_System_Pipes", "Win32_System_SystemServices", "Win32_System_Threading", "Win32_System_WindowsProgramming"] }