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

Michael Sloan created

This reverts commit c2afc2271b1dd420f482217cb0fa9dafcd17aab6.

Build on ARM if failing, likely because `c_char` is `u8` on arm and `i8`
on x86:

```
error[E0308]: mismatched types
   --> /home/runner/.cargo/git/checkouts/scap-40ad33e1dd47aaea/5715067/src/targets/linux/mod.rs:75:74
    |
75  |     let result = unsafe { XmbTextPropertyToTextList(display, &mut xname, &mut list, &mut count) };
    |                           -------------------------                      ^^^^^^^^^ expected `*mut *mut *mut u8`, found `&mut *mut *mut i8`
    |                           |
    |                           arguments to this function are incorrect
    |
    = note:    expected raw pointer `*mut *mut *mut u8`
            found mutable reference `&mut *mut *mut i8`
note: function defined here
   --> /home/runner/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/x11-2.21.0/src/xlib.rs:552:10
    |
552 |   pub fn XmbTextPropertyToTextList (_4: *mut Display, _3: *const XTextProperty, _2: *mut *mut *mut c_char, _1: *mut c_int) -> c_int,
    |          ^^^^^^^^^^^^^^^^^^^^^^^^^
```

Release Notes:

- N/A

Change summary

Cargo.lock                                           | 152 -------
Cargo.toml                                           |  11 
crates/gpui/Cargo.toml                               |  14 
crates/gpui/src/app.rs                               |   5 
crates/gpui/src/platform.rs                          |  12 
crates/gpui/src/platform/linux.rs                    |   3 
crates/gpui/src/platform/linux/headless/client.rs    |  22 
crates/gpui/src/platform/linux/platform.rs           |  13 
crates/gpui/src/platform/linux/wayland/client.rs     |  24 -
crates/gpui/src/platform/linux/x11/client.rs         |  19 
crates/gpui/src/platform/mac/platform.rs             |   4 
crates/gpui/src/platform/mac/screen_capture.rs       |  14 
crates/gpui/src/platform/scap_screen_capture.rs      | 282 --------------
crates/gpui/src/platform/test/platform.rs            |  18 
crates/gpui/src/platform/windows/platform.rs         |   4 
crates/livekit_client/Cargo.toml                     |   9 
crates/livekit_client/src/livekit_client/playback.rs |  46 --
crates/title_bar/src/collab.rs                       |   5 
crates/workspace/src/workspace.rs                    |  12 
script/linux                                         |   4 
20 files changed, 49 insertions(+), 624 deletions(-)

Detailed changes

Cargo.lock 🔗

@@ -3454,19 +3454,6 @@ dependencies = [
  "libc",
 ]
 
-[[package]]
-name = "core-graphics-helmer-fork"
-version = "0.24.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "32eb7c354ae9f6d437a6039099ce7ecd049337a8109b23d73e48e8ffba8e9cd5"
-dependencies = [
- "bitflags 2.9.0",
- "core-foundation 0.9.4",
- "core-graphics-types 0.1.3",
- "foreign-types 0.5.0",
- "libc",
-]
-
 [[package]]
 name = "core-graphics-types"
 version = "0.1.3"
@@ -4442,12 +4429,6 @@ dependencies = [
  "windows-sys 0.48.0",
 ]
 
-[[package]]
-name = "dispatch"
-version = "0.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b"
-
 [[package]]
 name = "displaydoc"
 version = "0.2.5"
@@ -6118,7 +6099,6 @@ dependencies = [
  "refineable",
  "reqwest_client",
  "resvg",
- "scap",
  "schemars",
  "seahash",
  "semantic_version",
@@ -8145,7 +8125,6 @@ dependencies = [
  "objc",
  "parking_lot",
  "postage",
- "scap",
  "serde",
  "serde_json",
  "sha2",
@@ -9157,18 +9136,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1"
 dependencies = [
  "malloc_buf",
- "objc_exception",
-]
-
-[[package]]
-name = "objc-foundation"
-version = "0.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1add1b659e36c9607c7aab864a76c7a4c2760cd0cd2e120f3fb8b952c7e22bf9"
-dependencies = [
- "block",
- "objc",
- "objc_id",
 ]
 
 [[package]]
@@ -9373,24 +9340,6 @@ dependencies = [
  "objc2-foundation",
 ]
 
-[[package]]
-name = "objc_exception"
-version = "0.1.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ad970fb455818ad6cba4c122ad012fae53ae8b4795f86378bce65e4f6bab2ca4"
-dependencies = [
- "cc",
-]
-
-[[package]]
-name = "objc_id"
-version = "0.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c92d4ddb4bd7b50d730c215ff871754d0da6b2178849f8a2a2ab69712d0c073b"
-dependencies = [
- "objc",
-]
-
 [[package]]
 name = "object"
 version = "0.36.7"
@@ -11197,15 +11146,6 @@ version = "2.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3"
 
-[[package]]
-name = "quick-xml"
-version = "0.30.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "eff6510e86862b57b210fd8cbe8ed3f0d7d600b9c2863cd4549a2e033c66e956"
-dependencies = [
- "memchr",
-]
-
 [[package]]
 name = "quick-xml"
 version = "0.32.0"
@@ -12434,27 +12374,6 @@ dependencies = [
  "winapi-util",
 ]
 
-[[package]]
-name = "scap"
-version = "0.0.8"
-source = "git+https://github.com/zed-industries/scap?rev=5715067104794aa356977c543e2f3e95c6183044#5715067104794aa356977c543e2f3e95c6183044"
-dependencies = [
- "anyhow",
- "cocoa 0.25.0",
- "core-graphics-helmer-fork",
- "log",
- "objc",
- "rand 0.8.5",
- "screencapturekit",
- "screencapturekit-sys",
- "sysinfo",
- "tao-core-video-sys",
- "windows 0.61.1",
- "windows-capture",
- "x11",
- "xcb",
-]
-
 [[package]]
 name = "schannel"
 version = "0.1.27"
@@ -12521,29 +12440,6 @@ version = "1.0.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9f6280af86e5f559536da57a45ebc84948833b3bee313a7dd25232e09c878a52"
 
-[[package]]
-name = "screencapturekit"
-version = "0.2.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1a5eeeb57ac94960cfe5ff4c402be6585ae4c8d29a2cf41b276048c2e849d64e"
-dependencies = [
- "screencapturekit-sys",
-]
-
-[[package]]
-name = "screencapturekit-sys"
-version = "0.2.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "22411b57f7d49e7fe08025198813ee6fd65e1ee5eff4ebc7880c12c82bde4c60"
-dependencies = [
- "block",
- "dispatch",
- "objc",
- "objc-foundation",
- "objc_id",
- "once_cell",
-]
-
 [[package]]
 name = "scrypt"
 version = "0.11.0"
@@ -14106,18 +14002,6 @@ version = "0.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "8bdb6fa0dfa67b38c1e66b7041ba9dcf23b99d8121907cd31c807a332f7a0bbb"
 
-[[package]]
-name = "tao-core-video-sys"
-version = "0.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "271450eb289cb4d8d0720c6ce70c72c8c858c93dd61fc625881616752e6b98f6"
-dependencies = [
- "cfg-if",
- "core-foundation-sys",
- "libc",
- "objc",
-]
-
 [[package]]
 name = "tap"
 version = "1.0.1"
@@ -16787,20 +16671,6 @@ dependencies = [
  "windows-numerics",
 ]
 
-[[package]]
-name = "windows-capture"
-version = "1.4.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6001b777f61cafce437201de46a019ed7f4afed3b669f02e5ce4e0759164cb3e"
-dependencies = [
- "clap",
- "ctrlc",
- "parking_lot",
- "rayon",
- "thiserror 1.0.69",
- "windows 0.58.0",
-]
-
 [[package]]
 name = "windows-collections"
 version = "0.2.0"
@@ -17804,16 +17674,6 @@ dependencies = [
  "tap",
 ]
 
-[[package]]
-name = "x11"
-version = "2.21.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "502da5464ccd04011667b11c435cb992822c2c0dbde1770c988480d312a0db2e"
-dependencies = [
- "libc",
- "pkg-config",
-]
-
 [[package]]
 name = "x11-clipboard"
 version = "0.9.3"
@@ -17852,18 +17712,6 @@ dependencies = [
  "libc",
 ]
 
-[[package]]
-name = "xcb"
-version = "1.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f1e2f212bb1a92cd8caac8051b829a6582ede155ccb60b5d5908b81b100952be"
-dependencies = [
- "bitflags 1.3.2",
- "libc",
- "quick-xml 0.30.0",
- "x11",
-]
-
 [[package]]
 name = "xcursor"
 version = "0.3.8"

Cargo.toml 🔗

@@ -400,12 +400,8 @@ async-tungstenite = "0.28"
 async-watch = "0.3.1"
 async_zip = { version = "0.0.17", features = ["deflate", "deflate64"] }
 aws-config = { version = "1.5.16", features = ["behavior-version-latest"] }
-aws-credential-types = { version = "1.2.1", features = [
-    "hardcoded-credentials",
-] }
-aws-sdk-bedrockruntime = { version = "1.73.0", features = [
-    "behavior-version-latest",
-] }
+aws-credential-types = { version = "1.2.1", features = ["hardcoded-credentials"] }
+aws-sdk-bedrockruntime = { version = "1.73.0", features = ["behavior-version-latest"] }
 aws-smithy-runtime-api = { version = "1.7.3", features = ["http-1x", "client"] }
 aws-smithy-types = { version = "1.2.13", features = ["http-body-1-x"] }
 base64 = "0.22"
@@ -512,7 +508,6 @@ rust-embed = { version = "8.4", features = ["include-exclude"] }
 rustc-hash = "2.1.0"
 rustls = { version = "0.23.22" }
 rustls-platform-verifier = "0.5.0"
-scap = { git = "https://github.com/zed-industries/scap", rev = "5715067104794aa356977c543e2f3e95c6183044", default-features = false }
 schemars = { version = "0.8", features = ["impl_json_schema", "indexmap2"] }
 semver = "1.0"
 serde = { version = "1.0", features = ["derive", "rc"] }
@@ -552,7 +547,7 @@ time = { version = "0.3", features = [
 tiny_http = "0.8"
 toml = "0.8"
 tokio = { version = "1" }
-tokio-tungstenite = { version = "0.26", features = ["__rustls-tls"] }
+tokio-tungstenite = { version = "0.26", features = ["__rustls-tls"]}
 tower-http = "0.4.4"
 tree-sitter = { version = "0.25.3", features = ["wasm"] }
 tree-sitter-bash = "0.23"

crates/gpui/Cargo.toml 🔗

@@ -49,7 +49,6 @@ wayland = [
     "filedescriptor",
     "xkbcommon",
     "open",
-    "scap",
 ]
 x11 = [
     "blade-graphics",
@@ -66,7 +65,6 @@ x11 = [
     "x11-clipboard",
     "filedescriptor",
     "open",
-    "scap"
 ]
 
 
@@ -101,11 +99,7 @@ profiling.workspace = true
 rand = { optional = true, workspace = true }
 raw-window-handle = "0.6"
 refineable.workspace = true
-resvg = { version = "0.45.0", default-features = false, features = [
-    "text",
-    "system-fonts",
-    "memmap-fonts",
-] }
+resvg = { version = "0.45.0", default-features = false, features = ["text", "system-fonts", "memmap-fonts"] }
 usvg = { version = "0.45.0", default-features = false }
 schemars.workspace = true
 seahash = "4.1"
@@ -165,7 +159,6 @@ cosmic-text = { version = "0.13.2", 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 }
@@ -200,10 +193,7 @@ x11rb = { version = "0.13.1", features = [
     "resource_manager",
     "sync",
 ], optional = true }
-xkbcommon = { version = "0.8.0", features = [
-    "wayland",
-    "x11",
-], optional = true }
+xkbcommon = { version = "0.8.0", features = ["wayland", "x11"], optional = true }
 xim = { git = "https://github.com/XDeme1/xim-rs", rev = "d50d461764c2213655cd9cf65a0ea94c70d3c4fd", features = [
     "x11rb-xcb",
     "x11rb-client",

crates/gpui/src/app.rs 🔗

@@ -650,11 +650,6 @@ impl App {
         self.platform.primary_display()
     }
 
-    /// Returns whether `screen_capture_sources` may work.
-    pub fn is_screen_capture_supported(&self) -> bool {
-        self.platform.is_screen_capture_supported()
-    }
-
     /// Returns a list of available screen capture sources.
     pub fn screen_capture_sources(
         &self,

crates/gpui/src/platform.rs 🔗

@@ -26,12 +26,6 @@ mod test;
 #[cfg(target_os = "windows")]
 mod windows;
 
-#[cfg(all(
-    any(target_os = "linux", target_os = "freebsd"),
-    any(feature = "wayland", feature = "x11"),
-))]
-pub(crate) mod scap_screen_capture;
-
 use crate::{
     Action, AnyWindowHandle, App, AsyncWindowContext, BackgroundExecutor, Bounds,
     DEFAULT_WINDOW_SIZE, DevicePixels, DispatchEventResult, Font, FontId, FontMetrics, FontRun,
@@ -164,7 +158,6 @@ pub(crate) trait Platform: 'static {
         None
     }
 
-    fn is_screen_capture_supported(&self) -> bool;
     fn screen_capture_sources(
         &self,
     ) -> oneshot::Receiver<Result<Vec<Box<dyn ScreenCaptureSource>>>>;
@@ -253,14 +246,13 @@ pub trait PlatformDisplay: Send + Sync + Debug {
 /// A source of on-screen video content that can be captured.
 pub trait ScreenCaptureSource {
     /// Returns the video resolution of this source.
-    fn resolution(&self) -> Result<Size<DevicePixels>>;
+    fn resolution(&self) -> Result<Size<Pixels>>;
 
     /// Start capture video from this source, invoking the given callback
     /// with each frame.
     fn stream(
         &self,
-        foreground_executor: &ForegroundExecutor,
-        frame_callback: Box<dyn Fn(ScreenCaptureFrame) + Send>,
+        frame_callback: Box<dyn Fn(ScreenCaptureFrame)>,
     ) -> oneshot::Receiver<Result<Box<dyn ScreenCaptureStream>>>;
 }
 

crates/gpui/src/platform/linux.rs 🔗

@@ -21,7 +21,4 @@ pub(crate) use wayland::*;
 #[cfg(feature = "x11")]
 pub(crate) use x11::*;
 
-#[cfg(any(feature = "wayland", feature = "x11"))]
-pub(crate) type PlatformScreenCaptureFrame = scap::frame::Frame;
-#[cfg(not(any(feature = "wayland", feature = "x11")))]
 pub(crate) type PlatformScreenCaptureFrame = ();

crates/gpui/src/platform/linux/headless/client.rs 🔗

@@ -1,16 +1,13 @@
 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, ScreenCaptureSource, WindowParams,
-};
+use crate::{AnyWindowHandle, CursorStyle, DisplayId, PlatformDisplay, WindowParams};
 
 pub struct HeadlessClientState {
     pub(crate) _loop_handle: LoopHandle<'static, HeadlessClient>,
@@ -66,21 +63,6 @@ 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
     }

crates/gpui/src/platform/linux/platform.rs 🔗

@@ -28,7 +28,6 @@ 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;
 
@@ -51,10 +50,6 @@ 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,
@@ -235,14 +230,12 @@ 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>>>> {
-        self.screen_capture_sources()
+        let (mut tx, rx) = oneshot::channel();
+        tx.send(Err(anyhow!("screen capture not implemented"))).ok();
+        rx
     }
 
     fn active_window(&self) -> Option<AnyWindowHandle> {

crates/gpui/src/platform/linux/wayland/client.rs 🔗

@@ -7,7 +7,6 @@ use std::{
     time::{Duration, Instant},
 };
 
-use anyhow::anyhow;
 use calloop::{
     EventLoop, LoopHandle,
     timer::{TimeoutAction, Timer},
@@ -15,7 +14,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;
@@ -86,8 +85,7 @@ use crate::{
     FileDropEvent, ForegroundExecutor, KeyDownEvent, KeyUpEvent, Keystroke, LinuxCommon, Modifiers,
     ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseExitEvent, MouseMoveEvent,
     MouseUpEvent, NavigationDirection, Pixels, PlatformDisplay, PlatformInput, Point, SCROLL_LINES,
-    ScaledPixels, ScreenCaptureSource, ScrollDelta, ScrollWheelEvent, Size, TouchPhase,
-    WindowParams, point, px, size,
+    ScaledPixels, ScrollDelta, ScrollWheelEvent, Size, TouchPhase, WindowParams, point, px, size,
 };
 
 /// Used to convert evdev scancode to xkb scancode
@@ -635,24 +633,6 @@ 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,

crates/gpui/src/platform/linux/x11/client.rs 🔗

@@ -1,4 +1,3 @@
-use crate::platform::scap_screen_capture::scap_screen_sources;
 use core::str;
 use std::{
     cell::RefCell,
@@ -9,13 +8,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;
@@ -60,8 +59,8 @@ use crate::platform::{
 use crate::{
     AnyWindowHandle, Bounds, ClipboardItem, CursorStyle, DisplayId, FileDropEvent, Keystroke,
     Modifiers, ModifiersChangedEvent, MouseButton, Pixels, Platform, PlatformDisplay,
-    PlatformInput, Point, RequestFrameOptions, ScaledPixels, ScreenCaptureSource, ScrollDelta,
-    Size, TouchPhase, WindowParams, X11Window, modifiers_from_xinput_info, point, px,
+    PlatformInput, Point, RequestFrameOptions, ScaledPixels, ScrollDelta, Size, TouchPhase,
+    WindowParams, X11Window, modifiers_from_xinput_info, point, px,
 };
 
 /// Value for DeviceId parameters which selects all devices.
@@ -1328,16 +1327,6 @@ 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,

crates/gpui/src/platform/mac/platform.rs 🔗

@@ -552,10 +552,6 @@ impl Platform for MacPlatform {
             .collect()
     }
 
-    fn is_screen_capture_supported(&self) -> bool {
-        true
-    }
-
     fn screen_capture_sources(
         &self,
     ) -> oneshot::Receiver<Result<Vec<Box<dyn ScreenCaptureSource>>>> {

crates/gpui/src/platform/mac/screen_capture.rs 🔗

@@ -1,7 +1,7 @@
 use crate::{
-    DevicePixels, ForegroundExecutor, Size,
+    Pixels, Size,
     platform::{ScreenCaptureFrame, ScreenCaptureSource, ScreenCaptureStream},
-    size,
+    px, size,
 };
 use anyhow::{Result, anyhow};
 use block::ConcreteBlock;
@@ -48,7 +48,7 @@ const FRAME_CALLBACK_IVAR: &str = "frame_callback";
 const SCStreamOutputTypeScreen: NSInteger = 0;
 
 impl ScreenCaptureSource for MacScreenCaptureSource {
-    fn resolution(&self) -> Result<Size<DevicePixels>> {
+    fn resolution(&self) -> Result<Size<Pixels>> {
         unsafe {
             let display_id: CGDirectDisplayID = msg_send![self.sc_display, displayID];
             let display_mode_ref = CGDisplayCopyDisplayMode(display_id);
@@ -56,17 +56,13 @@ impl ScreenCaptureSource for MacScreenCaptureSource {
             let height = CGDisplayModeGetPixelHeight(display_mode_ref);
             CGDisplayModeRelease(display_mode_ref);
 
-            Ok(size(
-                DevicePixels(width as i32),
-                DevicePixels(height as i32),
-            ))
+            Ok(size(px(width as f32), px(height as f32)))
         }
     }
 
     fn stream(
         &self,
-        _foreground_executor: &ForegroundExecutor,
-        frame_callback: Box<dyn Fn(ScreenCaptureFrame) + Send>,
+        frame_callback: Box<dyn Fn(ScreenCaptureFrame)>,
     ) -> oneshot::Receiver<Result<Box<dyn ScreenCaptureStream>>> {
         unsafe {
             let stream: id = msg_send![class!(SCStream), alloc];

crates/gpui/src/platform/scap_screen_capture.rs 🔗

@@ -1,282 +0,0 @@
-//! Screen capture for Linux and Windows
-use crate::{
-    DevicePixels, ForegroundExecutor, ScreenCaptureFrame, ScreenCaptureSource, ScreenCaptureStream,
-    Size, size,
-};
-use anyhow::{Context as _, Result, anyhow};
-use futures::channel::oneshot;
-use std::sync::Arc;
-use std::sync::atomic::{self, AtomicBool};
-
-/// Populates the receiver with the screens that can be captured.
-///
-/// `scap_default_target_source` should be used instead on Wayland, since `scap_screen_sources`
-/// won't return any results.
-#[allow(dead_code)]
-pub(crate) fn scap_screen_sources(
-    foreground_executor: &ForegroundExecutor,
-) -> oneshot::Receiver<Result<Vec<Box<dyn ScreenCaptureSource>>>> {
-    let (sources_tx, sources_rx) = oneshot::channel();
-    get_screen_targets(sources_tx);
-    to_dyn_screen_capture_sources(sources_rx, foreground_executor)
-}
-
-/// Starts screen capture for the default target, and populates the receiver with a single source
-/// for it. The first frame of the screen capture is used to determine the size of the stream.
-///
-/// On Wayland (Linux), prompts the user to select a target, and populates the receiver with a
-/// single screen capture source for their selection.
-#[allow(dead_code)]
-pub(crate) fn start_scap_default_target_source(
-    foreground_executor: &ForegroundExecutor,
-) -> oneshot::Receiver<Result<Vec<Box<dyn ScreenCaptureSource>>>> {
-    let (sources_tx, sources_rx) = oneshot::channel();
-    start_default_target_screen_capture(sources_tx);
-    to_dyn_screen_capture_sources(sources_rx, foreground_executor)
-}
-
-struct ScapCaptureSource {
-    target: scap::Target,
-    size: Size<DevicePixels>,
-}
-
-/// Populates the sender with the screens available for capture.
-fn get_screen_targets(sources_tx: oneshot::Sender<Result<Vec<ScapCaptureSource>>>) {
-    // Due to use of blocking APIs, a new thread is used.
-    std::thread::spawn(|| {
-        let targets = match scap::get_all_targets() {
-            Ok(targets) => targets,
-            Err(err) => {
-                sources_tx.send(Err(err)).ok();
-                return;
-            }
-        };
-        let sources = targets
-            .iter()
-            .filter_map(|target| match target {
-                scap::Target::Display(display) => {
-                    let size = Size {
-                        width: DevicePixels(display.width as i32),
-                        height: DevicePixels(display.height as i32),
-                    };
-                    Some(ScapCaptureSource {
-                        target: target.clone(),
-                        size,
-                    })
-                }
-                scap::Target::Window(_) => None,
-            })
-            .collect::<Vec<_>>();
-        sources_tx.send(Ok(sources)).ok();
-    });
-}
-
-impl ScreenCaptureSource for ScapCaptureSource {
-    fn resolution(&self) -> Result<Size<DevicePixels>> {
-        Ok(self.size)
-    }
-
-    fn stream(
-        &self,
-        foreground_executor: &ForegroundExecutor,
-        frame_callback: Box<dyn Fn(ScreenCaptureFrame) + Send>,
-    ) -> oneshot::Receiver<Result<Box<dyn ScreenCaptureStream>>> {
-        let (stream_tx, stream_rx) = oneshot::channel();
-        let target = self.target.clone();
-
-        // Due to use of blocking APIs, a dedicated thread is used.
-        std::thread::spawn(move || match new_scap_capturer(Some(target)) {
-            Ok(mut capturer) => {
-                capturer.start_capture();
-                run_capture(capturer, frame_callback, stream_tx);
-            }
-            Err(e) => {
-                stream_tx.send(Err(e)).ok();
-            }
-        });
-
-        to_dyn_screen_capture_stream(stream_rx, foreground_executor)
-    }
-}
-
-struct ScapDefaultTargetCaptureSource {
-    // Sender populated by single call to `ScreenCaptureSource::stream`.
-    stream_call_tx: std::sync::mpsc::SyncSender<(
-        // Provides the result of `ScreenCaptureSource::stream`.
-        oneshot::Sender<Result<ScapStream>>,
-        // Callback for frames.
-        Box<dyn Fn(ScreenCaptureFrame) + Send>,
-    )>,
-    size: Size<DevicePixels>,
-}
-
-/// Starts screen capture on the default capture target, and populates the sender with the source.
-fn start_default_target_screen_capture(
-    sources_tx: oneshot::Sender<Result<Vec<ScapDefaultTargetCaptureSource>>>,
-) {
-    // Due to use of blocking APIs, a dedicated thread is used.
-    std::thread::spawn(|| {
-        let start_result = util::maybe!({
-            let mut capturer = new_scap_capturer(None)?;
-            capturer.start_capture();
-            let first_frame = capturer
-                .get_next_frame()
-                .context("Failed to get first frame of screenshare to get the size.")?;
-            let size = frame_size(&first_frame);
-            Ok((capturer, size))
-        });
-
-        match start_result {
-            Err(e) => {
-                sources_tx.send(Err(e)).ok();
-            }
-            Ok((capturer, size)) => {
-                let (stream_call_tx, stream_rx) = std::sync::mpsc::sync_channel(1);
-                sources_tx
-                    .send(Ok(vec![ScapDefaultTargetCaptureSource {
-                        stream_call_tx,
-                        size,
-                    }]))
-                    .ok();
-                let Ok((stream_tx, frame_callback)) = stream_rx.recv() else {
-                    return;
-                };
-                run_capture(capturer, frame_callback, stream_tx);
-            }
-        }
-    });
-}
-
-impl ScreenCaptureSource for ScapDefaultTargetCaptureSource {
-    fn resolution(&self) -> Result<Size<DevicePixels>> {
-        Ok(self.size)
-    }
-
-    fn stream(
-        &self,
-        foreground_executor: &ForegroundExecutor,
-        frame_callback: Box<dyn Fn(ScreenCaptureFrame) + Send>,
-    ) -> oneshot::Receiver<Result<Box<dyn ScreenCaptureStream>>> {
-        let (tx, rx) = oneshot::channel();
-        match self.stream_call_tx.try_send((tx, frame_callback)) {
-            Ok(()) => {}
-            Err(std::sync::mpsc::TrySendError::Full((tx, _)))
-            | Err(std::sync::mpsc::TrySendError::Disconnected((tx, _))) => {
-                // Note: support could be added for being called again after end of prior stream.
-                tx.send(Err(anyhow!(
-                    "Can't call ScapDefaultTargetCaptureSource::stream multiple times."
-                )))
-                .ok();
-            }
-        }
-        to_dyn_screen_capture_stream(rx, foreground_executor)
-    }
-}
-
-fn new_scap_capturer(target: Option<scap::Target>) -> Result<scap::capturer::Capturer> {
-    scap::capturer::Capturer::build(scap::capturer::Options {
-        fps: 60,
-        show_cursor: true,
-        show_highlight: true,
-        // Note that the actual frame output type may differ.
-        output_type: scap::frame::FrameType::YUVFrame,
-        output_resolution: scap::capturer::Resolution::Captured,
-        crop_area: None,
-        target,
-        excluded_targets: None,
-    })
-}
-
-fn run_capture(
-    mut capturer: scap::capturer::Capturer,
-    frame_callback: Box<dyn Fn(ScreenCaptureFrame) + Send>,
-    stream_tx: oneshot::Sender<Result<ScapStream>>,
-) {
-    let cancel_stream = Arc::new(AtomicBool::new(false));
-    let stream_send_result = stream_tx.send(Ok(ScapStream {
-        cancel_stream: cancel_stream.clone(),
-    }));
-    if let Err(_) = stream_send_result {
-        return;
-    }
-    while !cancel_stream.load(std::sync::atomic::Ordering::SeqCst) {
-        match capturer.get_next_frame() {
-            Ok(frame) => frame_callback(ScreenCaptureFrame(frame)),
-            Err(err) => {
-                log::error!("Halting screen capture due to error: {err}");
-                break;
-            }
-        }
-    }
-    capturer.stop_capture();
-}
-
-struct ScapStream {
-    cancel_stream: Arc<AtomicBool>,
-}
-
-impl ScreenCaptureStream for ScapStream {}
-
-impl Drop for ScapStream {
-    fn drop(&mut self) {
-        self.cancel_stream.store(true, atomic::Ordering::SeqCst);
-    }
-}
-
-fn frame_size(frame: &scap::frame::Frame) -> Size<DevicePixels> {
-    let (width, height) = match frame {
-        scap::frame::Frame::YUVFrame(frame) => (frame.width, frame.height),
-        scap::frame::Frame::RGB(frame) => (frame.width, frame.height),
-        scap::frame::Frame::RGBx(frame) => (frame.width, frame.height),
-        scap::frame::Frame::XBGR(frame) => (frame.width, frame.height),
-        scap::frame::Frame::BGRx(frame) => (frame.width, frame.height),
-        scap::frame::Frame::BGR0(frame) => (frame.width, frame.height),
-        scap::frame::Frame::BGRA(frame) => (frame.width, frame.height),
-    };
-    size(DevicePixels(width), DevicePixels(height))
-}
-
-/// This is used by `get_screen_targets` and `start_default_target_screen_capture` to turn their
-/// results into `Box<dyn ScreenCaptureSource>`. They need to `Send` their capture source, and so
-/// the capture source structs are used as `Box<dyn ScreenCaptureSource>` is not `Send`.
-fn to_dyn_screen_capture_sources<T: ScreenCaptureSource + 'static>(
-    sources_rx: oneshot::Receiver<Result<Vec<T>>>,
-    foreground_executor: &ForegroundExecutor,
-) -> oneshot::Receiver<Result<Vec<Box<dyn ScreenCaptureSource>>>> {
-    let (dyn_sources_tx, dyn_sources_rx) = oneshot::channel();
-    foreground_executor
-        .spawn(async move {
-            match sources_rx.await {
-                Ok(Ok(results)) => dyn_sources_tx
-                    .send(Ok(results
-                        .into_iter()
-                        .map(|source| Box::new(source) as Box<dyn ScreenCaptureSource>)
-                        .collect::<Vec<_>>()))
-                    .ok(),
-                Ok(Err(err)) => dyn_sources_tx.send(Err(err)).ok(),
-                Err(oneshot::Canceled) => None,
-            }
-        })
-        .detach();
-    dyn_sources_rx
-}
-
-/// Same motivation as `to_dyn_screen_capture_sources` above.
-fn to_dyn_screen_capture_stream<T: ScreenCaptureStream + 'static>(
-    sources_rx: oneshot::Receiver<Result<T>>,
-    foreground_executor: &ForegroundExecutor,
-) -> oneshot::Receiver<Result<Box<dyn ScreenCaptureStream>>> {
-    let (dyn_sources_tx, dyn_sources_rx) = oneshot::channel();
-    foreground_executor
-        .spawn(async move {
-            match sources_rx.await {
-                Ok(Ok(stream)) => dyn_sources_tx
-                    .send(Ok(Box::new(stream) as Box<dyn ScreenCaptureStream>))
-                    .ok(),
-                Ok(Err(err)) => dyn_sources_tx.send(Err(err)).ok(),
-                Err(oneshot::Canceled) => None,
-            }
-        })
-        .detach();
-    dyn_sources_rx
-}

crates/gpui/src/platform/test/platform.rs 🔗

@@ -1,8 +1,7 @@
 use crate::{
-    AnyWindowHandle, BackgroundExecutor, ClipboardItem, CursorStyle, DevicePixels,
-    ForegroundExecutor, Keymap, Platform, PlatformDisplay, PlatformTextSystem, ScreenCaptureFrame,
-    ScreenCaptureSource, ScreenCaptureStream, Size, Task, TestDisplay, TestWindow,
-    WindowAppearance, WindowParams, size,
+    AnyWindowHandle, BackgroundExecutor, ClipboardItem, CursorStyle, ForegroundExecutor, Keymap,
+    Platform, PlatformDisplay, PlatformTextSystem, ScreenCaptureFrame, ScreenCaptureSource,
+    ScreenCaptureStream, Task, TestDisplay, TestWindow, WindowAppearance, WindowParams, px, size,
 };
 use anyhow::Result;
 use collections::VecDeque;
@@ -47,14 +46,13 @@ pub struct TestScreenCaptureSource {}
 pub struct TestScreenCaptureStream {}
 
 impl ScreenCaptureSource for TestScreenCaptureSource {
-    fn resolution(&self) -> Result<Size<DevicePixels>> {
-        Ok(size(DevicePixels(1), DevicePixels(1)))
+    fn resolution(&self) -> Result<crate::Size<crate::Pixels>> {
+        Ok(size(px(1.), px(1.)))
     }
 
     fn stream(
         &self,
-        _foreground_executor: &ForegroundExecutor,
-        _frame_callback: Box<dyn Fn(ScreenCaptureFrame) + Send>,
+        _frame_callback: Box<dyn Fn(ScreenCaptureFrame)>,
     ) -> oneshot::Receiver<Result<Box<dyn ScreenCaptureStream>>> {
         let (mut tx, rx) = oneshot::channel();
         let stream = TestScreenCaptureStream {};
@@ -273,10 +271,6 @@ impl Platform for TestPlatform {
         Some(self.active_display.clone())
     }
 
-    fn is_screen_capture_supported(&self) -> bool {
-        true
-    }
-
     fn screen_capture_sources(
         &self,
     ) -> oneshot::Receiver<Result<Vec<Box<dyn ScreenCaptureSource>>>> {

crates/gpui/src/platform/windows/platform.rs 🔗

@@ -396,10 +396,6 @@ impl Platform for WindowsPlatform {
         WindowsDisplay::primary_monitor().map(|display| Rc::new(display) as Rc<dyn PlatformDisplay>)
     }
 
-    fn is_screen_capture_supported(&self) -> bool {
-        false
-    }
-
     fn screen_capture_sources(
         &self,
     ) -> oneshot::Receiver<Result<Vec<Box<dyn ScreenCaptureSource>>>> {

crates/livekit_client/Cargo.toml 🔗

@@ -25,7 +25,7 @@ async-trait.workspace = true
 collections.workspace = true
 cpal = "0.15"
 futures.workspace = true
-gpui = { workspace = true, features = ["x11", "wayland"] }
+gpui.workspace = true
 gpui_tokio.workspace = true
 http_client_tls.workspace = true
 image.workspace = true
@@ -41,12 +41,7 @@ workspace-hack.workspace = true
 
 [target.'cfg(not(all(target_os = "windows", target_env = "gnu")))'.dependencies]
 libwebrtc = { rev = "80bb8f4c9112789f7c24cc98d8423010977806a6", git = "https://github.com/zed-industries/livekit-rust-sdks" }
-livekit = { rev = "80bb8f4c9112789f7c24cc98d8423010977806a6", git = "https://github.com/zed-industries/livekit-rust-sdks", features = [
-    "__rustls-tls"
-] }
-
-[target.'cfg(any(target_os = "linux", target_os = "freebsd"))'.dependencies]
-scap.workspace = true
+livekit = { rev = "80bb8f4c9112789f7c24cc98d8423010977806a6", git = "https://github.com/zed-industries/livekit-rust-sdks", features = ["__rustls-tls"] }
 
 [target.'cfg(target_os = "macos")'.dependencies]
 core-foundation.workspace = true

crates/livekit_client/src/livekit_client/playback.rs 🔗

@@ -336,7 +336,7 @@ pub(crate) async fn capture_local_video_track(
     .await?;
 
     let capture_stream = capture_source
-        .stream(cx.foreground_executor(), {
+        .stream({
             let track_source = track_source.clone();
             Box::new(move |frame| {
                 if let Some(buffer) = video_frame_buffer_to_webrtc(frame) {
@@ -620,49 +620,7 @@ fn video_frame_buffer_to_webrtc(frame: ScreenCaptureFrame) -> Option<impl AsRef<
     }
 }
 
-#[cfg(any(target_os = "linux", target_os = "freebsd"))]
-fn video_frame_buffer_to_webrtc(frame: ScreenCaptureFrame) -> Option<impl AsRef<dyn VideoBuffer>> {
-    use libwebrtc::native::yuv_helper::argb_to_nv12;
-    use livekit::webrtc::prelude::NV12Buffer;
-    match frame.0 {
-        scap::frame::Frame::BGRx(frame) => {
-            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();
-            argb_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,
-                yuvframe.height as u32,
-                yuvframe.luminance_stride as u32,
-                yuvframe.chrominance_stride as u32,
-            );
-            let (luminance, chrominance) = buffer.data_mut();
-            luminance.copy_from_slice(yuvframe.luminance_bytes.as_slice());
-            chrominance.copy_from_slice(yuvframe.chrominance_bytes.as_slice());
-            Some(buffer)
-        }
-        _ => {
-            log::error!(
-                "Expected BGRx or YUV frame from scap screen capture but got some other format."
-            );
-            None
-        }
-    }
-}
-
-#[cfg(target_os = "windows")]
+#[cfg(not(target_os = "macos"))]
 fn video_frame_buffer_to_webrtc(_frame: ScreenCaptureFrame) -> Option<impl AsRef<dyn VideoBuffer>> {
     None as Option<Box<dyn VideoBuffer>>
 }

crates/title_bar/src/collab.rs 🔗

@@ -299,7 +299,10 @@ impl TitleBar {
         let is_screen_sharing = room.is_screen_sharing();
         let can_use_microphone = room.can_use_microphone();
         let can_share_projects = room.can_share_projects();
-        let screen_sharing_supported = cx.is_screen_capture_supported();
+        let screen_sharing_supported = match self.platform_style {
+            PlatformStyle::Mac => true,
+            PlatformStyle::Linux | PlatformStyle::Windows => false,
+        };
 
         let mut children = Vec::new();
 

crates/workspace/src/workspace.rs 🔗

@@ -4423,6 +4423,18 @@ impl Workspace {
         None
     }
 
+    #[cfg(target_os = "windows")]
+    fn shared_screen_for_peer(
+        &self,
+        _peer_id: PeerId,
+        _pane: &Entity<Pane>,
+        _window: &mut Window,
+        _cx: &mut App,
+    ) -> Option<Entity<SharedScreen>> {
+        None
+    }
+
+    #[cfg(not(target_os = "windows"))]
     fn shared_screen_for_peer(
         &self,
         peer_id: PeerId,

script/linux 🔗

@@ -28,7 +28,6 @@ if [[ -n $apt ]]; then
     libasound2-dev
     libfontconfig-dev
     libwayland-dev
-    libx11-xcb-dev
     libxkbcommon-x11-dev
     libssl-dev
     libzstd-dev
@@ -77,7 +76,6 @@ if [[ -n $dnf ]] || [[ -n $yum ]]; then
     alsa-lib-devel
     fontconfig-devel
     wayland-devel
-    libxcb-devel
     libxkbcommon-x11-devel
     openssl-devel
     libzstd-devel
@@ -146,7 +144,6 @@ if [[ -n $zyp ]]; then
     gzip
     jq
     libvulkan1
-    libxcb-devel
     libxkbcommon-devel
     libxkbcommon-x11-devel
     libzstd-devel
@@ -177,7 +174,6 @@ if [[ -n $pacman ]]; then
     fontconfig
     wayland
     libgit2
-    libxcb
     libxkbcommon-x11
     openbsd-netcat
     openssl