This reverts commit c2afc2271b
.
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
This commit is contained in:
parent
6ddad64af1
commit
c1259c136e
20 changed files with 49 additions and 624 deletions
152
Cargo.lock
generated
152
Cargo.lock
generated
|
@ -3454,19 +3454,6 @@ dependencies = [
|
||||||
"libc",
|
"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]]
|
[[package]]
|
||||||
name = "core-graphics-types"
|
name = "core-graphics-types"
|
||||||
version = "0.1.3"
|
version = "0.1.3"
|
||||||
|
@ -4442,12 +4429,6 @@ dependencies = [
|
||||||
"windows-sys 0.48.0",
|
"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]]
|
[[package]]
|
||||||
name = "displaydoc"
|
name = "displaydoc"
|
||||||
version = "0.2.5"
|
version = "0.2.5"
|
||||||
|
@ -6118,7 +6099,6 @@ dependencies = [
|
||||||
"refineable",
|
"refineable",
|
||||||
"reqwest_client",
|
"reqwest_client",
|
||||||
"resvg",
|
"resvg",
|
||||||
"scap",
|
|
||||||
"schemars",
|
"schemars",
|
||||||
"seahash",
|
"seahash",
|
||||||
"semantic_version",
|
"semantic_version",
|
||||||
|
@ -8145,7 +8125,6 @@ dependencies = [
|
||||||
"objc",
|
"objc",
|
||||||
"parking_lot",
|
"parking_lot",
|
||||||
"postage",
|
"postage",
|
||||||
"scap",
|
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"sha2",
|
"sha2",
|
||||||
|
@ -9157,18 +9136,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1"
|
checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"malloc_buf",
|
"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]]
|
[[package]]
|
||||||
|
@ -9373,24 +9340,6 @@ dependencies = [
|
||||||
"objc2-foundation",
|
"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]]
|
[[package]]
|
||||||
name = "object"
|
name = "object"
|
||||||
version = "0.36.7"
|
version = "0.36.7"
|
||||||
|
@ -11197,15 +11146,6 @@ version = "2.0.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3"
|
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]]
|
[[package]]
|
||||||
name = "quick-xml"
|
name = "quick-xml"
|
||||||
version = "0.32.0"
|
version = "0.32.0"
|
||||||
|
@ -12434,27 +12374,6 @@ dependencies = [
|
||||||
"winapi-util",
|
"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]]
|
[[package]]
|
||||||
name = "schannel"
|
name = "schannel"
|
||||||
version = "0.1.27"
|
version = "0.1.27"
|
||||||
|
@ -12521,29 +12440,6 @@ version = "1.0.8"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9f6280af86e5f559536da57a45ebc84948833b3bee313a7dd25232e09c878a52"
|
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]]
|
[[package]]
|
||||||
name = "scrypt"
|
name = "scrypt"
|
||||||
version = "0.11.0"
|
version = "0.11.0"
|
||||||
|
@ -14106,18 +14002,6 @@ version = "0.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8bdb6fa0dfa67b38c1e66b7041ba9dcf23b99d8121907cd31c807a332f7a0bbb"
|
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]]
|
[[package]]
|
||||||
name = "tap"
|
name = "tap"
|
||||||
version = "1.0.1"
|
version = "1.0.1"
|
||||||
|
@ -16787,20 +16671,6 @@ dependencies = [
|
||||||
"windows-numerics",
|
"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]]
|
[[package]]
|
||||||
name = "windows-collections"
|
name = "windows-collections"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
|
@ -17804,16 +17674,6 @@ dependencies = [
|
||||||
"tap",
|
"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]]
|
[[package]]
|
||||||
name = "x11-clipboard"
|
name = "x11-clipboard"
|
||||||
version = "0.9.3"
|
version = "0.9.3"
|
||||||
|
@ -17852,18 +17712,6 @@ dependencies = [
|
||||||
"libc",
|
"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]]
|
[[package]]
|
||||||
name = "xcursor"
|
name = "xcursor"
|
||||||
version = "0.3.8"
|
version = "0.3.8"
|
||||||
|
|
11
Cargo.toml
11
Cargo.toml
|
@ -400,12 +400,8 @@ async-tungstenite = "0.28"
|
||||||
async-watch = "0.3.1"
|
async-watch = "0.3.1"
|
||||||
async_zip = { version = "0.0.17", features = ["deflate", "deflate64"] }
|
async_zip = { version = "0.0.17", features = ["deflate", "deflate64"] }
|
||||||
aws-config = { version = "1.5.16", features = ["behavior-version-latest"] }
|
aws-config = { version = "1.5.16", features = ["behavior-version-latest"] }
|
||||||
aws-credential-types = { version = "1.2.1", features = [
|
aws-credential-types = { version = "1.2.1", features = ["hardcoded-credentials"] }
|
||||||
"hardcoded-credentials",
|
aws-sdk-bedrockruntime = { version = "1.73.0", features = ["behavior-version-latest"] }
|
||||||
] }
|
|
||||||
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-runtime-api = { version = "1.7.3", features = ["http-1x", "client"] }
|
||||||
aws-smithy-types = { version = "1.2.13", features = ["http-body-1-x"] }
|
aws-smithy-types = { version = "1.2.13", features = ["http-body-1-x"] }
|
||||||
base64 = "0.22"
|
base64 = "0.22"
|
||||||
|
@ -512,7 +508,6 @@ rust-embed = { version = "8.4", features = ["include-exclude"] }
|
||||||
rustc-hash = "2.1.0"
|
rustc-hash = "2.1.0"
|
||||||
rustls = { version = "0.23.22" }
|
rustls = { version = "0.23.22" }
|
||||||
rustls-platform-verifier = "0.5.0"
|
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"] }
|
schemars = { version = "0.8", features = ["impl_json_schema", "indexmap2"] }
|
||||||
semver = "1.0"
|
semver = "1.0"
|
||||||
serde = { version = "1.0", features = ["derive", "rc"] }
|
serde = { version = "1.0", features = ["derive", "rc"] }
|
||||||
|
@ -552,7 +547,7 @@ time = { version = "0.3", features = [
|
||||||
tiny_http = "0.8"
|
tiny_http = "0.8"
|
||||||
toml = "0.8"
|
toml = "0.8"
|
||||||
tokio = { version = "1" }
|
tokio = { version = "1" }
|
||||||
tokio-tungstenite = { version = "0.26", features = ["__rustls-tls"] }
|
tokio-tungstenite = { version = "0.26", features = ["__rustls-tls"]}
|
||||||
tower-http = "0.4.4"
|
tower-http = "0.4.4"
|
||||||
tree-sitter = { version = "0.25.3", features = ["wasm"] }
|
tree-sitter = { version = "0.25.3", features = ["wasm"] }
|
||||||
tree-sitter-bash = "0.23"
|
tree-sitter-bash = "0.23"
|
||||||
|
|
|
@ -49,7 +49,6 @@ wayland = [
|
||||||
"filedescriptor",
|
"filedescriptor",
|
||||||
"xkbcommon",
|
"xkbcommon",
|
||||||
"open",
|
"open",
|
||||||
"scap",
|
|
||||||
]
|
]
|
||||||
x11 = [
|
x11 = [
|
||||||
"blade-graphics",
|
"blade-graphics",
|
||||||
|
@ -66,7 +65,6 @@ x11 = [
|
||||||
"x11-clipboard",
|
"x11-clipboard",
|
||||||
"filedescriptor",
|
"filedescriptor",
|
||||||
"open",
|
"open",
|
||||||
"scap"
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@ -101,11 +99,7 @@ profiling.workspace = true
|
||||||
rand = { optional = true, workspace = true }
|
rand = { optional = true, workspace = true }
|
||||||
raw-window-handle = "0.6"
|
raw-window-handle = "0.6"
|
||||||
refineable.workspace = true
|
refineable.workspace = true
|
||||||
resvg = { version = "0.45.0", default-features = false, features = [
|
resvg = { version = "0.45.0", default-features = false, features = ["text", "system-fonts", "memmap-fonts"] }
|
||||||
"text",
|
|
||||||
"system-fonts",
|
|
||||||
"memmap-fonts",
|
|
||||||
] }
|
|
||||||
usvg = { version = "0.45.0", default-features = false }
|
usvg = { version = "0.45.0", default-features = false }
|
||||||
schemars.workspace = true
|
schemars.workspace = true
|
||||||
seahash = "4.1"
|
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 = [
|
font-kit = { git = "https://github.com/zed-industries/font-kit", rev = "5474cfad4b719a72ec8ed2cb7327b2b01fd10568", features = [
|
||||||
"source-fontconfig-dlopen",
|
"source-fontconfig-dlopen",
|
||||||
], optional = true }
|
], optional = true }
|
||||||
scap = { workspace = true, optional = true }
|
|
||||||
|
|
||||||
calloop = { version = "0.13.0" }
|
calloop = { version = "0.13.0" }
|
||||||
filedescriptor = { version = "0.8.2", optional = true }
|
filedescriptor = { version = "0.8.2", optional = true }
|
||||||
|
@ -200,10 +193,7 @@ x11rb = { version = "0.13.1", features = [
|
||||||
"resource_manager",
|
"resource_manager",
|
||||||
"sync",
|
"sync",
|
||||||
], optional = true }
|
], optional = true }
|
||||||
xkbcommon = { version = "0.8.0", features = [
|
xkbcommon = { version = "0.8.0", features = ["wayland", "x11"], optional = true }
|
||||||
"wayland",
|
|
||||||
"x11",
|
|
||||||
], optional = true }
|
|
||||||
xim = { git = "https://github.com/XDeme1/xim-rs", rev = "d50d461764c2213655cd9cf65a0ea94c70d3c4fd", features = [
|
xim = { git = "https://github.com/XDeme1/xim-rs", rev = "d50d461764c2213655cd9cf65a0ea94c70d3c4fd", features = [
|
||||||
"x11rb-xcb",
|
"x11rb-xcb",
|
||||||
"x11rb-client",
|
"x11rb-client",
|
||||||
|
|
|
@ -650,11 +650,6 @@ impl App {
|
||||||
self.platform.primary_display()
|
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.
|
/// Returns a list of available screen capture sources.
|
||||||
pub fn screen_capture_sources(
|
pub fn screen_capture_sources(
|
||||||
&self,
|
&self,
|
||||||
|
|
|
@ -26,12 +26,6 @@ mod test;
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
mod 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::{
|
use crate::{
|
||||||
Action, AnyWindowHandle, App, AsyncWindowContext, BackgroundExecutor, Bounds,
|
Action, AnyWindowHandle, App, AsyncWindowContext, BackgroundExecutor, Bounds,
|
||||||
DEFAULT_WINDOW_SIZE, DevicePixels, DispatchEventResult, Font, FontId, FontMetrics, FontRun,
|
DEFAULT_WINDOW_SIZE, DevicePixels, DispatchEventResult, Font, FontId, FontMetrics, FontRun,
|
||||||
|
@ -164,7 +158,6 @@ pub(crate) trait Platform: 'static {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_screen_capture_supported(&self) -> bool;
|
|
||||||
fn screen_capture_sources(
|
fn screen_capture_sources(
|
||||||
&self,
|
&self,
|
||||||
) -> oneshot::Receiver<Result<Vec<Box<dyn ScreenCaptureSource>>>>;
|
) -> 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.
|
/// A source of on-screen video content that can be captured.
|
||||||
pub trait ScreenCaptureSource {
|
pub trait ScreenCaptureSource {
|
||||||
/// Returns the video resolution of this source.
|
/// 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
|
/// Start capture video from this source, invoking the given callback
|
||||||
/// with each frame.
|
/// with each frame.
|
||||||
fn stream(
|
fn stream(
|
||||||
&self,
|
&self,
|
||||||
foreground_executor: &ForegroundExecutor,
|
frame_callback: Box<dyn Fn(ScreenCaptureFrame)>,
|
||||||
frame_callback: Box<dyn Fn(ScreenCaptureFrame) + Send>,
|
|
||||||
) -> oneshot::Receiver<Result<Box<dyn ScreenCaptureStream>>>;
|
) -> oneshot::Receiver<Result<Box<dyn ScreenCaptureStream>>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,4 @@ pub(crate) use wayland::*;
|
||||||
#[cfg(feature = "x11")]
|
#[cfg(feature = "x11")]
|
||||||
pub(crate) use 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 = ();
|
pub(crate) type PlatformScreenCaptureFrame = ();
|
||||||
|
|
|
@ -1,16 +1,13 @@
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use anyhow::anyhow;
|
|
||||||
use calloop::{EventLoop, LoopHandle};
|
use calloop::{EventLoop, LoopHandle};
|
||||||
use futures::channel::oneshot;
|
|
||||||
use util::ResultExt;
|
use util::ResultExt;
|
||||||
|
|
||||||
use crate::platform::linux::LinuxClient;
|
use crate::platform::linux::LinuxClient;
|
||||||
use crate::platform::{LinuxCommon, PlatformWindow};
|
use crate::platform::{LinuxCommon, PlatformWindow};
|
||||||
use crate::{
|
use crate::{AnyWindowHandle, CursorStyle, DisplayId, PlatformDisplay, WindowParams};
|
||||||
AnyWindowHandle, CursorStyle, DisplayId, PlatformDisplay, ScreenCaptureSource, WindowParams,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub struct HeadlessClientState {
|
pub struct HeadlessClientState {
|
||||||
pub(crate) _loop_handle: LoopHandle<'static, HeadlessClient>,
|
pub(crate) _loop_handle: LoopHandle<'static, HeadlessClient>,
|
||||||
|
@ -66,21 +63,6 @@ impl LinuxClient for HeadlessClient {
|
||||||
None
|
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> {
|
fn active_window(&self) -> Option<AnyWindowHandle> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,7 +28,6 @@ use crate::{
|
||||||
Pixels, Platform, PlatformDisplay, PlatformTextSystem, PlatformWindow, Point, Result,
|
Pixels, Platform, PlatformDisplay, PlatformTextSystem, PlatformWindow, Point, Result,
|
||||||
ScreenCaptureSource, Task, WindowAppearance, WindowParams, px,
|
ScreenCaptureSource, Task, WindowAppearance, WindowParams, px,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(any(feature = "wayland", feature = "x11"))]
|
#[cfg(any(feature = "wayland", feature = "x11"))]
|
||||||
pub(crate) const SCROLL_LINES: f32 = 3.0;
|
pub(crate) const SCROLL_LINES: f32 = 3.0;
|
||||||
|
|
||||||
|
@ -51,10 +50,6 @@ pub trait LinuxClient {
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
fn display(&self, id: DisplayId) -> Option<Rc<dyn PlatformDisplay>>;
|
fn display(&self, id: DisplayId) -> Option<Rc<dyn PlatformDisplay>>;
|
||||||
fn primary_display(&self) -> 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(
|
fn open_window(
|
||||||
&self,
|
&self,
|
||||||
|
@ -235,14 +230,12 @@ impl<P: LinuxClient + 'static> Platform for P {
|
||||||
self.displays()
|
self.displays()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_screen_capture_supported(&self) -> bool {
|
|
||||||
self.is_screen_capture_supported()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn screen_capture_sources(
|
fn screen_capture_sources(
|
||||||
&self,
|
&self,
|
||||||
) -> oneshot::Receiver<Result<Vec<Box<dyn ScreenCaptureSource>>>> {
|
) -> 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> {
|
fn active_window(&self) -> Option<AnyWindowHandle> {
|
||||||
|
|
|
@ -7,7 +7,6 @@ use std::{
|
||||||
time::{Duration, Instant},
|
time::{Duration, Instant},
|
||||||
};
|
};
|
||||||
|
|
||||||
use anyhow::anyhow;
|
|
||||||
use calloop::{
|
use calloop::{
|
||||||
EventLoop, LoopHandle,
|
EventLoop, LoopHandle,
|
||||||
timer::{TimeoutAction, Timer},
|
timer::{TimeoutAction, Timer},
|
||||||
|
@ -15,7 +14,7 @@ use calloop::{
|
||||||
use calloop_wayland_source::WaylandSource;
|
use calloop_wayland_source::WaylandSource;
|
||||||
use collections::HashMap;
|
use collections::HashMap;
|
||||||
use filedescriptor::Pipe;
|
use filedescriptor::Pipe;
|
||||||
use futures::channel::oneshot;
|
|
||||||
use http_client::Url;
|
use http_client::Url;
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
use util::ResultExt;
|
use util::ResultExt;
|
||||||
|
@ -86,8 +85,7 @@ use crate::{
|
||||||
FileDropEvent, ForegroundExecutor, KeyDownEvent, KeyUpEvent, Keystroke, LinuxCommon, Modifiers,
|
FileDropEvent, ForegroundExecutor, KeyDownEvent, KeyUpEvent, Keystroke, LinuxCommon, Modifiers,
|
||||||
ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseExitEvent, MouseMoveEvent,
|
ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseExitEvent, MouseMoveEvent,
|
||||||
MouseUpEvent, NavigationDirection, Pixels, PlatformDisplay, PlatformInput, Point, SCROLL_LINES,
|
MouseUpEvent, NavigationDirection, Pixels, PlatformDisplay, PlatformInput, Point, SCROLL_LINES,
|
||||||
ScaledPixels, ScreenCaptureSource, ScrollDelta, ScrollWheelEvent, Size, TouchPhase,
|
ScaledPixels, ScrollDelta, ScrollWheelEvent, Size, TouchPhase, WindowParams, point, px, size,
|
||||||
WindowParams, point, px, size,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Used to convert evdev scancode to xkb scancode
|
/// Used to convert evdev scancode to xkb scancode
|
||||||
|
@ -635,24 +633,6 @@ impl LinuxClient for WaylandClient {
|
||||||
None
|
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(
|
fn open_window(
|
||||||
&self,
|
&self,
|
||||||
handle: AnyWindowHandle,
|
handle: AnyWindowHandle,
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
use crate::platform::scap_screen_capture::scap_screen_sources;
|
|
||||||
use core::str;
|
use core::str;
|
||||||
use std::{
|
use std::{
|
||||||
cell::RefCell,
|
cell::RefCell,
|
||||||
|
@ -9,13 +8,13 @@ use std::{
|
||||||
time::{Duration, Instant},
|
time::{Duration, Instant},
|
||||||
};
|
};
|
||||||
|
|
||||||
use anyhow::Context as _;
|
|
||||||
use calloop::{
|
use calloop::{
|
||||||
EventLoop, LoopHandle, RegistrationToken,
|
EventLoop, LoopHandle, RegistrationToken,
|
||||||
generic::{FdWrapper, Generic},
|
generic::{FdWrapper, Generic},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use anyhow::Context as _;
|
||||||
use collections::HashMap;
|
use collections::HashMap;
|
||||||
use futures::channel::oneshot;
|
|
||||||
use http_client::Url;
|
use http_client::Url;
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
use util::ResultExt;
|
use util::ResultExt;
|
||||||
|
@ -60,8 +59,8 @@ use crate::platform::{
|
||||||
use crate::{
|
use crate::{
|
||||||
AnyWindowHandle, Bounds, ClipboardItem, CursorStyle, DisplayId, FileDropEvent, Keystroke,
|
AnyWindowHandle, Bounds, ClipboardItem, CursorStyle, DisplayId, FileDropEvent, Keystroke,
|
||||||
Modifiers, ModifiersChangedEvent, MouseButton, Pixels, Platform, PlatformDisplay,
|
Modifiers, ModifiersChangedEvent, MouseButton, Pixels, Platform, PlatformDisplay,
|
||||||
PlatformInput, Point, RequestFrameOptions, ScaledPixels, ScreenCaptureSource, ScrollDelta,
|
PlatformInput, Point, RequestFrameOptions, ScaledPixels, ScrollDelta, Size, TouchPhase,
|
||||||
Size, TouchPhase, WindowParams, X11Window, modifiers_from_xinput_info, point, px,
|
WindowParams, X11Window, modifiers_from_xinput_info, point, px,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Value for DeviceId parameters which selects all devices.
|
/// 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(
|
fn open_window(
|
||||||
&self,
|
&self,
|
||||||
handle: AnyWindowHandle,
|
handle: AnyWindowHandle,
|
||||||
|
|
|
@ -552,10 +552,6 @@ impl Platform for MacPlatform {
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_screen_capture_supported(&self) -> bool {
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
fn screen_capture_sources(
|
fn screen_capture_sources(
|
||||||
&self,
|
&self,
|
||||||
) -> oneshot::Receiver<Result<Vec<Box<dyn ScreenCaptureSource>>>> {
|
) -> oneshot::Receiver<Result<Vec<Box<dyn ScreenCaptureSource>>>> {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
DevicePixels, ForegroundExecutor, Size,
|
Pixels, Size,
|
||||||
platform::{ScreenCaptureFrame, ScreenCaptureSource, ScreenCaptureStream},
|
platform::{ScreenCaptureFrame, ScreenCaptureSource, ScreenCaptureStream},
|
||||||
size,
|
px, size,
|
||||||
};
|
};
|
||||||
use anyhow::{Result, anyhow};
|
use anyhow::{Result, anyhow};
|
||||||
use block::ConcreteBlock;
|
use block::ConcreteBlock;
|
||||||
|
@ -48,7 +48,7 @@ const FRAME_CALLBACK_IVAR: &str = "frame_callback";
|
||||||
const SCStreamOutputTypeScreen: NSInteger = 0;
|
const SCStreamOutputTypeScreen: NSInteger = 0;
|
||||||
|
|
||||||
impl ScreenCaptureSource for MacScreenCaptureSource {
|
impl ScreenCaptureSource for MacScreenCaptureSource {
|
||||||
fn resolution(&self) -> Result<Size<DevicePixels>> {
|
fn resolution(&self) -> Result<Size<Pixels>> {
|
||||||
unsafe {
|
unsafe {
|
||||||
let display_id: CGDirectDisplayID = msg_send![self.sc_display, displayID];
|
let display_id: CGDirectDisplayID = msg_send![self.sc_display, displayID];
|
||||||
let display_mode_ref = CGDisplayCopyDisplayMode(display_id);
|
let display_mode_ref = CGDisplayCopyDisplayMode(display_id);
|
||||||
|
@ -56,17 +56,13 @@ impl ScreenCaptureSource for MacScreenCaptureSource {
|
||||||
let height = CGDisplayModeGetPixelHeight(display_mode_ref);
|
let height = CGDisplayModeGetPixelHeight(display_mode_ref);
|
||||||
CGDisplayModeRelease(display_mode_ref);
|
CGDisplayModeRelease(display_mode_ref);
|
||||||
|
|
||||||
Ok(size(
|
Ok(size(px(width as f32), px(height as f32)))
|
||||||
DevicePixels(width as i32),
|
|
||||||
DevicePixels(height as i32),
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn stream(
|
fn stream(
|
||||||
&self,
|
&self,
|
||||||
_foreground_executor: &ForegroundExecutor,
|
frame_callback: Box<dyn Fn(ScreenCaptureFrame)>,
|
||||||
frame_callback: Box<dyn Fn(ScreenCaptureFrame) + Send>,
|
|
||||||
) -> oneshot::Receiver<Result<Box<dyn ScreenCaptureStream>>> {
|
) -> oneshot::Receiver<Result<Box<dyn ScreenCaptureStream>>> {
|
||||||
unsafe {
|
unsafe {
|
||||||
let stream: id = msg_send![class!(SCStream), alloc];
|
let stream: id = msg_send![class!(SCStream), alloc];
|
||||||
|
|
|
@ -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
|
|
||||||
}
|
|
|
@ -1,8 +1,7 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
AnyWindowHandle, BackgroundExecutor, ClipboardItem, CursorStyle, DevicePixels,
|
AnyWindowHandle, BackgroundExecutor, ClipboardItem, CursorStyle, ForegroundExecutor, Keymap,
|
||||||
ForegroundExecutor, Keymap, Platform, PlatformDisplay, PlatformTextSystem, ScreenCaptureFrame,
|
Platform, PlatformDisplay, PlatformTextSystem, ScreenCaptureFrame, ScreenCaptureSource,
|
||||||
ScreenCaptureSource, ScreenCaptureStream, Size, Task, TestDisplay, TestWindow,
|
ScreenCaptureStream, Task, TestDisplay, TestWindow, WindowAppearance, WindowParams, px, size,
|
||||||
WindowAppearance, WindowParams, size,
|
|
||||||
};
|
};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use collections::VecDeque;
|
use collections::VecDeque;
|
||||||
|
@ -47,14 +46,13 @@ pub struct TestScreenCaptureSource {}
|
||||||
pub struct TestScreenCaptureStream {}
|
pub struct TestScreenCaptureStream {}
|
||||||
|
|
||||||
impl ScreenCaptureSource for TestScreenCaptureSource {
|
impl ScreenCaptureSource for TestScreenCaptureSource {
|
||||||
fn resolution(&self) -> Result<Size<DevicePixels>> {
|
fn resolution(&self) -> Result<crate::Size<crate::Pixels>> {
|
||||||
Ok(size(DevicePixels(1), DevicePixels(1)))
|
Ok(size(px(1.), px(1.)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn stream(
|
fn stream(
|
||||||
&self,
|
&self,
|
||||||
_foreground_executor: &ForegroundExecutor,
|
_frame_callback: Box<dyn Fn(ScreenCaptureFrame)>,
|
||||||
_frame_callback: Box<dyn Fn(ScreenCaptureFrame) + Send>,
|
|
||||||
) -> oneshot::Receiver<Result<Box<dyn ScreenCaptureStream>>> {
|
) -> oneshot::Receiver<Result<Box<dyn ScreenCaptureStream>>> {
|
||||||
let (mut tx, rx) = oneshot::channel();
|
let (mut tx, rx) = oneshot::channel();
|
||||||
let stream = TestScreenCaptureStream {};
|
let stream = TestScreenCaptureStream {};
|
||||||
|
@ -273,10 +271,6 @@ impl Platform for TestPlatform {
|
||||||
Some(self.active_display.clone())
|
Some(self.active_display.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_screen_capture_supported(&self) -> bool {
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
fn screen_capture_sources(
|
fn screen_capture_sources(
|
||||||
&self,
|
&self,
|
||||||
) -> oneshot::Receiver<Result<Vec<Box<dyn ScreenCaptureSource>>>> {
|
) -> oneshot::Receiver<Result<Vec<Box<dyn ScreenCaptureSource>>>> {
|
||||||
|
|
|
@ -396,10 +396,6 @@ impl Platform for WindowsPlatform {
|
||||||
WindowsDisplay::primary_monitor().map(|display| Rc::new(display) as Rc<dyn PlatformDisplay>)
|
WindowsDisplay::primary_monitor().map(|display| Rc::new(display) as Rc<dyn PlatformDisplay>)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_screen_capture_supported(&self) -> bool {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
fn screen_capture_sources(
|
fn screen_capture_sources(
|
||||||
&self,
|
&self,
|
||||||
) -> oneshot::Receiver<Result<Vec<Box<dyn ScreenCaptureSource>>>> {
|
) -> oneshot::Receiver<Result<Vec<Box<dyn ScreenCaptureSource>>>> {
|
||||||
|
|
|
@ -25,7 +25,7 @@ async-trait.workspace = true
|
||||||
collections.workspace = true
|
collections.workspace = true
|
||||||
cpal = "0.15"
|
cpal = "0.15"
|
||||||
futures.workspace = true
|
futures.workspace = true
|
||||||
gpui = { workspace = true, features = ["x11", "wayland"] }
|
gpui.workspace = true
|
||||||
gpui_tokio.workspace = true
|
gpui_tokio.workspace = true
|
||||||
http_client_tls.workspace = true
|
http_client_tls.workspace = true
|
||||||
image.workspace = true
|
image.workspace = true
|
||||||
|
@ -41,12 +41,7 @@ workspace-hack.workspace = true
|
||||||
|
|
||||||
[target.'cfg(not(all(target_os = "windows", target_env = "gnu")))'.dependencies]
|
[target.'cfg(not(all(target_os = "windows", target_env = "gnu")))'.dependencies]
|
||||||
libwebrtc = { rev = "80bb8f4c9112789f7c24cc98d8423010977806a6", git = "https://github.com/zed-industries/livekit-rust-sdks" }
|
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 = [
|
livekit = { rev = "80bb8f4c9112789f7c24cc98d8423010977806a6", git = "https://github.com/zed-industries/livekit-rust-sdks", features = ["__rustls-tls"] }
|
||||||
"__rustls-tls"
|
|
||||||
] }
|
|
||||||
|
|
||||||
[target.'cfg(any(target_os = "linux", target_os = "freebsd"))'.dependencies]
|
|
||||||
scap.workspace = true
|
|
||||||
|
|
||||||
[target.'cfg(target_os = "macos")'.dependencies]
|
[target.'cfg(target_os = "macos")'.dependencies]
|
||||||
core-foundation.workspace = true
|
core-foundation.workspace = true
|
||||||
|
|
|
@ -336,7 +336,7 @@ pub(crate) async fn capture_local_video_track(
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let capture_stream = capture_source
|
let capture_stream = capture_source
|
||||||
.stream(cx.foreground_executor(), {
|
.stream({
|
||||||
let track_source = track_source.clone();
|
let track_source = track_source.clone();
|
||||||
Box::new(move |frame| {
|
Box::new(move |frame| {
|
||||||
if let Some(buffer) = video_frame_buffer_to_webrtc(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"))]
|
#[cfg(not(target_os = "macos"))]
|
||||||
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")]
|
|
||||||
fn video_frame_buffer_to_webrtc(_frame: ScreenCaptureFrame) -> Option<impl AsRef<dyn VideoBuffer>> {
|
fn video_frame_buffer_to_webrtc(_frame: ScreenCaptureFrame) -> Option<impl AsRef<dyn VideoBuffer>> {
|
||||||
None as Option<Box<dyn VideoBuffer>>
|
None as Option<Box<dyn VideoBuffer>>
|
||||||
}
|
}
|
||||||
|
|
|
@ -299,7 +299,10 @@ impl TitleBar {
|
||||||
let is_screen_sharing = room.is_screen_sharing();
|
let is_screen_sharing = room.is_screen_sharing();
|
||||||
let can_use_microphone = room.can_use_microphone();
|
let can_use_microphone = room.can_use_microphone();
|
||||||
let can_share_projects = room.can_share_projects();
|
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();
|
let mut children = Vec::new();
|
||||||
|
|
||||||
|
|
|
@ -4423,6 +4423,18 @@ impl Workspace {
|
||||||
None
|
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(
|
fn shared_screen_for_peer(
|
||||||
&self,
|
&self,
|
||||||
peer_id: PeerId,
|
peer_id: PeerId,
|
||||||
|
|
|
@ -28,7 +28,6 @@ if [[ -n $apt ]]; then
|
||||||
libasound2-dev
|
libasound2-dev
|
||||||
libfontconfig-dev
|
libfontconfig-dev
|
||||||
libwayland-dev
|
libwayland-dev
|
||||||
libx11-xcb-dev
|
|
||||||
libxkbcommon-x11-dev
|
libxkbcommon-x11-dev
|
||||||
libssl-dev
|
libssl-dev
|
||||||
libzstd-dev
|
libzstd-dev
|
||||||
|
@ -77,7 +76,6 @@ if [[ -n $dnf ]] || [[ -n $yum ]]; then
|
||||||
alsa-lib-devel
|
alsa-lib-devel
|
||||||
fontconfig-devel
|
fontconfig-devel
|
||||||
wayland-devel
|
wayland-devel
|
||||||
libxcb-devel
|
|
||||||
libxkbcommon-x11-devel
|
libxkbcommon-x11-devel
|
||||||
openssl-devel
|
openssl-devel
|
||||||
libzstd-devel
|
libzstd-devel
|
||||||
|
@ -146,7 +144,6 @@ if [[ -n $zyp ]]; then
|
||||||
gzip
|
gzip
|
||||||
jq
|
jq
|
||||||
libvulkan1
|
libvulkan1
|
||||||
libxcb-devel
|
|
||||||
libxkbcommon-devel
|
libxkbcommon-devel
|
||||||
libxkbcommon-x11-devel
|
libxkbcommon-x11-devel
|
||||||
libzstd-devel
|
libzstd-devel
|
||||||
|
@ -177,7 +174,6 @@ if [[ -n $pacman ]]; then
|
||||||
fontconfig
|
fontconfig
|
||||||
wayland
|
wayland
|
||||||
libgit2
|
libgit2
|
||||||
libxcb
|
|
||||||
libxkbcommon-x11
|
libxkbcommon-x11
|
||||||
openbsd-netcat
|
openbsd-netcat
|
||||||
openssl
|
openssl
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue