diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 90d105880f94ee..b37d17e6a23b65 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -44,6 +44,9 @@ jobs: run_tests_linux: if: (github.repository_owner == 'zed-industries' || github.repository_owner == 'zed-extensions') runs-on: namespace-profile-16x32-ubuntu-2204 + env: + CC: clang + CXX: clang++ steps: - name: steps::checkout_repo uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 @@ -123,6 +126,9 @@ jobs: check_scripts: if: (github.repository_owner == 'zed-industries' || github.repository_owner == 'zed-extensions') runs-on: namespace-profile-2x4-ubuntu-2404 + env: + CC: clang + CXX: clang++ steps: - name: steps::checkout_repo uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 diff --git a/.github/workflows/run_tests.yml b/.github/workflows/run_tests.yml index 323dd7fd1b52eb..44912d70c345d4 100644 --- a/.github/workflows/run_tests.yml +++ b/.github/workflows/run_tests.yml @@ -61,6 +61,9 @@ jobs: check_style: if: (github.repository_owner == 'zed-industries' || github.repository_owner == 'zed-extensions') runs-on: namespace-profile-4x8-ubuntu-2204 + env: + CC: clang + CXX: clang++ steps: - name: steps::checkout_repo uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 @@ -130,6 +133,9 @@ jobs: - orchestrate if: needs.orchestrate.outputs.run_tests == 'true' runs-on: namespace-profile-16x32-ubuntu-2204 + env: + CC: clang + CXX: clang++ steps: - name: steps::checkout_repo uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 @@ -213,6 +219,9 @@ jobs: - orchestrate if: needs.orchestrate.outputs.run_tests == 'true' runs-on: namespace-profile-16x32-ubuntu-2204 + env: + CC: clang + CXX: clang++ steps: - name: steps::checkout_repo uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 @@ -252,6 +261,9 @@ jobs: - orchestrate if: needs.orchestrate.outputs.run_tests == 'true' runs-on: namespace-profile-8x16-ubuntu-2204 + env: + CC: clang + CXX: clang++ steps: - name: steps::checkout_repo uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 @@ -292,6 +304,9 @@ jobs: - orchestrate if: needs.orchestrate.outputs.run_tests == 'true' runs-on: namespace-profile-2x4-ubuntu-2404 + env: + CC: clang + CXX: clang++ steps: - name: steps::checkout_repo uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 @@ -324,6 +339,9 @@ jobs: - orchestrate if: needs.orchestrate.outputs.run_docs == 'true' runs-on: namespace-profile-8x16-ubuntu-2204 + env: + CC: clang + CXX: clang++ steps: - name: steps::checkout_repo uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 @@ -394,6 +412,9 @@ jobs: - orchestrate if: needs.orchestrate.outputs.run_action_checks == 'true' runs-on: namespace-profile-2x4-ubuntu-2404 + env: + CC: clang + CXX: clang++ steps: - name: steps::checkout_repo uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 diff --git a/Cargo.lock b/Cargo.lock index e2613dc2776787..e409763bbe95bd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1221,9 +1221,9 @@ dependencies = [ [[package]] name = "async-tungstenite" -version = "0.31.0" +version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee88b4c88ac8c9ea446ad43498955750a4bbe64c4392f21ccfe5d952865e318f" +checksum = "b6f89c129ab749940f95509d84950c62092c8b4bc6e386ddb162229037a6ec91" dependencies = [ "atomic-waker", "futures-core", @@ -1235,7 +1235,7 @@ dependencies = [ "rustls-pki-types", "tokio", "tokio-rustls 0.26.2", - "tungstenite 0.27.0", + "tungstenite 0.28.0", ] [[package]] @@ -2092,7 +2092,7 @@ dependencies = [ "bitflags 2.9.4", "cexpr", "clang-sys", - "itertools 0.12.1", + "itertools 0.10.5", "log", "prettyplease", "proc-macro2", @@ -2112,7 +2112,7 @@ dependencies = [ "bitflags 2.9.4", "cexpr", "clang-sys", - "itertools 0.12.1", + "itertools 0.10.5", "log", "prettyplease", "proc-macro2", @@ -2202,7 +2202,7 @@ dependencies = [ "ash-window", "bitflags 2.9.4", "bytemuck", - "codespan-reporting 0.12.0", + "codespan-reporting", "glow", "gpu-alloc", "gpu-alloc-ash", @@ -2323,6 +2323,16 @@ dependencies = [ "unicode-segmentation", ] +[[package]] +name = "bmrng" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d54df9073108f1558f90ae6c5bf5ab9c917c4185f5527b280c87a993cbead0ac" +dependencies = [ + "futures-core", + "tokio", +] + [[package]] name = "borrow-or-share" version = "0.2.2" @@ -2605,6 +2615,7 @@ dependencies = [ "gpui_tokio", "http_client", "language", + "libwebrtc", "livekit_client", "log", "postage", @@ -2885,6 +2896,16 @@ dependencies = [ "target-lexicon 0.12.16", ] +[[package]] +name = "cfg-expr" +version = "0.20.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9acd0bdbbf4b2612d09f52ba61da432140cb10930354079d0d53fafc12968726" +dependencies = [ + "smallvec", + "target-lexicon 0.13.3", +] + [[package]] name = "cfg-if" version = "1.0.4" @@ -3302,17 +3323,6 @@ dependencies = [ "unicode-width", ] -[[package]] -name = "codespan-reporting" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba7a06c0b31fff5ff2e1e7d37dbf940864e2a974b336e1a2938d10af6e8fb283" -dependencies = [ - "serde", - "termcolor", - "unicode-width", -] - [[package]] name = "codestral" version = "0.1.0" @@ -3834,19 +3844,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.4", - "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" @@ -4443,9 +4440,9 @@ checksum = "f27ae1dd37df86211c42e150270f82743308803d90a6f6e6651cd730d5e1732f" [[package]] name = "cxx" -version = "1.0.187" +version = "1.0.186" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8465678d499296e2cbf9d3acf14307458fd69b471a31b65b3c519efe8b5e187" +checksum = "4e9c4fe7f2f5dc5c62871a1b43992d197da6fa1394656a94276ac2894a90a6fe" dependencies = [ "cc", "cxx-build", @@ -4458,12 +4455,12 @@ dependencies = [ [[package]] name = "cxx-build" -version = "1.0.187" +version = "1.0.186" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d74b6bcf49ebbd91f1b1875b706ea46545032a14003b5557b7dfa4bbeba6766e" +checksum = "b5cf2909d37d80633ddd208676fc27c2608a7f035fff69c882421168038b26dd" dependencies = [ "cc", - "codespan-reporting 0.13.0", + "codespan-reporting", "indexmap", "proc-macro2", "quote", @@ -4473,12 +4470,12 @@ dependencies = [ [[package]] name = "cxxbridge-cmd" -version = "1.0.187" +version = "1.0.186" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94ca2ad69673c4b35585edfa379617ac364bccd0ba0adf319811ba3a74ffa48a" +checksum = "077f5ee3d3bfd8d27f83208fdaa96ddd50af7f096c77077cc4b94da10bfacefd" dependencies = [ "clap", - "codespan-reporting 0.13.0", + "codespan-reporting", "indexmap", "proc-macro2", "quote", @@ -4487,19 +4484,20 @@ dependencies = [ [[package]] name = "cxxbridge-flags" -version = "1.0.187" +version = "1.0.186" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29b52102aa395386d77d322b3a0522f2035e716171c2c60aa87cc5e9466e523" +checksum = "b0108748615125b9f2e915dfafdffcbdabbca9b15102834f6d7e9a768f2f2864" [[package]] name = "cxxbridge-macro" -version = "1.0.187" +version = "1.0.186" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a8ebf0b6138325af3ec73324cb3a48b64d57721f17291b151206782e61f66cd" +checksum = "e6e896681ef9b8dc462cfa6961d61909704bde0984b30bcb4082fe102b478890" dependencies = [ "indexmap", "proc-macro2", "quote", + "rustversion", "syn 2.0.106", ] @@ -4761,7 +4759,7 @@ dependencies = [ "serde_json", "serde_json_lenient", "settings", - "sysinfo 0.37.2", + "sysinfo", "task", "tasks_ui", "telemetry", @@ -5054,7 +5052,7 @@ dependencies = [ "libc", "option-ext", "redox_users 0.5.2", - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -5660,7 +5658,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -7011,6 +7009,19 @@ version = "0.32.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7" +[[package]] +name = "gio-sys" +version = "0.21.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "171ed2f6dd927abbe108cfd9eebff2052c335013f5879d55bab0dc1dee19b706" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps 7.0.7", + "windows-sys 0.59.0", +] + [[package]] name = "git" version = "0.1.0" @@ -7137,6 +7148,50 @@ dependencies = [ "zlog", ] +[[package]] +name = "glib" +version = "0.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b9dbecb1c33e483a98be4acfea2ab369e1c28f517c6eadb674537409c25c4b2" +dependencies = [ + "bitflags 2.9.4", + "futures-channel", + "futures-core", + "futures-executor", + "futures-task", + "futures-util", + "gio-sys", + "glib-macros", + "glib-sys", + "gobject-sys", + "libc", + "memchr", + "smallvec", +] + +[[package]] +name = "glib-macros" +version = "0.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "880e524e0085f3546cfb38532b2c202c0d64741d9977a6e4aa24704bfc9f19fb" +dependencies = [ + "heck 0.5.0", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.106", +] + +[[package]] +name = "glib-sys" +version = "0.21.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d09d3d0fddf7239521674e57b0465dfbd844632fec54f059f7f56112e3f927e1" +dependencies = [ + "libc", + "system-deps 7.0.7", +] + [[package]] name = "glob" version = "0.3.3" @@ -7203,6 +7258,17 @@ dependencies = [ "workspace", ] +[[package]] +name = "gobject-sys" +version = "0.21.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "538e41d8776173ec107e7b0f2aceced60abc368d7e1d81c1f0e2ecd35f59080d" +dependencies = [ + "glib-sys", + "libc", + "system-deps 7.0.7", +] + [[package]] name = "goblin" version = "0.8.2" @@ -7359,7 +7425,6 @@ dependencies = [ "x11rb", "xkbcommon", "zed-font-kit", - "zed-scap", "zed-xim", ] @@ -7947,7 +8012,7 @@ dependencies = [ "libc", "percent-encoding", "pin-project-lite", - "socket2 0.6.1", + "socket2 0.5.10", "tokio", "tower-service", "tracing", @@ -7965,7 +8030,7 @@ dependencies = [ "js-sys", "log", "wasm-bindgen", - "windows-core 0.62.2", + "windows-core 0.58.0", ] [[package]] @@ -9180,10 +9245,11 @@ dependencies = [ [[package]] name = "libwebrtc" -version = "0.3.10" -source = "git+https://github.com/zed-industries/livekit-rust-sdks?rev=5f04705ac3f356350ae31534ffbc476abc9ea83d#5f04705ac3f356350ae31534ffbc476abc9ea83d" +version = "0.3.19" +source = "git+https://github.com/Be-ing/rust-sdks?branch=zed#01dbdc073bc1559a209e4d7ce48737361a2d7af7" dependencies = [ "cxx", + "glib", "jni", "js-sys", "lazy_static", @@ -9280,9 +9346,11 @@ checksum = "f5e54036fe321fd421e10d732f155734c4e4afd610dd556d9a82833ab3ee0bed" [[package]] name = "livekit" -version = "0.7.8" -source = "git+https://github.com/zed-industries/livekit-rust-sdks?rev=5f04705ac3f356350ae31534ffbc476abc9ea83d#5f04705ac3f356350ae31534ffbc476abc9ea83d" +version = "0.7.24" +source = "git+https://github.com/Be-ing/rust-sdks?branch=zed#01dbdc073bc1559a209e4d7ce48737361a2d7af7" dependencies = [ + "bmrng", + "bytes 1.10.1", "chrono", "futures-util", "lazy_static", @@ -9303,11 +9371,13 @@ dependencies = [ [[package]] name = "livekit-api" -version = "0.4.2" -source = "git+https://github.com/zed-industries/livekit-rust-sdks?rev=5f04705ac3f356350ae31534ffbc476abc9ea83d#5f04705ac3f356350ae31534ffbc476abc9ea83d" +version = "0.4.9" +source = "git+https://github.com/Be-ing/rust-sdks?branch=zed#01dbdc073bc1559a209e4d7ce48737361a2d7af7" dependencies = [ + "base64 0.21.7", "futures-util", - "http 0.2.12", + "http 1.3.1", + "jsonwebtoken", "livekit-protocol", "livekit-runtime", "log", @@ -9315,20 +9385,21 @@ dependencies = [ "pbjson-types", "prost 0.12.6", "rand 0.9.2", - "reqwest 0.11.27", + "reqwest 0.12.24", "scopeguard", "serde", + "serde_json", "sha2", "thiserror 1.0.69", "tokio", - "tokio-tungstenite 0.26.2", + "tokio-tungstenite 0.28.0", "url", ] [[package]] name = "livekit-protocol" -version = "0.3.9" -source = "git+https://github.com/zed-industries/livekit-rust-sdks?rev=5f04705ac3f356350ae31534ffbc476abc9ea83d#5f04705ac3f356350ae31534ffbc476abc9ea83d" +version = "0.5.1" +source = "git+https://github.com/Be-ing/rust-sdks?branch=zed#01dbdc073bc1559a209e4d7ce48737361a2d7af7" dependencies = [ "futures-util", "livekit-runtime", @@ -9336,7 +9407,6 @@ dependencies = [ "pbjson", "pbjson-types", "prost 0.12.6", - "prost-types 0.12.6", "serde", "thiserror 1.0.69", "tokio", @@ -9345,7 +9415,7 @@ dependencies = [ [[package]] name = "livekit-runtime" version = "0.4.0" -source = "git+https://github.com/zed-industries/livekit-rust-sdks?rev=5f04705ac3f356350ae31534ffbc476abc9ea83d#5f04705ac3f356350ae31534ffbc476abc9ea83d" +source = "git+https://github.com/Be-ing/rust-sdks?branch=zed#01dbdc073bc1559a209e4d7ce48737361a2d7af7" dependencies = [ "tokio", "tokio-stream", @@ -9399,10 +9469,10 @@ dependencies = [ "sha2", "simplelog", "smallvec", - "tokio-tungstenite 0.26.2", + "tokio", + "tokio-tungstenite 0.28.0", "ui", "util", - "zed-scap", ] [[package]] @@ -10162,7 +10232,7 @@ dependencies = [ "bit-set 0.8.0", "bitflags 2.9.4", "cfg_aliases 0.2.1", - "codespan-reporting 0.12.0", + "codespan-reporting", "half", "hashbrown 0.15.5", "hexf-parse", @@ -10473,7 +10543,7 @@ version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -10663,18 +10733,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]] @@ -10814,24 +10872,6 @@ dependencies = [ "objc2-quartz-core", ] -[[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" @@ -13221,7 +13261,7 @@ checksum = "22505a5c94da8e3b7c2996394d1c933236c4d743e81a410bcca4e6989fc066a4" dependencies = [ "bytes 1.10.1", "heck 0.5.0", - "itertools 0.12.1", + "itertools 0.10.5", "log", "multimap 0.10.1", "once_cell", @@ -13254,7 +13294,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81bddcdb20abf9501610992b6759a4c888aef7d1a7247ef75e2404275ac24af1" dependencies = [ "anyhow", - "itertools 0.12.1", + "itertools 0.10.5", "proc-macro2", "quote", "syn 2.0.106", @@ -13431,15 +13471,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.37.5" @@ -13472,7 +13503,7 @@ dependencies = [ "quinn-udp", "rustc-hash 2.1.1", "rustls 0.23.33", - "socket2 0.6.1", + "socket2 0.5.10", "thiserror 2.0.17", "tokio", "tracing", @@ -13509,9 +13540,9 @@ dependencies = [ "cfg_aliases 0.2.1", "libc", "once_cell", - "socket2 0.6.1", + "socket2 0.5.10", "tracing", - "windows-sys 0.60.2", + "windows-sys 0.59.0", ] [[package]] @@ -13648,7 +13679,7 @@ dependencies = [ "rand 0.8.5", "rand_chacha 0.3.1", "simd_helpers", - "system-deps", + "system-deps 6.2.2", "thiserror 1.0.69", "v_frame", "wasm-bindgen", @@ -14034,7 +14065,7 @@ dependencies = [ "settings", "shellexpand 2.1.2", "smol", - "sysinfo 0.37.2", + "sysinfo", "task", "theme", "thiserror 2.0.17", @@ -14121,7 +14152,6 @@ dependencies = [ "http 0.2.12", "http-body 0.4.6", "hyper 0.14.32", - "hyper-rustls 0.24.2", "hyper-tls", "ipnet", "js-sys", @@ -14131,8 +14161,6 @@ dependencies = [ "once_cell", "percent-encoding", "pin-project-lite", - "rustls 0.21.12", - "rustls-native-certs 0.6.3", "rustls-pemfile 1.0.4", "serde", "serde_json", @@ -14141,7 +14169,6 @@ dependencies = [ "system-configuration 0.5.1", "tokio", "tokio-native-tls", - "tokio-rustls 0.24.1", "tower-service", "url", "wasm-bindgen", @@ -14598,7 +14625,7 @@ dependencies = [ "errno 0.3.14", "libc", "linux-raw-sys 0.11.0", - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -14904,29 +14931,6 @@ version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d68f2ec51b097e4c1a75b681a8bec621909b5e91f15bb7b840c4f2f7b01148b2" -[[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 = "scroll" version = "0.12.0" @@ -16773,20 +16777,6 @@ dependencies = [ "walkdir", ] -[[package]] -name = "sysinfo" -version = "0.31.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "355dbe4f8799b304b05e1b0f05fc59b2a18d36645cf169607da45bde2f69a1be" -dependencies = [ - "core-foundation-sys", - "libc", - "memchr", - "ntapi", - "rayon", - "windows 0.57.0", -] - [[package]] name = "sysinfo" version = "0.37.2" @@ -16849,13 +16839,26 @@ version = "6.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3e535eb8dded36d55ec13eddacd30dec501792ff23a0b1682c38601b8cf2349" dependencies = [ - "cfg-expr", + "cfg-expr 0.15.8", "heck 0.5.0", "pkg-config", "toml 0.8.23", "version-compare", ] +[[package]] +name = "system-deps" +version = "7.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c8f33736f986f16d69b6cb8b03f55ddcad5c41acc4ccc39dd88e84aa805e7f" +dependencies = [ + "cfg-expr 0.20.4", + "heck 0.5.0", + "pkg-config", + "toml 0.9.8", + "version-compare", +] + [[package]] name = "system-interface" version = "0.27.3" @@ -16883,7 +16886,7 @@ dependencies = [ "pciid-parser", "release_channel", "serde", - "sysinfo 0.37.2", + "sysinfo", ] [[package]] @@ -16936,18 +16939,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" @@ -17044,7 +17035,7 @@ dependencies = [ "getrandom 0.3.4", "once_cell", "rustix 1.1.2", - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -17086,7 +17077,7 @@ dependencies = [ "serde", "settings", "smol", - "sysinfo 0.37.2", + "sysinfo", "task", "theme", "thiserror 2.0.17", @@ -17459,6 +17450,7 @@ dependencies = [ "db", "gpui", "http_client", + "libwebrtc", "notifications", "pretty_assertions", "project", @@ -17598,9 +17590,9 @@ dependencies = [ [[package]] name = "tokio-tungstenite" -version = "0.26.2" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a9daff607c6d2bf6c16fd681ccb7eecc83e4e2cdc1ca067ffaadfca5de7f084" +checksum = "d25a406cddcc431a75d3d9afc6a7c0f7428d4891dd973e4d54c56b46127bf857" dependencies = [ "futures-util", "log", @@ -17608,7 +17600,7 @@ dependencies = [ "rustls-pki-types", "tokio", "tokio-rustls 0.26.2", - "tungstenite 0.26.2", + "tungstenite 0.28.0", ] [[package]] @@ -18247,28 +18239,9 @@ dependencies = [ [[package]] name = "tungstenite" -version = "0.26.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4793cb5e56680ecbb1d843515b23b6de9a75eb04b66643e256a396d43be33c13" -dependencies = [ - "bytes 1.10.1", - "data-encoding", - "http 1.3.1", - "httparse", - "log", - "rand 0.9.2", - "rustls 0.23.33", - "rustls-pki-types", - "sha1", - "thiserror 2.0.17", - "utf-8", -] - -[[package]] -name = "tungstenite" -version = "0.27.0" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eadc29d668c91fcc564941132e17b28a7ceb2f3ebf0b9dae3e03fd7a6748eb0d" +checksum = "8628dcc84e5a09eb3d8423d6cb682965dea9133204e8fb3efee74c2a0c259442" dependencies = [ "bytes 1.10.1", "data-encoding", @@ -19638,25 +19611,27 @@ dependencies = [ [[package]] name = "webrtc-sys" -version = "0.3.7" -source = "git+https://github.com/zed-industries/livekit-rust-sdks?rev=5f04705ac3f356350ae31534ffbc476abc9ea83d#5f04705ac3f356350ae31534ffbc476abc9ea83d" +version = "0.3.16" +source = "git+https://github.com/Be-ing/rust-sdks?branch=zed#01dbdc073bc1559a209e4d7ce48737361a2d7af7" dependencies = [ "cc", "cxx", "cxx-build", "glob", "log", + "pkg-config", "webrtc-sys-build", ] [[package]] name = "webrtc-sys-build" -version = "0.3.6" -source = "git+https://github.com/zed-industries/livekit-rust-sdks?rev=5f04705ac3f356350ae31534ffbc476abc9ea83d#5f04705ac3f356350ae31534ffbc476abc9ea83d" +version = "0.3.11" +source = "git+https://github.com/Be-ing/rust-sdks?branch=zed#01dbdc073bc1559a209e4d7ce48737361a2d7af7" dependencies = [ + "anyhow", "fs2", "regex", - "reqwest 0.11.27", + "reqwest 0.12.24", "scratch", "semver", "zip 0.6.6", @@ -19766,7 +19741,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.48.0", ] [[package]] @@ -19803,16 +19778,6 @@ dependencies = [ "windows-targets 0.52.6", ] -[[package]] -name = "windows" -version = "0.57.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12342cb4d8e3b046f3d80effd474a7a02447231330ef77d71daa6fbc40681143" -dependencies = [ - "windows-core 0.57.0", - "windows-targets 0.52.6", -] - [[package]] name = "windows" version = "0.58.0" @@ -19836,20 +19801,6 @@ dependencies = [ "windows-numerics", ] -[[package]] -name = "windows-capture" -version = "1.4.3" -source = "git+https://github.com/zed-industries/windows-capture.git?rev=f0d6c1b6691db75461b732f6d5ff56eed002eeb9#f0d6c1b6691db75461b732f6d5ff56eed002eeb9" -dependencies = [ - "clap", - "ctrlc", - "parking_lot", - "rayon", - "thiserror 2.0.17", - "windows 0.61.3", - "windows-future", -] - [[package]] name = "windows-collections" version = "0.2.0" @@ -19869,18 +19820,6 @@ dependencies = [ "windows-targets 0.52.6", ] -[[package]] -name = "windows-core" -version = "0.57.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2ed2439a290666cd67ecce2b0ffaad89c2a56b976b736e6ece670297897832d" -dependencies = [ - "windows-implement 0.57.0", - "windows-interface 0.57.0", - "windows-result 0.1.2", - "windows-targets 0.52.6", -] - [[package]] name = "windows-core" version = "0.58.0" @@ -19907,19 +19846,6 @@ dependencies = [ "windows-strings 0.4.2", ] -[[package]] -name = "windows-core" -version = "0.62.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" -dependencies = [ - "windows-implement 0.60.2", - "windows-interface 0.59.3", - "windows-link 0.2.1", - "windows-result 0.4.1", - "windows-strings 0.5.1", -] - [[package]] name = "windows-future" version = "0.2.1" @@ -19931,17 +19857,6 @@ dependencies = [ "windows-threading", ] -[[package]] -name = "windows-implement" -version = "0.57.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9107ddc059d5b6fbfbffdfa7a7fe3e22a226def0b2608f72e9d552763d3e1ad7" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.106", -] - [[package]] name = "windows-implement" version = "0.58.0" @@ -19964,17 +19879,6 @@ dependencies = [ "syn 2.0.106", ] -[[package]] -name = "windows-interface" -version = "0.57.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29bee4b38ea3cde66011baa44dba677c432a78593e202392d1e9070cf2a7fca7" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.106", -] - [[package]] name = "windows-interface" version = "0.58.0" @@ -20830,16 +20734,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" @@ -20889,18 +20783,6 @@ dependencies = [ "libc", ] -[[package]] -name = "xcb" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f07c123b796139bfe0603e654eaf08e132e52387ba95b252c78bad3640ba37ea" -dependencies = [ - "bitflags 1.3.2", - "libc", - "quick-xml 0.30.0", - "x11", -] - [[package]] name = "xcursor" version = "0.3.10" @@ -21273,7 +21155,7 @@ dependencies = [ "snippets_ui", "supermaven", "svg_preview", - "sysinfo 0.37.2", + "sysinfo", "system_specs", "tab_switcher", "task", @@ -21386,27 +21268,6 @@ dependencies = [ "windows-registry 0.4.0", ] -[[package]] -name = "zed-scap" -version = "0.0.8-zed" -source = "git+https://github.com/zed-industries/scap?rev=4afea48c3b002197176fb19cd0f9b180dd36eaac#4afea48c3b002197176fb19cd0f9b180dd36eaac" -dependencies = [ - "anyhow", - "cocoa 0.25.0", - "core-graphics-helmer-fork", - "log", - "objc", - "rand 0.8.5", - "screencapturekit", - "screencapturekit-sys", - "sysinfo 0.31.4", - "tao-core-video-sys", - "windows 0.61.3", - "windows-capture", - "x11", - "xcb", -] - [[package]] name = "zed-xim" version = "0.4.0-zed" diff --git a/Cargo.toml b/Cargo.toml index 63303678ebfbdb..2cc1ca352b0199 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -460,7 +460,7 @@ async-recursion = "1.0.0" async-tar = "0.5.1" async-task = "4.7" async-trait = "0.1" -async-tungstenite = "0.31.0" +async-tungstenite = "0.32.0" async_zip = { version = "0.0.18", features = ["deflate", "deflate64"] } aws-config = { version = "1.6.1", features = ["behavior-version-latest"] } aws-credential-types = { version = "1.2.2", features = [ @@ -540,6 +540,10 @@ jupyter-websocket-client = "0.15.0" libc = "0.2" libsqlite3-sys = { version = "0.30.1", features = ["bundled"] } linkify = "0.10.0" +libwebrtc = { branch = "zed", git = "https://github.com/Be-ing/rust-sdks" } +livekit = { branch = "zed", git = "https://github.com/Be-ing/rust-sdks", features = [ + "__rustls-tls" +] } log = { version = "0.4.16", features = ["kv_unstable_serde", "serde"] } lsp-types = { git = "https://github.com/zed-industries/lsp-types", rev = "b71ab4eeb27d9758be8092020a46fe33fbca4e33" } mach2 = "0.5" @@ -627,8 +631,6 @@ rust-embed = { version = "8.4", features = ["include-exclude"] } rustc-hash = "2.1.0" rustls = { version = "0.23.26" } rustls-platform-verifier = "0.5.0" -# WARNING: If you change this, you must also publish a new version of zed-scap to crates.io -scap = { git = "https://github.com/zed-industries/scap", rev = "4afea48c3b002197176fb19cd0f9b180dd36eaac", default-features = false, package = "zed-scap", version = "0.0.8-zed" } schemars = { version = "1.0", features = ["indexmap2"] } semver = "1.0" serde = { version = "1.0.221", features = ["derive", "rc"] } @@ -671,7 +673,7 @@ time = { version = "0.3", features = [ ] } tiny_http = "0.8" tokio = { version = "1" } -tokio-tungstenite = { version = "0.26", features = ["__rustls-tls"] } +tokio-tungstenite = { version = "0.28", features = ["__rustls-tls"] } toml = "0.8" toml_edit = { version = "0.22", default-features = false, features = ["display", "parse", "serde"] } tower-http = "0.4.4" @@ -781,7 +783,6 @@ features = [ [patch.crates-io] notify = { git = "https://github.com/zed-industries/notify.git", rev = "b4588b2e5aee68f4c0e100f140e808cbce7b1419" } notify-types = { git = "https://github.com/zed-industries/notify.git", rev = "b4588b2e5aee68f4c0e100f140e808cbce7b1419" } -windows-capture = { git = "https://github.com/zed-industries/windows-capture.git", rev = "f0d6c1b6691db75461b732f6d5ff56eed002eeb9" } calloop = { git = "https://github.com/zed-industries/calloop" } [profile.dev] diff --git a/crates/audio/Cargo.toml b/crates/audio/Cargo.toml index 2aee764007a791..0d78f08038dca2 100644 --- a/crates/audio/Cargo.toml +++ b/crates/audio/Cargo.toml @@ -29,4 +29,4 @@ thiserror.workspace = true util.workspace = true [target.'cfg(not(any(all(target_os = "windows", target_env = "gnu"), target_os = "freebsd")))'.dependencies] -libwebrtc = { rev = "5f04705ac3f356350ae31534ffbc476abc9ea83d", git = "https://github.com/zed-industries/livekit-rust-sdks" } +libwebrtc = { workspace = true } diff --git a/crates/call/Cargo.toml b/crates/call/Cargo.toml index ff034f914b0be4..f0c3aef8e65c97 100644 --- a/crates/call/Cargo.toml +++ b/crates/call/Cargo.toml @@ -30,8 +30,9 @@ collections.workspace = true fs.workspace = true futures.workspace = true feature_flags.workspace = true -gpui = { workspace = true, features = ["screen-capture"] } +gpui.workspace = true language.workspace = true +libwebrtc.workspace = true log.workspace = true postage.workspace = true project.workspace = true diff --git a/crates/call/src/call_impl/room.rs b/crates/call/src/call_impl/room.rs index 2a540619d4576e..019f4b04cadf7b 100644 --- a/crates/call/src/call_impl/room.rs +++ b/crates/call/src/call_impl/room.rs @@ -13,13 +13,16 @@ use feature_flags::FeatureFlagAppExt; use fs::Fs; use futures::StreamExt; use gpui::{ - App, AppContext as _, AsyncApp, Context, Entity, EventEmitter, FutureExt as _, - ScreenCaptureSource, ScreenCaptureStream, Task, Timeout, WeakEntity, + App, AppContext as _, AsyncApp, Context, Entity, EventEmitter, FutureExt as _, Task, Timeout, + WeakEntity, }; use gpui_tokio::Tokio; use language::LanguageRegistry; +use libwebrtc::desktop_capturer::CaptureSource; use livekit::{LocalTrackPublication, ParticipantIdentity, RoomEvent}; -use livekit_client::{self as livekit, AudioStream, TrackSid}; +use livekit_client::{ + self as livekit, AudioStream, ScreenCaptureStreamHandle, TrackSid, screen_capture_sources, +}; use postage::{sink::Sink, stream::Stream, watch}; use project::Project; use settings::Settings as _; @@ -87,6 +90,7 @@ pub struct Room { pending_room_update: Option>, maintain_connection: Option>>, created: Instant, + available_screens: Vec, } impl EventEmitter for Room {} @@ -159,6 +163,7 @@ impl Room { room_update_completed_tx, room_update_completed_rx, created: cx.background_executor().now(), + available_screens: screen_capture_sources(), } } @@ -1267,9 +1272,7 @@ impl Room { pub fn shared_screen_id(&self) -> Option { self.live_kit.as_ref().and_then(|lk| match lk.screen_track { - LocalTrack::Published { ref _stream, .. } => { - _stream.metadata().ok().map(|meta| meta.id) - } + LocalTrack::Published { ref _stream, .. } => Some(_stream.screen_id), _ => None, }) } @@ -1376,7 +1379,7 @@ impl Room { } live_kit.microphone_track = LocalTrack::Published { track_publication: publication, - _stream: Box::new(stream), + _stream: stream, }; cx.notify(); } @@ -1396,9 +1399,10 @@ impl Room { }) } + /// Only tests should pass None for `source`. pub fn share_screen( &mut self, - source: Rc, + source: Option, cx: &mut Context, ) -> Task> { if self.status.is_offline() { @@ -1408,6 +1412,9 @@ impl Room { return Task::ready(Err(anyhow!("screen was already shared"))); } + #[cfg(not(feature = "test-support"))] + assert!(source.is_some()); + let (participant, publish_id) = if let Some(live_kit) = self.live_kit.as_mut() { let publish_id = post_inc(&mut live_kit.next_publish_id); live_kit.screen_track = LocalTrack::Pending { publish_id }; @@ -1418,9 +1425,12 @@ impl Room { }; cx.spawn(async move |this, cx| { - let publication = participant.publish_screenshare_track(&*source, cx).await; + let publication = participant.publish_screenshare_track(source, cx).await; this.update(cx, |this, cx| { + // If screens were added or removed since joining the room or unsharing the screen, + // let the user pick them from the screen selection menu. + this.available_screens = screen_capture_sources(); let live_kit = this .live_kit .as_mut() @@ -1511,6 +1521,9 @@ impl Room { pub fn unshare_screen(&mut self, play_sound: bool, cx: &mut Context) -> Result<()> { anyhow::ensure!(!self.status.is_offline(), "room is offline"); + // If screens were added or removed during the screen share, ensure they are available + // to share from the screen picker menu when re-enabling screen sharing. + self.available_screens = screen_capture_sources(); let live_kit = self .live_kit @@ -1592,6 +1605,10 @@ impl Room { } } } + + pub fn available_screens(&self) -> &Vec { + &self.available_screens + } } fn spawn_room_connection( @@ -1644,7 +1661,7 @@ fn spawn_room_connection( struct LiveKitRoom { room: Rc, - screen_track: LocalTrack, + screen_track: LocalTrack, microphone_track: LocalTrack, /// Tracks whether we're currently in a muted state due to auto-mute from deafening or manual mute performed by user. muted_by_user: bool, @@ -1684,7 +1701,7 @@ impl LiveKitRoom { } #[derive(Default)] -enum LocalTrack { +enum LocalTrack { #[default] None, Pending { @@ -1692,7 +1709,7 @@ enum LocalTrack { }, Published { track_publication: LocalTrackPublication, - _stream: Box, + _stream: Stream, }, } diff --git a/crates/collab/src/tests/following_tests.rs b/crates/collab/src/tests/following_tests.rs index f3827b6f119539..23cae820ff1e37 100644 --- a/crates/collab/src/tests/following_tests.rs +++ b/crates/collab/src/tests/following_tests.rs @@ -463,7 +463,6 @@ async fn test_basic_following( // #[cfg(all(not(target_os = "macos"), not(target_os = "windows")))] { use crate::rpc::RECONNECT_TIMEOUT; - use gpui::TestScreenCaptureSource; use workspace::{ dock::{DockPosition, test::TestPanel}, item::test::TestItem, @@ -471,25 +470,15 @@ async fn test_basic_following( }; // Client B activates an external window, which causes a new screen-sharing item to be added to the pane. - let display = TestScreenCaptureSource::new(); active_call_b .update(cx_b, |call, cx| call.set_location(None, cx)) .await .unwrap(); - cx_b.set_screen_capture_sources(vec![display]); - let source = cx_b - .read(|cx| cx.screen_capture_sources()) - .await - .unwrap() - .unwrap() - .into_iter() - .next() - .unwrap(); active_call_b .update(cx_b, |call, cx| { call.room() .unwrap() - .update(cx, |room, cx| room.share_screen(source, cx)) + .update(cx, |room, cx| room.share_screen(None, cx)) }) .await .unwrap(); diff --git a/crates/collab/src/tests/integration_tests.rs b/crates/collab/src/tests/integration_tests.rs index fcda8688d427f3..012202e0a6f833 100644 --- a/crates/collab/src/tests/integration_tests.rs +++ b/crates/collab/src/tests/integration_tests.rs @@ -274,23 +274,13 @@ async fn test_basic_calls( ); // User A shares their screen - let display = gpui::TestScreenCaptureSource::new(); let events_b = active_call_events(cx_b); let events_c = active_call_events(cx_c); - cx_a.set_screen_capture_sources(vec![display]); - let screen_a = cx_a - .update(|cx| cx.screen_capture_sources()) - .await - .unwrap() - .unwrap() - .into_iter() - .next() - .unwrap(); active_call_a .update(cx_a, |call, cx| { call.room() .unwrap() - .update(cx, |room, cx| room.share_screen(screen_a, cx)) + .update(cx, |room, cx| room.share_screen(None, cx)) }) .await .unwrap(); @@ -6439,22 +6429,11 @@ async fn test_join_call_after_screen_was_shared( assert_eq!(call_b.calling_user.github_login, "user_a"); // User A shares their screen - let display = gpui::TestScreenCaptureSource::new(); - cx_a.set_screen_capture_sources(vec![display]); - let screen_a = cx_a - .update(|cx| cx.screen_capture_sources()) - .await - .unwrap() - .unwrap() - .into_iter() - .next() - .unwrap(); - active_call_a .update(cx_a, |call, cx| { call.room() .unwrap() - .update(cx, |room, cx| room.share_screen(screen_a, cx)) + .update(cx, |room, cx| room.share_screen(None, cx)) }) .await .unwrap(); diff --git a/crates/collab_ui/src/collab_panel.rs b/crates/collab_ui/src/collab_panel.rs index 618348307f1270..2aef7743deb4ca 100644 --- a/crates/collab_ui/src/collab_panel.rs +++ b/crates/collab_ui/src/collab_panel.rs @@ -152,13 +152,10 @@ pub fn init(cx: &mut App) { if room.is_sharing_screen() { room.unshare_screen(true, cx).ok(); } else { - let sources = cx.screen_capture_sources(); - + let first_screen = room.available_screens().first().map(|s| s.clone()); cx.spawn(async move |room, cx| { - let sources = sources.await??; - let first = sources.into_iter().next(); - if let Some(first) = first { - room.update(cx, |room, cx| room.share_screen(first, cx))? + if let Some(first) = first_screen { + room.update(cx, |room, cx| room.share_screen(Some(first), cx))? .await } else { Ok(()) diff --git a/crates/gpui/Cargo.toml b/crates/gpui/Cargo.toml index f8c668cbe71ccc..dc7d914a57f989 100644 --- a/crates/gpui/Cargo.toml +++ b/crates/gpui/Cargo.toml @@ -73,10 +73,6 @@ x11 = [ "x11-clipboard", "filedescriptor", "open", - "scap?/x11", -] -screen-capture = [ - "scap", ] windows-manifest = [] @@ -164,9 +160,6 @@ metal.workspace = true [target.'cfg(any(target_os = "linux", target_os = "freebsd", target_os = "macos"))'.dependencies] pathfinder_geometry = "0.5" -[target.'cfg(any(target_os = "linux", target_os = "freebsd", target_os = "windows"))'.dependencies] -scap = { workspace = true, optional = true } - [target.'cfg(any(target_os = "linux", target_os = "freebsd"))'.dependencies] # Always used flume = "0.11" diff --git a/crates/gpui/src/app.rs b/crates/gpui/src/app.rs index c042d85a1239dc..a87410d96dffcc 100644 --- a/crates/gpui/src/app.rs +++ b/crates/gpui/src/app.rs @@ -40,8 +40,8 @@ use crate::{ Keymap, Keystroke, LayoutId, Menu, MenuItem, OwnedMenu, PathPromptOptions, Pixels, Platform, PlatformDisplay, PlatformKeyboardLayout, PlatformKeyboardMapper, Point, PromptBuilder, PromptButton, PromptHandle, PromptLevel, Render, RenderImage, RenderablePromptHandle, - Reservation, ScreenCaptureSource, SharedString, SubscriberSet, Subscription, SvgRenderer, Task, - TextSystem, Window, WindowAppearance, WindowHandle, WindowId, WindowInvalidator, + Reservation, SharedString, SubscriberSet, Subscription, SvgRenderer, Task, TextSystem, Window, + WindowAppearance, WindowHandle, WindowId, WindowInvalidator, colors::{Colors, GlobalColors}, current_platform, hash, init_app_menus, }; @@ -1026,18 +1026,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, - ) -> oneshot::Receiver>>> { - self.platform.screen_capture_sources() - } - /// Returns the display with the given ID, if one exists. pub fn find_display(&self, id: DisplayId) -> Option> { self.displays() diff --git a/crates/gpui/src/app/test_context.rs b/crates/gpui/src/app/test_context.rs index 4a7b73c359ed3d..57315003ca6d7e 100644 --- a/crates/gpui/src/app/test_context.rs +++ b/crates/gpui/src/app/test_context.rs @@ -3,9 +3,8 @@ use crate::{ BackgroundExecutor, BorrowAppContext, Bounds, Capslock, ClipboardItem, DrawPhase, Drawable, Element, Empty, EventEmitter, ForegroundExecutor, Global, InputEvent, Keystroke, Modifiers, ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, Pixels, - Platform, Point, Render, Result, Size, Task, TestDispatcher, TestPlatform, - TestScreenCaptureSource, TestWindow, TextSystem, VisualContext, Window, WindowBounds, - WindowHandle, WindowOptions, + Platform, Point, Render, Result, Size, Task, TestDispatcher, TestPlatform, TestWindow, + TextSystem, VisualContext, Window, WindowBounds, WindowHandle, WindowOptions, }; use anyhow::{anyhow, bail}; use futures::{Stream, StreamExt, channel::oneshot}; @@ -340,12 +339,6 @@ impl TestAppContext { rx } - /// Causes the given sources to be returned if the application queries for screen - /// capture sources. - pub fn set_screen_capture_sources(&self, sources: Vec) { - self.test_platform.set_screen_capture_sources(sources); - } - /// Returns all windows open in the test. pub fn windows(&self) -> Vec { self.app.borrow().windows() diff --git a/crates/gpui/src/platform.rs b/crates/gpui/src/platform.rs index caedf0317f21b9..e3086edde17034 100644 --- a/crates/gpui/src/platform.rs +++ b/crates/gpui/src/platform.rs @@ -23,18 +23,6 @@ mod test; #[cfg(target_os = "windows")] mod windows; -#[cfg(all( - feature = "screen-capture", - any( - target_os = "windows", - 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, @@ -86,7 +74,7 @@ pub(crate) use windows::*; pub use linux::layer_shell; #[cfg(any(test, feature = "test-support"))] -pub use test::{TestDispatcher, TestScreenCaptureSource, TestScreenCaptureStream}; +pub use test::TestDispatcher; /// Returns a background executor for the current platform. pub fn background_executor() -> BackgroundExecutor { @@ -98,6 +86,18 @@ pub(crate) fn current_platform(headless: bool) -> Rc { Rc::new(MacPlatform::new(headless)) } +#[cfg(any(target_os = "linux", target_os = "freebsd"))] +#[derive(strum::Display)] +/// The available display systems for Linux +pub enum LinuxCompositor { + /// Wayland + Wayland, + /// X11 + X11, + /// Headless (no display) + Headless, +} + #[cfg(any(target_os = "linux", target_os = "freebsd"))] pub(crate) fn current_platform(headless: bool) -> Rc { #[cfg(feature = "x11")] @@ -109,17 +109,18 @@ pub(crate) fn current_platform(headless: bool) -> Rc { match guess_compositor() { #[cfg(feature = "wayland")] - "Wayland" => Rc::new(WaylandClient::new()), - + LinuxCompositor::Wayland => Rc::new(WaylandClient::new()), + #[cfg(not(feature = "wayland"))] + LinuxCompositor::Wayland => panic!("Running on Wayland but built without Wayland support"), #[cfg(feature = "x11")] - "X11" => Rc::new( + LinuxCompositor::X11 => Rc::new( X11Client::new() .context("Failed to initialize X11 client.") .unwrap(), ), - - "Headless" => Rc::new(HeadlessClient::new()), - _ => unreachable!(), + #[cfg(not(feature = "x11"))] + LinuxCompositor::X11 => panic!("Running on X11 but built without X11 support"), + LinuxCompositor::Headless => Rc::new(HeadlessClient::new()), } } @@ -136,9 +137,9 @@ pub(crate) fn current_platform(_headless: bool) -> Rc { /// Does not attempt to connect to the given compositor #[cfg(any(target_os = "linux", target_os = "freebsd"))] #[inline] -pub fn guess_compositor() -> &'static str { +pub fn guess_compositor() -> LinuxCompositor { if std::env::var_os("ZED_HEADLESS").is_some() { - return "Headless"; + return LinuxCompositor::Headless; } #[cfg(feature = "wayland")] @@ -155,11 +156,11 @@ pub fn guess_compositor() -> &'static str { let use_x11 = x11_display.is_some_and(|display| !display.is_empty()); if use_wayland { - "Wayland" + LinuxCompositor::Wayland } else if use_x11 { - "X11" + LinuxCompositor::X11 } else { - "Headless" + LinuxCompositor::Headless } } @@ -183,28 +184,6 @@ pub(crate) trait Platform: 'static { None } - #[cfg(feature = "screen-capture")] - fn is_screen_capture_supported(&self) -> bool; - #[cfg(not(feature = "screen-capture"))] - fn is_screen_capture_supported(&self) -> bool { - false - } - #[cfg(feature = "screen-capture")] - fn screen_capture_sources(&self) - -> oneshot::Receiver>>>; - #[cfg(not(feature = "screen-capture"))] - fn screen_capture_sources( - &self, - ) -> oneshot::Receiver>>> { - let (sources_tx, sources_rx) = oneshot::channel(); - sources_tx - .send(Err(anyhow::anyhow!( - "gpui was compiled without the screen-capture feature" - ))) - .ok(); - sources_rx - } - fn open_window( &self, handle: AnyWindowHandle, @@ -302,42 +281,6 @@ pub trait PlatformDisplay: Send + Sync + Debug { } } -/// Metadata for a given [ScreenCaptureSource] -#[derive(Clone)] -pub struct SourceMetadata { - /// Opaque identifier of this screen. - pub id: u64, - /// Human-readable label for this source. - pub label: Option, - /// Whether this source is the main display. - pub is_main: Option, - /// Video resolution of this source. - pub resolution: Size, -} - -/// A source of on-screen video content that can be captured. -pub trait ScreenCaptureSource { - /// Returns metadata for this source. - fn metadata(&self) -> Result; - - /// Start capture video from this source, invoking the given callback - /// with each frame. - fn stream( - &self, - foreground_executor: &ForegroundExecutor, - frame_callback: Box, - ) -> oneshot::Receiver>>; -} - -/// A video stream captured from a screen. -pub trait ScreenCaptureStream { - /// Returns metadata for this source. - fn metadata(&self) -> Result; -} - -/// A frame of video captured from a screen. -pub struct ScreenCaptureFrame(pub PlatformScreenCaptureFrame); - /// An opaque identifier for a hardware display #[derive(PartialEq, Eq, Hash, Copy, Clone)] pub struct DisplayId(pub(crate) u32); diff --git a/crates/gpui/src/platform/linux.rs b/crates/gpui/src/platform/linux.rs index f7d7ed0ebaa416..a7a5603735741c 100644 --- a/crates/gpui/src/platform/linux.rs +++ b/crates/gpui/src/platform/linux.rs @@ -23,10 +23,5 @@ pub(crate) use wayland::*; #[cfg(feature = "x11")] pub(crate) use x11::*; -#[cfg(all(feature = "screen-capture", any(feature = "wayland", feature = "x11")))] -pub(crate) type PlatformScreenCaptureFrame = scap::frame::Frame; -#[cfg(not(all(feature = "screen-capture", any(feature = "wayland", feature = "x11"))))] -pub(crate) type PlatformScreenCaptureFrame = (); - #[cfg(feature = "wayland")] pub use wayland::layer_shell; diff --git a/crates/gpui/src/platform/linux/headless/client.rs b/crates/gpui/src/platform/linux/headless/client.rs index 33f1bb17e3230d..4a39f6b1bf75b9 100644 --- a/crates/gpui/src/platform/linux/headless/client.rs +++ b/crates/gpui/src/platform/linux/headless/client.rs @@ -68,24 +68,6 @@ impl LinuxClient for HeadlessClient { None } - #[cfg(feature = "screen-capture")] - fn is_screen_capture_supported(&self) -> bool { - false - } - - #[cfg(feature = "screen-capture")] - fn screen_capture_sources( - &self, - ) -> futures::channel::oneshot::Receiver>>> - { - let (mut tx, rx) = futures::channel::oneshot::channel(); - tx.send(Err(anyhow::anyhow!( - "Headless mode does not support screen capture." - ))) - .ok(); - rx - } - fn active_window(&self) -> Option { None } diff --git a/crates/gpui/src/platform/linux/platform.rs b/crates/gpui/src/platform/linux/platform.rs index 21468370c49905..9801987b40b3ba 100644 --- a/crates/gpui/src/platform/linux/platform.rs +++ b/crates/gpui/src/platform/linux/platform.rs @@ -51,13 +51,6 @@ pub trait LinuxClient { #[allow(unused)] fn display(&self, id: DisplayId) -> Option>; fn primary_display(&self) -> Option>; - #[cfg(feature = "screen-capture")] - fn is_screen_capture_supported(&self) -> bool; - #[cfg(feature = "screen-capture")] - fn screen_capture_sources( - &self, - ) -> oneshot::Receiver>>>; - fn open_window( &self, handle: AnyWindowHandle, @@ -252,18 +245,6 @@ impl Platform for P { self.displays() } - #[cfg(feature = "screen-capture")] - fn is_screen_capture_supported(&self) -> bool { - self.is_screen_capture_supported() - } - - #[cfg(feature = "screen-capture")] - fn screen_capture_sources( - &self, - ) -> oneshot::Receiver>>> { - self.screen_capture_sources() - } - fn active_window(&self) -> Option { self.active_window() } diff --git a/crates/gpui/src/platform/linux/wayland/client.rs b/crates/gpui/src/platform/linux/wayland/client.rs index 9a9ec213edd27d..df6e8a65b64003 100644 --- a/crates/gpui/src/platform/linux/wayland/client.rs +++ b/crates/gpui/src/platform/linux/wayland/client.rs @@ -698,29 +698,6 @@ impl LinuxClient for WaylandClient { None } - #[cfg(feature = "screen-capture")] - fn is_screen_capture_supported(&self) -> bool { - false - } - - #[cfg(feature = "screen-capture")] - fn screen_capture_sources( - &self, - ) -> futures::channel::oneshot::Receiver>>> - { - // 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) = futures::channel::oneshot::channel(); - sources_tx - .send(Err(anyhow::anyhow!( - "Wayland screen capture not yet implemented." - ))) - .ok(); - sources_rx - } - fn open_window( &self, handle: AnyWindowHandle, diff --git a/crates/gpui/src/platform/linux/x11/client.rs b/crates/gpui/src/platform/linux/x11/client.rs index 32f50cdf5d9d94..e717b8f8d2276f 100644 --- a/crates/gpui/src/platform/linux/x11/client.rs +++ b/crates/gpui/src/platform/linux/x11/client.rs @@ -1454,21 +1454,6 @@ impl LinuxClient for X11Client { )) } - #[cfg(feature = "screen-capture")] - fn is_screen_capture_supported(&self) -> bool { - true - } - - #[cfg(feature = "screen-capture")] - fn screen_capture_sources( - &self, - ) -> futures::channel::oneshot::Receiver>>> - { - crate::platform::scap_screen_capture::scap_screen_sources( - &self.0.borrow().common.foreground_executor, - ) - } - fn open_window( &self, handle: AnyWindowHandle, diff --git a/crates/gpui/src/platform/mac.rs b/crates/gpui/src/platform/mac.rs index 76d636b457517d..b278eaa2f0eac6 100644 --- a/crates/gpui/src/platform/mac.rs +++ b/crates/gpui/src/platform/mac.rs @@ -6,15 +6,11 @@ mod display_link; mod events; mod keyboard; -#[cfg(feature = "screen-capture")] -mod screen_capture; - #[cfg(not(feature = "macos-blade"))] mod metal_atlas; #[cfg(not(feature = "macos-blade"))] pub mod metal_renderer; -use core_video::image_buffer::CVImageBuffer; #[cfg(not(feature = "macos-blade"))] use metal_renderer as renderer; @@ -55,9 +51,6 @@ pub(crate) use window::*; #[cfg(feature = "font-kit")] pub(crate) use text_system::*; -/// A frame of video captured from a screen. -pub(crate) type PlatformScreenCaptureFrame = CVImageBuffer; - trait BoolExt { fn to_objc(self) -> BOOL; } diff --git a/crates/gpui/src/platform/mac/platform.rs b/crates/gpui/src/platform/mac/platform.rs index 7f71d4f164b497..fb0d869070ad26 100644 --- a/crates/gpui/src/platform/mac/platform.rs +++ b/crates/gpui/src/platform/mac/platform.rs @@ -606,19 +606,6 @@ impl Platform for MacPlatform { .collect() } - #[cfg(feature = "screen-capture")] - fn is_screen_capture_supported(&self) -> bool { - let min_version = cocoa::foundation::NSOperatingSystemVersion::new(12, 3, 0); - super::is_macos_version_at_least(min_version) - } - - #[cfg(feature = "screen-capture")] - fn screen_capture_sources( - &self, - ) -> oneshot::Receiver>>> { - super::screen_capture::get_sources() - } - fn active_window(&self) -> Option { MacWindow::active_window() } diff --git a/crates/gpui/src/platform/mac/screen_capture.rs b/crates/gpui/src/platform/mac/screen_capture.rs deleted file mode 100644 index 4d4ffa6896520e..00000000000000 --- a/crates/gpui/src/platform/mac/screen_capture.rs +++ /dev/null @@ -1,334 +0,0 @@ -use crate::{ - DevicePixels, ForegroundExecutor, SharedString, SourceMetadata, - platform::{ScreenCaptureFrame, ScreenCaptureSource, ScreenCaptureStream}, - size, -}; -use anyhow::{Result, anyhow}; -use block::ConcreteBlock; -use cocoa::{ - base::{YES, id, nil}, - foundation::{NSArray, NSString}, -}; -use collections::HashMap; -use core_foundation::base::TCFType; -use core_graphics::display::{ - CGDirectDisplayID, CGDisplayCopyDisplayMode, CGDisplayModeGetPixelHeight, - CGDisplayModeGetPixelWidth, CGDisplayModeRelease, -}; -use ctor::ctor; -use futures::channel::oneshot; -use media::core_media::{CMSampleBuffer, CMSampleBufferRef}; -use metal::NSInteger; -use objc::{ - class, - declare::ClassDecl, - msg_send, - runtime::{Class, Object, Sel}, - sel, sel_impl, -}; -use std::{cell::RefCell, ffi::c_void, mem, ptr, rc::Rc}; - -use super::NSStringExt; - -#[derive(Clone)] -pub struct MacScreenCaptureSource { - sc_display: id, - meta: Option, -} - -pub struct MacScreenCaptureStream { - sc_stream: id, - sc_stream_output: id, - meta: SourceMetadata, -} - -static mut DELEGATE_CLASS: *const Class = ptr::null(); -static mut OUTPUT_CLASS: *const Class = ptr::null(); -const FRAME_CALLBACK_IVAR: &str = "frame_callback"; - -#[allow(non_upper_case_globals)] -const SCStreamOutputTypeScreen: NSInteger = 0; - -impl ScreenCaptureSource for MacScreenCaptureSource { - fn metadata(&self) -> Result { - let (display_id, size) = unsafe { - let display_id: CGDirectDisplayID = msg_send![self.sc_display, displayID]; - let display_mode_ref = CGDisplayCopyDisplayMode(display_id); - let width = CGDisplayModeGetPixelWidth(display_mode_ref); - let height = CGDisplayModeGetPixelHeight(display_mode_ref); - CGDisplayModeRelease(display_mode_ref); - - ( - display_id, - size(DevicePixels(width as i32), DevicePixels(height as i32)), - ) - }; - let (label, is_main) = self - .meta - .clone() - .map(|meta| (meta.label, meta.is_main)) - .unzip(); - - Ok(SourceMetadata { - id: display_id as u64, - label, - is_main, - resolution: size, - }) - } - - fn stream( - &self, - _foreground_executor: &ForegroundExecutor, - frame_callback: Box, - ) -> oneshot::Receiver>> { - unsafe { - let stream: id = msg_send![class!(SCStream), alloc]; - let filter: id = msg_send![class!(SCContentFilter), alloc]; - let configuration: id = msg_send![class!(SCStreamConfiguration), alloc]; - let delegate: id = msg_send![DELEGATE_CLASS, alloc]; - let output: id = msg_send![OUTPUT_CLASS, alloc]; - - let excluded_windows = NSArray::array(nil); - let filter: id = msg_send![filter, initWithDisplay:self.sc_display excludingWindows:excluded_windows]; - let configuration: id = msg_send![configuration, init]; - let _: id = msg_send![configuration, setScalesToFit: true]; - let _: id = msg_send![configuration, setPixelFormat: 0x42475241]; - // let _: id = msg_send![configuration, setShowsCursor: false]; - // let _: id = msg_send![configuration, setCaptureResolution: 3]; - let delegate: id = msg_send![delegate, init]; - let output: id = msg_send![output, init]; - - output.as_mut().unwrap().set_ivar( - FRAME_CALLBACK_IVAR, - Box::into_raw(Box::new(frame_callback)) as *mut c_void, - ); - - let meta = self.metadata().unwrap(); - let _: id = msg_send![configuration, setWidth: meta.resolution.width.0 as i64]; - let _: id = msg_send![configuration, setHeight: meta.resolution.height.0 as i64]; - let stream: id = msg_send![stream, initWithFilter:filter configuration:configuration delegate:delegate]; - - let (mut tx, rx) = oneshot::channel(); - - let mut error: id = nil; - let _: () = msg_send![stream, addStreamOutput:output type:SCStreamOutputTypeScreen sampleHandlerQueue:0 error:&mut error as *mut id]; - if error != nil { - let message: id = msg_send![error, localizedDescription]; - tx.send(Err(anyhow!("failed to add stream output {message:?}"))) - .ok(); - return rx; - } - - let tx = Rc::new(RefCell::new(Some(tx))); - let handler = ConcreteBlock::new({ - move |error: id| { - let result = if error == nil { - let stream = MacScreenCaptureStream { - meta: meta.clone(), - sc_stream: stream, - sc_stream_output: output, - }; - Ok(Box::new(stream) as Box) - } else { - let message: id = msg_send![error, localizedDescription]; - Err(anyhow!("failed to stop screen capture stream {message:?}")) - }; - if let Some(tx) = tx.borrow_mut().take() { - tx.send(result).ok(); - } - } - }); - let handler = handler.copy(); - let _: () = msg_send![stream, startCaptureWithCompletionHandler:handler]; - rx - } - } -} - -impl Drop for MacScreenCaptureSource { - fn drop(&mut self) { - unsafe { - let _: () = msg_send![self.sc_display, release]; - } - } -} - -impl ScreenCaptureStream for MacScreenCaptureStream { - fn metadata(&self) -> Result { - Ok(self.meta.clone()) - } -} - -impl Drop for MacScreenCaptureStream { - fn drop(&mut self) { - unsafe { - let mut error: id = nil; - let _: () = msg_send![self.sc_stream, removeStreamOutput:self.sc_stream_output type:SCStreamOutputTypeScreen error:&mut error as *mut _]; - if error != nil { - let message: id = msg_send![error, localizedDescription]; - log::error!("failed to add stream output {message:?}"); - } - - let handler = ConcreteBlock::new(move |error: id| { - if error != nil { - let message: id = msg_send![error, localizedDescription]; - log::error!("failed to stop screen capture stream {message:?}"); - } - }); - let block = handler.copy(); - let _: () = msg_send![self.sc_stream, stopCaptureWithCompletionHandler:block]; - let _: () = msg_send![self.sc_stream, release]; - let _: () = msg_send![self.sc_stream_output, release]; - } - } -} - -#[derive(Clone)] -struct ScreenMeta { - label: SharedString, - // Is this the screen with menu bar? - is_main: bool, -} - -unsafe fn screen_id_to_human_label() -> HashMap { - let screens: id = msg_send![class!(NSScreen), screens]; - let count: usize = msg_send![screens, count]; - let mut map = HashMap::default(); - let screen_number_key = unsafe { NSString::alloc(nil).init_str("NSScreenNumber") }; - for i in 0..count { - let screen: id = msg_send![screens, objectAtIndex: i]; - let device_desc: id = msg_send![screen, deviceDescription]; - if device_desc == nil { - continue; - } - - let nsnumber: id = msg_send![device_desc, objectForKey: screen_number_key]; - if nsnumber == nil { - continue; - } - - let screen_id: u32 = msg_send![nsnumber, unsignedIntValue]; - - let name: id = msg_send![screen, localizedName]; - if name != nil { - let cstr: *const std::os::raw::c_char = msg_send![name, UTF8String]; - let rust_str = unsafe { - std::ffi::CStr::from_ptr(cstr) - .to_string_lossy() - .into_owned() - }; - map.insert( - screen_id, - ScreenMeta { - label: rust_str.into(), - is_main: i == 0, - }, - ); - } - } - map -} - -pub(crate) fn get_sources() -> oneshot::Receiver>>> { - unsafe { - let (mut tx, rx) = oneshot::channel(); - let tx = Rc::new(RefCell::new(Some(tx))); - let screen_id_to_label = screen_id_to_human_label(); - let block = ConcreteBlock::new(move |shareable_content: id, error: id| { - let Some(mut tx) = tx.borrow_mut().take() else { - return; - }; - - let result = if error == nil { - let displays: id = msg_send![shareable_content, displays]; - let mut result = Vec::new(); - for i in 0..displays.count() { - let display = displays.objectAtIndex(i); - let id: CGDirectDisplayID = msg_send![display, displayID]; - let meta = screen_id_to_label.get(&id).cloned(); - let source = MacScreenCaptureSource { - sc_display: msg_send![display, retain], - meta, - }; - result.push(Rc::new(source) as Rc); - } - Ok(result) - } else { - let msg: id = msg_send![error, localizedDescription]; - Err(anyhow!( - "Screen share failed: {:?}", - NSStringExt::to_str(&msg) - )) - }; - tx.send(result).ok(); - }); - let block = block.copy(); - - let _: () = msg_send![ - class!(SCShareableContent), - getShareableContentExcludingDesktopWindows:YES - onScreenWindowsOnly:YES - completionHandler:block]; - rx - } -} - -#[ctor] -unsafe fn build_classes() { - let mut decl = ClassDecl::new("GPUIStreamDelegate", class!(NSObject)).unwrap(); - unsafe { - decl.add_method( - sel!(outputVideoEffectDidStartForStream:), - output_video_effect_did_start_for_stream as extern "C" fn(&Object, Sel, id), - ); - decl.add_method( - sel!(outputVideoEffectDidStopForStream:), - output_video_effect_did_stop_for_stream as extern "C" fn(&Object, Sel, id), - ); - decl.add_method( - sel!(stream:didStopWithError:), - stream_did_stop_with_error as extern "C" fn(&Object, Sel, id, id), - ); - DELEGATE_CLASS = decl.register(); - - let mut decl = ClassDecl::new("GPUIStreamOutput", class!(NSObject)).unwrap(); - decl.add_method( - sel!(stream:didOutputSampleBuffer:ofType:), - stream_did_output_sample_buffer_of_type - as extern "C" fn(&Object, Sel, id, id, NSInteger), - ); - decl.add_ivar::<*mut c_void>(FRAME_CALLBACK_IVAR); - - OUTPUT_CLASS = decl.register(); - } -} - -extern "C" fn output_video_effect_did_start_for_stream(_this: &Object, _: Sel, _stream: id) {} - -extern "C" fn output_video_effect_did_stop_for_stream(_this: &Object, _: Sel, _stream: id) {} - -extern "C" fn stream_did_stop_with_error(_this: &Object, _: Sel, _stream: id, _error: id) {} - -extern "C" fn stream_did_output_sample_buffer_of_type( - this: &Object, - _: Sel, - _stream: id, - sample_buffer: id, - buffer_type: NSInteger, -) { - if buffer_type != SCStreamOutputTypeScreen { - return; - } - - unsafe { - let sample_buffer = sample_buffer as CMSampleBufferRef; - let sample_buffer = CMSampleBuffer::wrap_under_get_rule(sample_buffer); - if let Some(buffer) = sample_buffer.image_buffer() { - let callback: Box> = - Box::from_raw(*this.get_ivar::<*mut c_void>(FRAME_CALLBACK_IVAR) as *mut _); - callback(ScreenCaptureFrame(buffer)); - mem::forget(callback); - } - } -} diff --git a/crates/gpui/src/platform/scap_screen_capture.rs b/crates/gpui/src/platform/scap_screen_capture.rs deleted file mode 100644 index d6d19cd8102d58..00000000000000 --- a/crates/gpui/src/platform/scap_screen_capture.rs +++ /dev/null @@ -1,325 +0,0 @@ -//! Screen capture for Linux and Windows -use crate::{ - DevicePixels, ForegroundExecutor, ScreenCaptureFrame, ScreenCaptureSource, ScreenCaptureStream, - Size, SourceMetadata, size, -}; -use anyhow::{Context as _, Result, anyhow}; -use futures::channel::oneshot; -use scap::Target; -use std::rc::Rc; -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>>> { - 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>>> { - 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::Display, - size: Size, -} - -/// Populates the sender with the screens available for capture. -fn get_screen_targets(sources_tx: oneshot::Sender>>) { - // 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 - .into_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: display, - size, - }) - } - scap::Target::Window(_) => None, - }) - .collect::>(); - sources_tx.send(Ok(sources)).ok(); - }); -} - -impl ScreenCaptureSource for ScapCaptureSource { - fn metadata(&self) -> Result { - Ok(SourceMetadata { - resolution: self.size, - label: Some(self.target.title.clone().into()), - is_main: None, - id: self.target.id as u64, - }) - } - - fn stream( - &self, - foreground_executor: &ForegroundExecutor, - frame_callback: Box, - ) -> oneshot::Receiver>> { - 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(scap::Target::Display(target.clone()))) { - Ok(mut capturer) => { - capturer.start_capture(); - run_capture(capturer, target.clone(), 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>, - // Callback for frames. - Box, - )>, - target: scap::Display, - size: Size, -} - -/// 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>>, -) { - // 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); - let target = capturer - .target() - .context("Unable to determine the target display.")?; - let target = target.clone(); - Ok((capturer, size, target)) - }); - - match start_result { - Ok((capturer, size, Target::Display(display))) => { - let (stream_call_tx, stream_rx) = std::sync::mpsc::sync_channel(1); - sources_tx - .send(Ok(vec![ScapDefaultTargetCaptureSource { - stream_call_tx, - size, - target: display.clone(), - }])) - .ok(); - let Ok((stream_tx, frame_callback)) = stream_rx.recv() else { - return; - }; - run_capture(capturer, display, frame_callback, stream_tx); - } - Err(e) => { - sources_tx.send(Err(e)).ok(); - } - _ => { - sources_tx - .send(Err(anyhow!("The screen capture source is not a display"))) - .ok(); - } - } - }); -} - -impl ScreenCaptureSource for ScapDefaultTargetCaptureSource { - fn metadata(&self) -> Result { - Ok(SourceMetadata { - resolution: self.size, - label: None, - is_main: None, - id: self.target.id as u64, - }) - } - - fn stream( - &self, - foreground_executor: &ForegroundExecutor, - frame_callback: Box, - ) -> oneshot::Receiver>> { - 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) -> Result { - 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, - display: scap::Display, - frame_callback: Box, - stream_tx: oneshot::Sender>, -) { - let cancel_stream = Arc::new(AtomicBool::new(false)); - let size = Size { - width: DevicePixels(display.width as i32), - height: DevicePixels(display.height as i32), - }; - let stream_send_result = stream_tx.send(Ok(ScapStream { - cancel_stream: cancel_stream.clone(), - display, - size, - })); - if stream_send_result.is_err() { - 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, - display: scap::Display, - size: Size, -} - -impl ScreenCaptureStream for ScapStream { - fn metadata(&self) -> Result { - Ok(SourceMetadata { - resolution: self.size, - label: Some(self.display.title.clone().into()), - is_main: None, - id: self.display.id as u64, - }) - } -} - -impl Drop for ScapStream { - fn drop(&mut self) { - self.cancel_stream.store(true, atomic::Ordering::SeqCst); - } -} - -fn frame_size(frame: &scap::frame::Frame) -> Size { - 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 `Rc`. They need to `Send` their capture source, and so -/// the capture source structs are used as `Rc` is not `Send`. -fn to_dyn_screen_capture_sources( - sources_rx: oneshot::Receiver>>, - foreground_executor: &ForegroundExecutor, -) -> oneshot::Receiver>>> { - 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| Rc::new(source) as Rc) - .collect::>())) - .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( - sources_rx: oneshot::Receiver>, - foreground_executor: &ForegroundExecutor, -) -> oneshot::Receiver>> { - 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)) - .ok(), - Ok(Err(err)) => dyn_sources_tx.send(Err(err)).ok(), - Err(oneshot::Canceled) => None, - } - }) - .detach(); - dyn_sources_rx -} diff --git a/crates/gpui/src/platform/test.rs b/crates/gpui/src/platform/test.rs index 9227df5b63314b..a5a81f0a93ba05 100644 --- a/crates/gpui/src/platform/test.rs +++ b/crates/gpui/src/platform/test.rs @@ -7,5 +7,3 @@ pub use dispatcher::*; pub(crate) use display::*; pub(crate) use platform::*; pub(crate) use window::*; - -pub use platform::{TestScreenCaptureSource, TestScreenCaptureStream}; diff --git a/crates/gpui/src/platform/test/platform.rs b/crates/gpui/src/platform/test/platform.rs index dfada364667989..77c8c9f44e154c 100644 --- a/crates/gpui/src/platform/test/platform.rs +++ b/crates/gpui/src/platform/test/platform.rs @@ -1,9 +1,8 @@ use crate::{ - AnyWindowHandle, BackgroundExecutor, ClipboardItem, CursorStyle, DevicePixels, - DummyKeyboardMapper, ForegroundExecutor, Keymap, NoopTextSystem, Platform, PlatformDisplay, - PlatformKeyboardLayout, PlatformKeyboardMapper, PlatformTextSystem, PromptButton, - ScreenCaptureFrame, ScreenCaptureSource, ScreenCaptureStream, SourceMetadata, Task, - TestDisplay, TestWindow, WindowAppearance, WindowParams, size, + AnyWindowHandle, BackgroundExecutor, ClipboardItem, CursorStyle, DummyKeyboardMapper, + ForegroundExecutor, Keymap, NoopTextSystem, Platform, PlatformDisplay, PlatformKeyboardLayout, + PlatformKeyboardMapper, PlatformTextSystem, PromptButton, Task, TestDisplay, TestWindow, + WindowAppearance, WindowParams, }; use anyhow::Result; use collections::VecDeque; @@ -15,11 +14,6 @@ use std::{ rc::{Rc, Weak}, sync::Arc, }; -#[cfg(target_os = "windows")] -use windows::Win32::{ - Graphics::Imaging::{CLSID_WICImagingFactory, IWICImagingFactory}, - System::Com::{CLSCTX_INPROC_SERVER, CoCreateInstance}, -}; /// TestPlatform implements the Platform trait for use in tests. pub(crate) struct TestPlatform { @@ -33,51 +27,12 @@ pub(crate) struct TestPlatform { #[cfg(any(target_os = "linux", target_os = "freebsd"))] current_primary_item: Mutex>, pub(crate) prompts: RefCell, - screen_capture_sources: RefCell>, pub opened_url: RefCell>, pub text_system: Arc, pub expect_restart: RefCell>>>, - #[cfg(target_os = "windows")] - bitmap_factory: std::mem::ManuallyDrop, weak: Weak, } -#[derive(Clone)] -/// A fake screen capture source, used for testing. -pub struct TestScreenCaptureSource {} - -/// A fake screen capture stream, used for testing. -pub struct TestScreenCaptureStream {} - -impl ScreenCaptureSource for TestScreenCaptureSource { - fn metadata(&self) -> Result { - Ok(SourceMetadata { - id: 0, - is_main: None, - label: None, - resolution: size(DevicePixels(1), DevicePixels(1)), - }) - } - - fn stream( - &self, - _foreground_executor: &ForegroundExecutor, - _frame_callback: Box, - ) -> oneshot::Receiver>> { - let (mut tx, rx) = oneshot::channel(); - let stream = TestScreenCaptureStream {}; - tx.send(Ok(Box::new(stream) as Box)) - .ok(); - rx - } -} - -impl ScreenCaptureStream for TestScreenCaptureStream { - fn metadata(&self) -> Result { - TestScreenCaptureSource {}.metadata() - } -} - struct TestPrompt { msg: String, detail: Option, @@ -93,23 +48,12 @@ pub(crate) struct TestPrompts { impl TestPlatform { pub fn new(executor: BackgroundExecutor, foreground_executor: ForegroundExecutor) -> Rc { - #[cfg(target_os = "windows")] - let bitmap_factory = unsafe { - windows::Win32::System::Ole::OleInitialize(None) - .expect("unable to initialize Windows OLE"); - std::mem::ManuallyDrop::new( - CoCreateInstance(&CLSID_WICImagingFactory, None, CLSCTX_INPROC_SERVER) - .expect("Error creating bitmap factory."), - ) - }; - let text_system = Arc::new(NoopTextSystem); Rc::new_cyclic(|weak| TestPlatform { background_executor: executor, foreground_executor, prompts: Default::default(), - screen_capture_sources: Default::default(), active_cursor: Default::default(), active_display: Rc::new(TestDisplay::new()), active_window: Default::default(), @@ -119,8 +63,6 @@ impl TestPlatform { current_primary_item: Mutex::new(None), weak: weak.clone(), opened_url: Default::default(), - #[cfg(target_os = "windows")] - bitmap_factory, text_system, }) } @@ -170,10 +112,6 @@ impl TestPlatform { )) } - pub(crate) fn set_screen_capture_sources(&self, sources: Vec) { - *self.screen_capture_sources.borrow_mut() = sources; - } - pub(crate) fn prompt( &self, msg: &str, @@ -282,26 +220,6 @@ impl Platform for TestPlatform { Some(self.active_display.clone()) } - #[cfg(feature = "screen-capture")] - fn is_screen_capture_supported(&self) -> bool { - true - } - - #[cfg(feature = "screen-capture")] - fn screen_capture_sources( - &self, - ) -> oneshot::Receiver>>> { - let (mut tx, rx) = oneshot::channel(); - tx.send(Ok(self - .screen_capture_sources - .borrow() - .iter() - .map(|source| Rc::new(source.clone()) as Rc) - .collect())) - .ok(); - rx - } - fn active_window(&self) -> Option { self.active_window .borrow() @@ -437,23 +355,6 @@ impl Platform for TestPlatform { } } -impl TestScreenCaptureSource { - /// Create a fake screen capture source, for testing. - pub fn new() -> Self { - Self {} - } -} - -#[cfg(target_os = "windows")] -impl Drop for TestPlatform { - fn drop(&mut self) { - unsafe { - std::mem::ManuallyDrop::drop(&mut self.bitmap_factory); - windows::Win32::System::Ole::OleUninitialize(); - } - } -} - struct TestKeyboardLayout; impl PlatformKeyboardLayout for TestKeyboardLayout { diff --git a/crates/gpui/src/platform/windows.rs b/crates/gpui/src/platform/windows.rs index 9cd1a7d05f4bcc..6b325b5afa54d2 100644 --- a/crates/gpui/src/platform/windows.rs +++ b/crates/gpui/src/platform/windows.rs @@ -33,8 +33,3 @@ pub(crate) use window::*; pub(crate) use wrapper::*; pub(crate) use windows::Win32::Foundation::HWND; - -#[cfg(feature = "screen-capture")] -pub(crate) type PlatformScreenCaptureFrame = scap::frame::Frame; -#[cfg(not(feature = "screen-capture"))] -pub(crate) type PlatformScreenCaptureFrame = (); diff --git a/crates/gpui/src/platform/windows/platform.rs b/crates/gpui/src/platform/windows/platform.rs index b7f13f1fab495b..c4ce2d4b948647 100644 --- a/crates/gpui/src/platform/windows/platform.rs +++ b/crates/gpui/src/platform/windows/platform.rs @@ -423,18 +423,6 @@ impl Platform for WindowsPlatform { WindowsDisplay::primary_monitor().map(|display| Rc::new(display) as Rc) } - #[cfg(feature = "screen-capture")] - fn is_screen_capture_supported(&self) -> bool { - true - } - - #[cfg(feature = "screen-capture")] - fn screen_capture_sources( - &self, - ) -> oneshot::Receiver>>> { - crate::platform::scap_screen_capture::scap_screen_sources(&self.foreground_executor) - } - fn active_window(&self) -> Option { let active_window_hwnd = unsafe { GetActiveWindow() }; self.window_from_hwnd(active_window_hwnd) diff --git a/crates/livekit_client/Cargo.toml b/crates/livekit_client/Cargo.toml index a7766b5ba5b857..8861b95b07c813 100644 --- a/crates/livekit_client/Cargo.toml +++ b/crates/livekit_client/Cargo.toml @@ -26,10 +26,12 @@ audio.workspace = true collections.workspace = true cpal.workspace = true futures.workspace = true -gpui = { workspace = true, features = ["screen-capture", "x11", "wayland", "windows-manifest"] } +gpui = { workspace = true, features = ["x11", "wayland", "windows-manifest"] } gpui_tokio.workspace = true http_client_tls.workspace = true image.workspace = true +libwebrtc.workspace = true +livekit.workspace = true livekit_api.workspace = true log.workspace = true nanoid.workspace = true @@ -40,19 +42,11 @@ serde.workspace = true serde_urlencoded.workspace = true settings.workspace = true smallvec.workspace = true +tokio.workspace = true tokio-tungstenite.workspace = true ui.workspace = true util.workspace = true -[target.'cfg(not(any(all(target_os = "windows", target_env = "gnu"), target_os = "freebsd")))'.dependencies] -libwebrtc = { rev = "5f04705ac3f356350ae31534ffbc476abc9ea83d", git = "https://github.com/zed-industries/livekit-rust-sdks" } -livekit = { rev = "5f04705ac3f356350ae31534ffbc476abc9ea83d", git = "https://github.com/zed-industries/livekit-rust-sdks", features = [ - "__rustls-tls" -] } - -[target.'cfg(any(target_os = "linux", target_os = "freebsd", target_os = "windows"))'.dependencies] -scap.workspace = true - [target.'cfg(target_os = "macos")'.dependencies] core-foundation.workspace = true core-video.workspace = true diff --git a/crates/livekit_client/examples/test_app.rs b/crates/livekit_client/examples/test_app.rs index 7b9b0183a087d1..3bb58f2916a1e5 100644 --- a/crates/livekit_client/examples/test_app.rs +++ b/crates/livekit_client/examples/test_app.rs @@ -3,15 +3,15 @@ use std::sync::Arc; use futures::StreamExt; use gpui::{ AppContext as _, AsyncApp, Bounds, Context, Entity, InteractiveElement, KeyBinding, Menu, - MenuItem, ParentElement, Pixels, Render, ScreenCaptureStream, SharedString, - StatefulInteractiveElement as _, Styled, Task, Window, WindowBounds, WindowHandle, - WindowOptions, actions, bounds, div, point, + MenuItem, ParentElement, Pixels, Render, SharedString, StatefulInteractiveElement as _, Styled, + Task, Window, WindowBounds, WindowHandle, WindowOptions, actions, bounds, div, point, prelude::{FluentBuilder as _, IntoElement}, px, rgb, size, }; use livekit_client::{ AudioStream, LocalTrackPublication, Participant, ParticipantIdentity, RemoteParticipant, RemoteTrackPublication, RemoteVideoTrack, RemoteVideoTrackView, Room, RoomEvent, + ScreenCaptureStreamHandle, screen_capture_sources, }; use livekit_api::token::{self, VideoGrant}; @@ -80,7 +80,7 @@ struct LivekitWindow { microphone_track: Option, screen_share_track: Option, microphone_stream: Option, - screen_share_stream: Option>, + screen_share_stream: Option, remote_participants: Vec<(ParticipantIdentity, ParticipantState)>, _events_task: Task<()>, } @@ -281,13 +281,12 @@ impl LivekitWindow { cx.notify(); } else { let participant = self.room.local_participant(); - let sources = cx.screen_capture_sources(); + let sources = screen_capture_sources(); cx.spawn_in(window, async move |this, cx| { - let sources = sources.await.unwrap()?; let source = sources.into_iter().next().unwrap(); let (publication, stream) = participant - .publish_screenshare_track(&*source, cx) + .publish_screenshare_track(Some(source), cx) .await .unwrap(); this.update(cx, |this, cx| { diff --git a/crates/livekit_client/src/lib.rs b/crates/livekit_client/src/lib.rs index 055aa3704e06f2..a2552b0bd96d2d 100644 --- a/crates/livekit_client/src/lib.rs +++ b/crates/livekit_client/src/lib.rs @@ -1,3 +1,8 @@ +use std::sync::{ + Arc, + atomic::{AtomicBool, Ordering}, +}; + use anyhow::Context as _; use collections::HashMap; @@ -180,6 +185,18 @@ pub enum RoomEvent { Reconnected, } +pub struct ScreenCaptureStreamHandle { + pub screen_id: u64, + stop_capture: Arc, + _spawn_handle: gpui::Task>, +} + +impl Drop for ScreenCaptureStreamHandle { + fn drop(&mut self) { + self.stop_capture.store(true, Ordering::Release); + } +} + pub(crate) fn default_device( input: bool, ) -> anyhow::Result<(cpal::Device, cpal::SupportedStreamConfig)> { diff --git a/crates/livekit_client/src/livekit_client.rs b/crates/livekit_client/src/livekit_client.rs index 30a13bd910d52d..8468729e0d60f7 100644 --- a/crates/livekit_client/src/livekit_client.rs +++ b/crates/livekit_client/src/livekit_client.rs @@ -1,13 +1,29 @@ -use std::sync::Arc; +use std::sync::{ + Arc, + atomic::{AtomicBool, Ordering}, +}; +use std::time::Duration; + +use crate::ScreenCaptureStreamHandle; use anyhow::{Context as _, Result, anyhow}; use audio::AudioSettings; use collections::HashMap; -use futures::{SinkExt, channel::mpsc}; -use gpui::{App, AsyncApp, ScreenCaptureSource, ScreenCaptureStream, Task}; +use futures::{SinkExt, channel::mpsc, stream::StreamExt}; +use gpui::{App, AsyncApp, Task}; use gpui_tokio::Tokio; +use livekit::options::{TrackPublishOptions, VideoCodec}; +use livekit::track::TrackSource; +use livekit::webrtc::desktop_capturer::{ + CaptureError, CaptureSource, DesktopCaptureSourceType, DesktopCapturer, DesktopCapturerOptions, + DesktopFrame, +}; +use livekit::webrtc::native::yuv_helper; +use livekit::webrtc::prelude::{ + I420Buffer, RtcVideoSource, VideoFrame, VideoResolution, VideoRotation, +}; +use livekit::webrtc::video_source::native::NativeVideoSource; use log::info; -use playback::capture_local_video_track; use settings::Settings; mod playback; @@ -29,6 +45,7 @@ pub struct RemoteTrackPublication(livekit::publication::RemoteTrackPublication); pub struct RemoteParticipant(livekit::participant::RemoteParticipant); #[derive(Clone, Debug)] +#[allow(unused)] pub struct LocalVideoTrack(livekit::track::LocalVideoTrack); #[derive(Clone, Debug)] pub struct LocalAudioTrack(livekit::track::LocalAudioTrack); @@ -37,6 +54,29 @@ pub struct LocalTrackPublication(livekit::publication::LocalTrackPublication); #[derive(Clone, Debug)] pub struct LocalParticipant(livekit::participant::LocalParticipant); +fn desktop_capturer_options() -> DesktopCapturerOptions { + // Picking either a screen or a window with one DesktopCapturer is only implemented + // in libwebrtc on Wayland and macOS. + #[allow(unused_variables)] + let source_type = DesktopCaptureSourceType::Screen; + #[cfg(any(target_os = "linux", target_os = "freebsd"))] + let source_type = match gpui::guess_compositor() { + gpui::LinuxCompositor::Wayland => DesktopCaptureSourceType::Generic, + _ => DesktopCaptureSourceType::Screen, + }; + #[cfg(target_os = "macos")] + let source_type = DesktopCaptureSourceType::Generic; + + let mut options = DesktopCapturerOptions::new(source_type); + options.set_include_cursor(true); + options +} + +pub fn screen_capture_sources() -> Vec { + let capturer = DesktopCapturer::new(desktop_capturer_options()).unwrap(); + capturer.get_source_list() +} + pub struct Room { room: livekit::Room, _task: Task<()>, @@ -156,20 +196,137 @@ impl Room { impl LocalParticipant { pub async fn publish_screenshare_track( &self, - source: &dyn ScreenCaptureSource, + source: Option, cx: &mut AsyncApp, - ) -> Result<(LocalTrackPublication, Box)> { - let (track, stream) = capture_local_video_track(source, cx).await?; - let options = livekit::options::TrackPublishOptions { - source: livekit::track::TrackSource::Screenshare, - video_codec: livekit::options::VideoCodec::VP8, - ..Default::default() + ) -> Result<(LocalTrackPublication, ScreenCaptureStreamHandle)> { + let stop_capture = Arc::new(AtomicBool::new(false)); + let (mut video_source_sender, mut video_source_receiver) = mpsc::channel(0); + let callback = { + // These dimensions are arbitrary initial values. + // libwebrtc only exposes the resolution of the source in the DesktopFrame + // passed to the callback, so wait to publish the video track until + // the callback is called the first time. + let mut stream_width = 1920; + let mut stream_height = 1080; + + let mut video_frame = VideoFrame { + rotation: VideoRotation::VideoRotation0, + buffer: I420Buffer::new(stream_width, stream_height), + timestamp_us: 0, + }; + let mut video_source: Option = None; + let stop_capture = stop_capture.clone(); + move |result: Result| { + let frame = match result { + Ok(frame) => frame, + // This error is expected on Wayland while waiting for the user + // to pick a screen with the XDG Desktop Portal. + Err(CaptureError::Temporary) => { + log::debug!("Temporary error capturing screen"); + return; + } + Err(CaptureError::Permanent) => { + log::error!("Error capturing screen"); + stop_capture.store(true, Ordering::Release); + return; + } + }; + let height = frame.height().try_into().unwrap(); + let width = frame.width().try_into().unwrap(); + + if width != stream_width || height != stream_height { + stream_width = width; + stream_height = height; + video_frame.buffer = I420Buffer::new(width, height); + } + + let stride = frame.stride(); + let data = frame.data(); + + let (s_y, s_u, s_v) = video_frame.buffer.strides(); + let (y, u, v) = video_frame.buffer.data_mut(); + yuv_helper::argb_to_i420( + data, + stride, + y, + s_y, + u, + s_u, + v, + s_v, + frame.width(), + frame.height(), + ); + + if let Some(video_source) = &video_source { + video_source.capture_frame(&video_frame); + } else { + // This is the first time the callback has been called. + // Use the resolution from the DesktopFrame to create a video source + // and push it over a channel to be published from the async context. + let video_source_inner = NativeVideoSource::new(VideoResolution { + width: stream_width, + height: stream_height, + }); + + video_source_sender + .try_send(video_source_inner.clone()) + .unwrap(); + + video_source = Some(video_source_inner); + } + } }; + + // source should only be None in tests which have a different implementation + // of this function. + let source = source.unwrap(); + let screen_id = source.id(); + + let mut capturer = DesktopCapturer::new(desktop_capturer_options()) + .ok_or(anyhow!("Failed to create DesktopCapturer"))?; + capturer.start_capture(Some(source), callback); + log::debug!("Starting screen capture"); + + let spawn_handle = gpui_tokio::Tokio::spawn(cx, { + let stop_capture = stop_capture.clone(); + async move { + loop { + if stop_capture.load(Ordering::Acquire) { + log::debug!("Stopping screen capture"); + break; + } + capturer.capture_frame(); + tokio::time::sleep(Duration::from_secs_f32(1.0 / 60.0)).await; + } + } + })?; + + let video_source = video_source_receiver.next().await.ok_or(anyhow!( + "No NativeVideoSource received from DesktopCapturer" + ))?; + let track = livekit::track::LocalVideoTrack::create_video_track( + "screen_share", + RtcVideoSource::Native(video_source), + ); + let publication = self - .publish_track(livekit::track::LocalTrack::Video(track.0), options, cx) + .publish_track( + livekit::track::LocalTrack::Video(track), + TrackPublishOptions { + source: TrackSource::Screenshare, + video_codec: VideoCodec::VP8, + ..Default::default() + }, + cx, + ) .await?; - - Ok((publication, stream)) + let handle = ScreenCaptureStreamHandle { + screen_id, + stop_capture, + _spawn_handle: spawn_handle, + }; + Ok((publication, handle)) } async fn publish_track( diff --git a/crates/livekit_client/src/livekit_client/playback.rs b/crates/livekit_client/src/livekit_client/playback.rs index cdd766453c58ad..1ff29a04c11297 100644 --- a/crates/livekit_client/src/livekit_client/playback.rs +++ b/crates/livekit_client/src/livekit_client/playback.rs @@ -4,10 +4,7 @@ use audio::{AudioSettings, CHANNEL_COUNT, LEGACY_CHANNEL_COUNT, LEGACY_SAMPLE_RA use cpal::traits::{DeviceTrait, StreamTrait as _}; use futures::channel::mpsc::UnboundedSender; use futures::{Stream, StreamExt as _}; -use gpui::{ - AsyncApp, BackgroundExecutor, ScreenCaptureFrame, ScreenCaptureSource, ScreenCaptureStream, - Task, -}; +use gpui::{AsyncApp, BackgroundExecutor, Task}; use libwebrtc::native::{apm, audio_mixer, audio_resampler}; use livekit::track; @@ -15,8 +12,7 @@ use livekit::webrtc::{ audio_frame::AudioFrame, audio_source::{AudioSourceOptions, RtcAudioSource, native::NativeAudioSource}, audio_stream::native::NativeAudioStream, - video_frame::{VideoBuffer, VideoFrame, VideoRotation}, - video_source::{RtcVideoSource, VideoResolution, native::NativeVideoSource}, + video_frame::VideoBuffer, video_stream::native::NativeVideoStream, }; use log::info; @@ -456,50 +452,11 @@ fn send_to_livekit(frame_tx: UnboundedSender>, mut microphon } } -use super::LocalVideoTrack; - pub enum AudioStream { Input { _task: Task<()> }, Output { _drop: Box }, } -pub(crate) async fn capture_local_video_track( - capture_source: &dyn ScreenCaptureSource, - cx: &mut gpui::AsyncApp, -) -> Result<(crate::LocalVideoTrack, Box)> { - let metadata = capture_source.metadata()?; - let track_source = gpui_tokio::Tokio::spawn(cx, async move { - NativeVideoSource::new(VideoResolution { - width: metadata.resolution.width.0 as u32, - height: metadata.resolution.height.0 as u32, - }) - })? - .await?; - - let capture_stream = capture_source - .stream(cx.foreground_executor(), { - let track_source = track_source.clone(); - Box::new(move |frame| { - if let Some(buffer) = video_frame_buffer_to_webrtc(frame) { - track_source.capture_frame(&VideoFrame { - rotation: VideoRotation::VideoRotation0, - timestamp_us: 0, - buffer, - }); - } - }) - }) - .await??; - - Ok(( - LocalVideoTrack(track::LocalVideoTrack::create_video_track( - "screen share", - RtcVideoSource::Native(track_source), - )), - capture_stream, - )) -} - #[derive(Clone)] struct AudioMixerSource { ssrc: i32, @@ -727,75 +684,6 @@ fn video_frame_buffer_from_webrtc(buffer: Box) -> Option Option> { - use livekit::webrtc; - - let pixel_buffer = frame.0.as_concrete_TypeRef(); - std::mem::forget(frame.0); - unsafe { - Some(webrtc::video_frame::native::NativeBuffer::from_cv_pixel_buffer(pixel_buffer as _)) - } -} - -#[cfg(not(target_os = "macos"))] -fn video_frame_buffer_to_webrtc(frame: ScreenCaptureFrame) -> Option> { - use libwebrtc::native::yuv_helper::{abgr_to_nv12, 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::RGBx(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(); - abgr_to_nv12( - &frame.data, - frame.width as u32 * 4, - data_y, - stride_y, - data_uv, - stride_uv, - frame.width, - frame.height, - ); - Some(buffer) - } - scap::frame::Frame::YUVFrame(yuvframe) => { - let mut buffer = NV12Buffer::with_strides( - yuvframe.width as u32, - 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 - } - } -} - trait DeviceChangeListenerApi: Stream + Sized { fn new(input: bool) -> Result; } diff --git a/crates/livekit_client/src/mock_client.rs b/crates/livekit_client/src/mock_client.rs index 7d355749c01ac5..8b084a567946bc 100644 --- a/crates/livekit_client/src/mock_client.rs +++ b/crates/livekit_client/src/mock_client.rs @@ -1,4 +1,5 @@ use crate::test; +use libwebrtc::desktop_capturer::CaptureSource; pub(crate) mod participant; pub(crate) mod publication; @@ -36,3 +37,7 @@ pub(crate) fn play_remote_video_track( ) -> impl futures::Stream + use<> { futures::stream::pending() } + +pub fn screen_capture_sources() -> Vec { + Vec::new() +} diff --git a/crates/livekit_client/src/mock_client/participant.rs b/crates/livekit_client/src/mock_client/participant.rs index 033808cbb54189..84efea5c896f9e 100644 --- a/crates/livekit_client/src/mock_client/participant.rs +++ b/crates/livekit_client/src/mock_client/participant.rs @@ -1,13 +1,14 @@ +use std::sync::{Arc, atomic::AtomicBool}; + use crate::{ AudioStream, LocalAudioTrack, LocalTrackPublication, LocalVideoTrack, Participant, - ParticipantIdentity, RemoteTrack, RemoteTrackPublication, TrackSid, + ParticipantIdentity, RemoteTrack, RemoteTrackPublication, ScreenCaptureStreamHandle, TrackSid, test::{Room, WeakRoom}, }; use anyhow::Result; use collections::HashMap; -use gpui::{ - AsyncApp, DevicePixels, ScreenCaptureSource, ScreenCaptureStream, SourceMetadata, size, -}; +use gpui::AsyncApp; +use livekit::webrtc::desktop_capturer::CaptureSource; #[derive(Clone, Debug)] pub struct LocalParticipant { @@ -59,20 +60,26 @@ impl LocalParticipant { pub async fn publish_screenshare_track( &self, - _source: &dyn ScreenCaptureSource, - _cx: &mut AsyncApp, - ) -> Result<(LocalTrackPublication, Box)> { + _source: Option, + cx: &mut AsyncApp, + ) -> Result<(LocalTrackPublication, ScreenCaptureStreamHandle)> { let this = self.clone(); let server = this.room.test_server(); let sid = server .publish_video_track(this.room.token(), LocalVideoTrack {}) .await?; + let spawn_handle = gpui_tokio::Tokio::spawn(cx, async {})?; + let handle = ScreenCaptureStreamHandle { + screen_id: 0, + stop_capture: Arc::new(AtomicBool::new(false)), + _spawn_handle: spawn_handle, + }; Ok(( LocalTrackPublication { room: self.room.downgrade(), sid, }, - Box::new(TestScreenCaptureStream {}), + handle, )) } } @@ -121,16 +128,3 @@ impl RemoteParticipant { self.identity.clone() } } - -struct TestScreenCaptureStream; - -impl ScreenCaptureStream for TestScreenCaptureStream { - fn metadata(&self) -> Result { - Ok(SourceMetadata { - id: 0, - is_main: None, - label: None, - resolution: size(DevicePixels(1), DevicePixels(1)), - }) - } -} diff --git a/crates/title_bar/Cargo.toml b/crates/title_bar/Cargo.toml index 6d5d0ce170e261..8c3159531c3488 100644 --- a/crates/title_bar/Cargo.toml +++ b/crates/title_bar/Cargo.toml @@ -35,7 +35,8 @@ chrono.workspace = true client.workspace = true cloud_llm_client.workspace = true db.workspace = true -gpui = { workspace = true, features = ["screen-capture"] } +gpui.workspace = true +libwebrtc.workspace = true notifications.workspace = true project.workspace = true remote.workspace = true diff --git a/crates/title_bar/src/collab.rs b/crates/title_bar/src/collab.rs index 16a0389efa4642..04b07e777b4690 100644 --- a/crates/title_bar/src/collab.rs +++ b/crates/title_bar/src/collab.rs @@ -4,11 +4,9 @@ use std::sync::Arc; use call::{ActiveCall, ParticipantLocation, Room}; use channel::ChannelStore; use client::{User, proto::PeerId}; -use gpui::{ - AnyElement, Hsla, IntoElement, MouseButton, Path, ScreenCaptureSource, Styled, WeakEntity, - canvas, point, -}; +use gpui::{AnyElement, Hsla, IntoElement, MouseButton, Path, Styled, WeakEntity, canvas, point}; use gpui::{App, Task, Window, actions}; +use libwebrtc::desktop_capturer::CaptureSource; use project::WorktreeSettings; use rpc::proto::{self}; use settings::{Settings as _, SettingsLocation}; @@ -34,58 +32,47 @@ actions!( ] ); -fn toggle_screen_sharing( - screen: anyhow::Result>>, - window: &mut Window, - cx: &mut App, -) { +fn toggle_screen_sharing(screen: Option, window: &mut Window, cx: &mut App) { let call = ActiveCall::global(cx).read(cx); - let toggle_screen_sharing = match screen { - Ok(screen) => { - let Some(room) = call.room().cloned() else { - return; - }; + let toggle_screen_sharing = { + let Some(room) = call.room().cloned() else { + return; + }; - room.update(cx, |room, cx| { - let clicked_on_currently_shared_screen = - room.shared_screen_id().is_some_and(|screen_id| { - Some(screen_id) - == screen - .as_deref() - .and_then(|s| s.metadata().ok().map(|meta| meta.id)) - }); - let should_unshare_current_screen = room.is_sharing_screen(); - let unshared_current_screen = should_unshare_current_screen.then(|| { + room.update(cx, |room, cx| { + let clicked_on_currently_shared_screen = screen + .clone() + .is_some_and(|s| Some(s.id()) == room.shared_screen_id()); + let should_unshare_current_screen = room.is_sharing_screen(); + let unshared_current_screen = should_unshare_current_screen.then(|| { + telemetry::event!( + "Screen Share Disabled", + room_id = room.id(), + channel_id = room.channel_id(), + ); + room.unshare_screen(clicked_on_currently_shared_screen || screen.is_none(), cx) + }); + if let Some(screen) = screen { + if !should_unshare_current_screen { telemetry::event!( - "Screen Share Disabled", + "Screen Share Enabled", room_id = room.id(), channel_id = room.channel_id(), ); - room.unshare_screen(clicked_on_currently_shared_screen || screen.is_none(), cx) - }); - if let Some(screen) = screen { - if !should_unshare_current_screen { - telemetry::event!( - "Screen Share Enabled", - room_id = room.id(), - channel_id = room.channel_id(), - ); - } - cx.spawn(async move |room, cx| { - unshared_current_screen.transpose()?; - if !clicked_on_currently_shared_screen { - room.update(cx, |room, cx| room.share_screen(screen, cx))? - .await - } else { - Ok(()) - } - }) - } else { - Task::ready(Ok(())) } - }) - } - Err(e) => Task::ready(Err(e)), + cx.spawn(async move |room, cx| { + unshared_current_screen.transpose()?; + if !clicked_on_currently_shared_screen { + room.update(cx, |room, cx| room.share_screen(Some(screen), cx))? + .await + } else { + Ok(()) + } + }) + } else { + Task::ready(Ok(())) + } + }) }; toggle_screen_sharing.detach_and_prompt_err("Sharing Screen Failed", window, cx, |e, _, _| Some(format!("{:?}\n\nPlease check that you have given Zed permissions to record your screen in Settings.", e))); } @@ -351,7 +338,6 @@ impl TitleBar { let is_screen_sharing = room.is_sharing_screen(); 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 channel_store = ChannelStore::global(cx); let channel = room @@ -496,7 +482,7 @@ impl TitleBar { .into_any_element(), ); - if can_use_microphone && screen_sharing_supported { + if can_use_microphone { let trigger = IconButton::new("screen-share", IconName::Screen) .style(ButtonStyle::Subtle) .icon_size(IconSize::Small) @@ -508,18 +494,13 @@ impl TitleBar { "Share Screen" })) .on_click(move |_, window, cx| { - let should_share = ActiveCall::global(cx) - .read(cx) - .room() - .is_some_and(|room| !room.read(cx).is_sharing_screen()); + let room = ActiveCall::global(cx).read(cx).room().unwrap().read(cx); + let first_screen = room.available_screens().first().cloned(); + let should_share = !room.is_sharing_screen(); window .spawn(cx, async move |cx| { - let screen = if should_share { - cx.update(|_, cx| pick_default_screen(cx))?.await - } else { - Ok(None) - }; + let screen = if should_share { first_screen } else { None }; cx.update(|window, cx| toggle_screen_sharing(screen, window, cx))?; Result::<_, anyhow::Error>::Ok(()) @@ -527,14 +508,18 @@ impl TitleBar { .detach(); }); - children.push( - SplitButton::new( - trigger.render(window, cx), - self.render_screen_list().into_any_element(), - ) - .style(SplitButtonStyle::Transparent) - .into_any_element(), - ); + match room.available_screens().len() { + 0 => (), + 1 => children.push(trigger.into_any_element()), + 2.. => children.push( + SplitButton::new( + trigger.render(window, cx), + self.render_screen_list().into_any_element(), + ) + .style(SplitButtonStyle::Transparent) + .into_any_element(), + ), + }; } children.push(div().pr_2().into_any_element()); @@ -557,28 +542,15 @@ impl TitleBar { .toggle_state(self.screen_share_popover_handle.is_deployed()), ) .menu(|window, cx| { - let screens = cx.screen_capture_sources(); Some(ContextMenu::build(window, cx, |context_menu, _, cx| { cx.spawn(async move |this: WeakEntity, cx| { - let screens = screens.await??; this.update(cx, |this, cx| { - let active_screenshare_id = ActiveCall::global(cx) - .read(cx) - .room() - .and_then(|room| room.read(cx).shared_screen_id()); + let room = ActiveCall::global(cx).read(cx).room().unwrap().read(cx); + let screens = room.available_screens().clone(); + let active_screenshare_id = room.shared_screen_id(); for screen in screens { - let Ok(meta) = screen.metadata() else { - continue; - }; - - let label = meta - .label - .clone() - .unwrap_or_else(|| SharedString::from("Unknown screen")); - let resolution = SharedString::from(format!( - "{} × {}", - meta.resolution.width.0, meta.resolution.height.0 - )); + let id = screen.id(); + let title = SharedString::from(screen.title().clone()); this.push_item(ContextMenuItem::CustomEntry { entry_render: Box::new(move |_, _| { h_flex() @@ -587,25 +559,20 @@ impl TitleBar { Icon::new(IconName::Screen) .size(IconSize::XSmall) .map(|this| { - if active_screenshare_id == Some(meta.id) { + if active_screenshare_id == Some(id) { this.color(Color::Accent) } else { this.color(Color::Muted) } }), ) - .child(Label::new(label.clone())) - .child( - Label::new(resolution.clone()) - .color(Color::Muted) - .size(LabelSize::Small), - ) + .child(Label::new(title.clone())) .into_any() }), selectable: true, documentation_aside: None, handler: Rc::new(move |_, window, cx| { - toggle_screen_sharing(Ok(Some(screen.clone())), window, cx); + toggle_screen_sharing(Some(screen.clone()), window, cx); }), }); } @@ -617,20 +584,3 @@ impl TitleBar { }) } } - -/// Picks the screen to share when clicking on the main screen sharing button. -fn pick_default_screen(cx: &App) -> Task>>> { - let source = cx.screen_capture_sources(); - cx.spawn(async move |_| { - let available_sources = source.await??; - Ok(available_sources - .iter() - .find(|it| { - it.as_ref() - .metadata() - .is_ok_and(|meta| meta.is_main.unwrap_or_default()) - }) - .or_else(|| available_sources.first()) - .cloned()) - }) -} diff --git a/nix/build.nix b/nix/build.nix index 9012a47c1fa1a1..7c83eaa9344795 100644 --- a/nix/build.nix +++ b/nix/build.nix @@ -24,6 +24,7 @@ fontconfig, freetype, git, + glib, libgit2, libglvnd, libxkbcommon, @@ -126,6 +127,7 @@ let curl fontconfig freetype + glib # TODO: need staticlib of this for linking the musl remote server. # should make it a separate derivation/flake output # see https://crane.dev/examples/cross-musl.html diff --git a/script/linux b/script/linux index c5c4ea9ab38565..28a82c10f054c4 100755 --- a/script/linux +++ b/script/linux @@ -30,7 +30,16 @@ if [[ -n $apt ]]; then libwayland-dev libx11-xcb-dev libxkbcommon-x11-dev + libxfixes-dev + libxdamage-dev + libxrandr-dev + libxcomposite-dev + libxext-dev + libdrm-dev + libgbm-dev + libglib2.0-dev libssl-dev + libva-dev libzstd-dev libvulkan1 libgit2-dev @@ -81,6 +90,14 @@ if [[ -n $dnf ]] || [[ -n $yum ]]; then wayland-devel libxcb-devel libxkbcommon-x11-devel + libXcomposite-devel + libXdamage-devel + libXext-devel + libXrandr-devel + libdrm-devel + libva-devel + mesa-libgbm-devel + glib2-devel openssl-devel libzstd-devel vulkan-loader diff --git a/tooling/xtask/src/tasks/workflows/run_tests.rs b/tooling/xtask/src/tasks/workflows/run_tests.rs index 5fff9bc8ec4094..8700967ad2c988 100644 --- a/tooling/xtask/src/tasks/workflows/run_tests.rs +++ b/tooling/xtask/src/tasks/workflows/run_tests.rs @@ -1,5 +1,5 @@ use gh_workflow::{ - Concurrency, Event, Expression, Job, PullRequest, Push, Run, Step, Use, Workflow, + Concurrency, Env, Event, Expression, Job, PullRequest, Push, Run, Step, Use, Workflow, }; use indexmap::IndexMap; @@ -15,6 +15,12 @@ use super::{ steps::{self, FluentBuilder, NamedJob, named, release_job}, }; +// Ubuntu 22.04's gcc is too old to compile libwebrtc's C++ headers +fn use_clang(job: Job) -> Job { + job.add_env(Env::new("CC", "clang")) + .add_env(Env::new("CXX", "clang++")) +} + pub(crate) fn run_tests() -> Workflow { // Specify anything which should potentially skip full test suite in this regex: // - docs/ @@ -230,7 +236,7 @@ fn check_style() -> NamedJob { ) // v1.38.1 .with(("config", "./typos.toml")) } - named::job( + named::job(use_clang( release_job(&[]) .runs_on(runners::LINUX_MEDIUM) .add_step(steps::checkout_repo()) @@ -241,7 +247,7 @@ fn check_style() -> NamedJob { .add_step(steps::script("./script/check-keymaps")) .add_step(check_for_typos()) .add_step(steps::cargo_fmt()), - ) + )) } fn check_dependencies() -> NamedJob { @@ -278,7 +284,7 @@ fn check_dependencies() -> NamedJob { .with(("license-check", false)) } - named::job( + named::job(use_clang( release_job(&[]) .runs_on(runners::LINUX_SMALL) .add_step(steps::checkout_repo()) @@ -287,11 +293,11 @@ fn check_dependencies() -> NamedJob { .add_step(run_cargo_machete()) .add_step(check_cargo_lock()) .add_step(check_vulnerable_dependencies()), - ) + )) } fn check_workspace_binaries() -> NamedJob { - named::job( + named::job(use_clang( release_job(&[]) .runs_on(runners::LINUX_LARGE) .add_step(steps::checkout_repo()) @@ -301,7 +307,7 @@ fn check_workspace_binaries() -> NamedJob { .add_step(steps::script("cargo build -p collab")) .add_step(steps::script("cargo build --workspace --bins --examples")) .add_step(steps::cleanup_cargo_config(Platform::Linux)), - ) + )) } pub(crate) fn run_platform_tests(platform: Platform) -> NamedJob { @@ -317,7 +323,7 @@ pub(crate) fn run_platform_tests(platform: Platform) -> NamedJob { .add_step(steps::checkout_repo()) .add_step(steps::setup_cargo_config(platform)) .when(platform == Platform::Linux, |this| { - this.add_step(steps::cache_rust_dependencies_namespace()) + use_clang(this.add_step(steps::cache_rust_dependencies_namespace())) }) .when( platform == Platform::Linux, @@ -382,7 +388,7 @@ fn doctests() -> NamedJob { .id("run_doctests") } - named::job( + named::job(use_clang( release_job(&[]) .runs_on(runners::LINUX_DEFAULT) .add_step(steps::checkout_repo()) @@ -391,7 +397,7 @@ fn doctests() -> NamedJob { .add_step(steps::setup_cargo_config(Platform::Linux)) .add_step(run_doctests()) .add_step(steps::cleanup_cargo_config(Platform::Linux)), - ) + )) } fn check_licenses() -> NamedJob { @@ -433,7 +439,7 @@ fn check_docs() -> NamedJob { "#}) } - named::job( + named::job(use_clang( release_job(&[]) .runs_on(runners::LINUX_LARGE) .add_step(steps::checkout_repo()) @@ -449,7 +455,7 @@ fn check_docs() -> NamedJob { .add_step( lychee_link_check("target/deploy/docs"), // check links in generated html ), - ) + )) } pub(crate) fn check_scripts() -> NamedJob { @@ -480,7 +486,7 @@ pub(crate) fn check_scripts() -> NamedJob { "#}) } - named::job( + named::job(use_clang( release_job(&[]) .runs_on(runners::LINUX_SMALL) .add_step(steps::checkout_repo()) @@ -488,5 +494,5 @@ pub(crate) fn check_scripts() -> NamedJob { .add_step(download_actionlint().id("get_actionlint")) .add_step(run_actionlint()) .add_step(check_xtask_workflows()), - ) + )) }