From a16ce3337be647347919255fc6aa9f482e95bccf Mon Sep 17 00:00:00 2001 From: Sebastian Bernauer Date: Sat, 10 Aug 2024 16:13:53 +0200 Subject: [PATCH] feat!: Add native display sink (#38) 1. Put DisplaySinks behind a shared trait `DisplaySink`, so far we have native display, VNC and ffmpeg 2. Add native display sink This feature was inspired by https://github.com/Rairosu/breakwater/tree/display_sink, so many thanks @Rairosu! Co-authored-by: Rairosu --- CHANGELOG.md | 4 + Cargo.lock | 1346 ++++++++++++++++++++++-- Cargo.toml | 6 +- README.md | 29 +- breakwater/Cargo.toml | 8 +- breakwater/src/cli_args.rs | 16 +- breakwater/src/main.rs | 191 ++-- breakwater/src/sinks/ffmpeg.rs | 45 +- breakwater/src/sinks/mod.rs | 46 + breakwater/src/sinks/native_display.rs | 190 ++++ breakwater/src/sinks/vnc.rs | 93 +- breakwater/src/statistics.rs | 4 +- default.nix | 34 +- 13 files changed, 1751 insertions(+), 261 deletions(-) create mode 100644 breakwater/src/sinks/native_display.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 5e88699..aa9f686 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,12 +7,15 @@ All notable changes to this project will be documented in this file. ### Added - Added support for binary sync protocol behind the `binary-sync-pixels` feature ([#34]) +- Added support for native display output ([#38]) ### Changed - BREAKING: Feature `binary-commands` has been renamed to `binary-set-pixel` ([#34]) - BREAKING: Remove the `breakwater-core` crate ([#37]) - Add a `FrameBuffer` trait, rename the existing implementation one to `SimpleFrameBuffer` ([#37]) +- Add a `DisplaySink`´trait, so that new sinks can be added more easily ([#38]) +- BREAKING: No display sink is now started by default. To start the VNC server add the `--vnc` CLI argument ([#38]) ### Fixed @@ -22,6 +25,7 @@ All notable changes to this project will be documented in this file. [#34]: https://github.com/sbernauer/breakwater/pull/34 [#36]: https://github.com/sbernauer/breakwater/pull/36 [#37]: https://github.com/sbernauer/breakwater/pull/37 +[#38]: https://github.com/sbernauer/breakwater/pull/38 ## [0.15.0] - 2024-06-12 diff --git a/Cargo.lock b/Cargo.lock index bdabac5..78a08f3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,16 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "ab_glyph" +version = "0.2.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79faae4620f45232f599d9bc7b290f88247a0834162c4495ab2f02d60004adfb" +dependencies = [ + "ab_glyph_rasterizer", + "owned_ttf_parser 0.24.0", +] + [[package]] name = "ab_glyph_rasterizer" version = "0.1.8" @@ -23,6 +33,19 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[package]] +name = "ahash" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if 1.0.0", + "getrandom", + "once_cell", + "version_check", + "zerocopy", +] + [[package]] name = "aho-corasick" version = "1.1.3" @@ -38,6 +61,33 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4aa90d7ce82d4be67b64039a3d588d38dbcc6736577de4a847025ce5b0c468d1" +[[package]] +name = "android-activity" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef6978589202a00cd7e118380c448a08b6ed394c3a8df3a430d0898e3a42d046" +dependencies = [ + "android-properties", + "bitflags 2.6.0", + "cc", + "cesu8", + "jni", + "jni-sys", + "libc", + "log", + "ndk", + "ndk-context", + "ndk-sys", + "num_enum", + "thiserror", +] + +[[package]] +name = "android-properties" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7eb209b1518d6bb87b283c20095f5228ecda460da70b44f0802523dea6da04" + [[package]] name = "android-tzdata" version = "0.1.1" @@ -131,18 +181,47 @@ dependencies = [ "syn", ] +[[package]] +name = "arrayref" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d151e35f61089500b617991b791fc8bfd237ae50cd5950803758a179b41e67a" + [[package]] name = "arrayvec" version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" +[[package]] +name = "as-raw-xcb-connection" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "175571dd1d178ced59193a6fc02dde1b972eb0bc56c892cde9beeceac5bf0f6b" + [[package]] name = "ascii" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d92bec98840b8f03a5ff5413de5293bfcd8bf96467cf5452609f939ec6f5de16" +[[package]] +name = "async-trait" +version = "0.1.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + [[package]] name = "autocfg" version = "1.3.0" @@ -234,10 +313,20 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3dcde5f311c85b8ca30c2e4198d4326bc342c76541590106f5fa4a50946ea499" +[[package]] +name = "block2" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c132eebf10f5cad5289222520a4a058514204aed6d791f1cf4fe8088b82d15f" +dependencies = [ + "objc2", +] + [[package]] name = "breakwater" version = "0.15.0" dependencies = [ + "async-trait", "breakwater-parser", "chrono", "clap", @@ -254,9 +343,10 @@ dependencies = [ "serde_json", "simple_moving_average", "snafu", - "thread-priority", + "softbuffer", "tokio", "vncserver", + "winit", ] [[package]] @@ -293,6 +383,20 @@ name = "bytemuck" version = "1.16.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "102087e286b4677862ea56cf8fc58bb2cdfa8725c40ffb80fe3a008eb7f2fc83" +dependencies = [ + "bytemuck_derive", +] + +[[package]] +name = "bytemuck_derive" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ee891b04274a59bd38b412188e24b849617b2e45a0fd8d057deb63e7403761b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] [[package]] name = "byteorder" @@ -312,6 +416,32 @@ version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50" +[[package]] +name = "calloop" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b99da2f8558ca23c71f4fd15dc57c906239752dd27ff3c00a1d56b685b7cbfec" +dependencies = [ + "bitflags 2.6.0", + "log", + "polling", + "rustix", + "slab", + "thiserror", +] + +[[package]] +name = "calloop-wayland-source" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95a66a987056935f7efce4ab5668920b5d0dac4a7c99991a67395f13702ddd20" +dependencies = [ + "calloop", + "rustix", + "wayland-backend", + "wayland-client", +] + [[package]] name = "cast" version = "0.3.0" @@ -320,14 +450,20 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.1.7" +version = "1.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26a5c3fd7bfa1ce3897a3a3501d362b2d87b7f2583ebcb4a949ec25911025cbc" +checksum = "504bdec147f2cc13c8b57ed9401fd8a147cc66b67ad5cb241394244f2c947549" dependencies = [ "jobserver", "libc", ] +[[package]] +name = "cesu8" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" + [[package]] name = "cexpr" version = "0.6.0" @@ -359,6 +495,12 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + [[package]] name = "chrono" version = "0.4.38" @@ -370,7 +512,7 @@ dependencies = [ "js-sys", "num-traits", "wasm-bindgen", - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -419,9 +561,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.13" +version = "4.5.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fbb260a053428790f3de475e304ff84cdbc4face759ea7a3e64c1edd938a7fc" +checksum = "11d8838454fda655dafd3accb2b6e2bea645b9e4078abe84a22ceb947235c5cc" dependencies = [ "clap_builder", "clap_derive", @@ -429,9 +571,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.13" +version = "4.5.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64b17d7ea74e9f833c7dbf2cbe4fb12ff26783eda4782a8975b72f895c9b4d99" +checksum = "216aec2b177652e3846684cbfe25c9964d18ec45234f0f5da5157b207ed1aab6" dependencies = [ "anstream", "anstyle", @@ -469,6 +611,25 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" +[[package]] +name = "combine" +version = "4.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" +dependencies = [ + "bytes", + "memchr", +] + +[[package]] +name = "concurrent-queue" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "const_format" version = "0.2.32" @@ -489,12 +650,46 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "core-foundation-sys" version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" +[[package]] +name = "core-graphics" +version = "0.23.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c07782be35f9e1140080c6b96f0d44b739e2278479f64e02fdab4e32dfd8b081" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "core-graphics-types", + "foreign-types", + "libc", +] + +[[package]] +name = "core-graphics-types" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "libc", +] + [[package]] name = "crc32fast" version = "1.4.2" @@ -573,6 +768,18 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" +[[package]] +name = "ctor-lite" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f791803201ab277ace03903de1594460708d2d54df6053f2d9e82f592b19e3b" + +[[package]] +name = "cursor-icon" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96a6ac251f4a2aca6b3f91340350eab87ae57c3f127ffeb585e92bd336717991" + [[package]] name = "deranged" version = "0.3.11" @@ -582,6 +789,72 @@ dependencies = [ "powerfmt", ] +[[package]] +name = "dispatch" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" + +[[package]] +name = "dlib" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412" +dependencies = [ + "libloading", +] + +[[package]] +name = "downcast-rs" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" + +[[package]] +name = "dpi" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f25c0e292a7ca6d6498557ff1df68f32c99850012b6ea401cf8daf771f22ff53" + +[[package]] +name = "drm" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98888c4bbd601524c11a7ed63f814b8825f420514f78e96f752c437ae9cbb5d1" +dependencies = [ + "bitflags 2.6.0", + "bytemuck", + "drm-ffi", + "drm-fourcc", + "rustix", +] + +[[package]] +name = "drm-ffi" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97c98727e48b7ccb4f4aea8cfe881e5b07f702d17b7875991881b41af7278d53" +dependencies = [ + "drm-sys", + "rustix", +] + +[[package]] +name = "drm-fourcc" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0aafbcdb8afc29c1a7ee5fbe53b5d62f4565b35a042a662ca9fecd0b54dae6f4" + +[[package]] +name = "drm-sys" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd39dde40b6e196c2e8763f23d119ddb1a8714534bf7d77fa97a65b0feda3986" +dependencies = [ + "libc", + "linux-raw-sys 0.6.4", +] + [[package]] name = "either" version = "1.13.0" @@ -643,6 +916,12 @@ dependencies = [ "zune-inflate", ] +[[package]] +name = "fastrand" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" + [[package]] name = "fdeflate" version = "0.3.4" @@ -677,6 +956,33 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foreign-types" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" +dependencies = [ + "foreign-types-macros", + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-macros" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "foreign-types-shared" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" + [[package]] name = "form_urlencoded" version = "1.2.1" @@ -781,6 +1087,16 @@ dependencies = [ "slab", ] +[[package]] +name = "gethostname" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0176e0459c2e4a1fe232f984bca6890e681076abb9934f6cea7c326f3fc47818" +dependencies = [ + "libc", + "windows-targets 0.48.5", +] + [[package]] name = "getrandom" version = "0.2.15" @@ -842,6 +1158,12 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" +[[package]] +name = "hermit-abi" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" + [[package]] name = "home" version = "0.5.9" @@ -956,7 +1278,7 @@ version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f23ff5ef2b80d608d61efee834934d862cd92461afc0560dedf493e4c033738b" dependencies = [ - "hermit-abi", + "hermit-abi 0.3.9", "libc", "windows-sys 0.52.0", ] @@ -991,6 +1313,28 @@ version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" +[[package]] +name = "jni" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" +dependencies = [ + "cesu8", + "cfg-if 1.0.0", + "combine", + "jni-sys", + "log", + "thiserror", + "walkdir", + "windows-sys 0.45.0", +] + +[[package]] +name = "jni-sys" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" + [[package]] name = "jobserver" version = "0.1.32" @@ -1067,7 +1411,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" dependencies = [ "cfg-if 1.0.0", - "windows-targets", + "windows-targets 0.52.6", +] + +[[package]] +name = "libredox" +version = "0.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3af92c55d7d839293953fcd0fda5ecfe93297cfde6ffbdec13b41d99c0ba6607" +dependencies = [ + "bitflags 2.6.0", + "libc", + "redox_syscall 0.4.1", ] [[package]] @@ -1076,6 +1431,12 @@ version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" +[[package]] +name = "linux-raw-sys" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0b5399f6804fbab912acbd8878ed3532d506b7c951b8f9f164ef90fef39e3f4" + [[package]] name = "lock_api" version = "0.4.12" @@ -1128,6 +1489,15 @@ version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +[[package]] +name = "memmap2" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe751422e4a8caa417e13c3ea66452215d7d63e19e604f4980461212f3ae1322" +dependencies = [ + "libc", +] + [[package]] name = "minimal-lexical" version = "0.2.1" @@ -1150,26 +1520,56 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4569e456d394deccd22ce1c1913e6ea0e54519f577285001215d33557431afe4" dependencies = [ - "hermit-abi", + "hermit-abi 0.3.9", "libc", "wasi", "windows-sys 0.52.0", ] [[package]] -name = "net2" -version = "0.2.39" +name = "ndk" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b13b648036a2339d06de780866fbdfda0dde886de7b3af2ddeba8b14f4ee34ac" +checksum = "c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4" dependencies = [ - "cfg-if 0.1.10", - "libc", - "winapi 0.3.9", + "bitflags 2.6.0", + "jni-sys", + "log", + "ndk-sys", + "num_enum", + "raw-window-handle", + "thiserror", ] [[package]] -name = "new_debug_unreachable" -version = "1.0.6" +name = "ndk-context" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" + +[[package]] +name = "ndk-sys" +version = "0.6.0+11769913" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee6cda3051665f1fb8d9e08fc35c96d5a244fb1be711a03b71118828afc9a873" +dependencies = [ + "jni-sys", +] + +[[package]] +name = "net2" +version = "0.2.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b13b648036a2339d06de780866fbdfda0dde886de7b3af2ddeba8b14f4ee34ac" +dependencies = [ + "cfg-if 0.1.10", + "libc", + "winapi 0.3.9", +] + +[[package]] +name = "new_debug_unreachable" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" @@ -1254,17 +1654,241 @@ dependencies = [ "autocfg", ] +[[package]] +name = "num_enum" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e613fc340b2220f734a8595782c551f1250e969d87d3be1ae0579e8d4065179" +dependencies = [ + "num_enum_derive", +] + +[[package]] +name = "num_enum_derive" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "number_prefix" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" +[[package]] +name = "objc-sys" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb91bdd390c7ce1a8607f35f3ca7151b65afc0ff5ff3b34fa350f7d7c7e4310" + +[[package]] +name = "objc2" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46a785d4eeff09c14c487497c162e92766fbb3e4059a71840cecc03d9a50b804" +dependencies = [ + "objc-sys", + "objc2-encode", +] + +[[package]] +name = "objc2-app-kit" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4e89ad9e3d7d297152b17d39ed92cd50ca8063a89a9fa569046d41568891eff" +dependencies = [ + "bitflags 2.6.0", + "block2", + "libc", + "objc2", + "objc2-core-data", + "objc2-core-image", + "objc2-foundation", + "objc2-quartz-core", +] + +[[package]] +name = "objc2-cloud-kit" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74dd3b56391c7a0596a295029734d3c1c5e7e510a4cb30245f8221ccea96b009" +dependencies = [ + "bitflags 2.6.0", + "block2", + "objc2", + "objc2-core-location", + "objc2-foundation", +] + +[[package]] +name = "objc2-contacts" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5ff520e9c33812fd374d8deecef01d4a840e7b41862d849513de77e44aa4889" +dependencies = [ + "block2", + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-data" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "617fbf49e071c178c0b24c080767db52958f716d9eabdf0890523aeae54773ef" +dependencies = [ + "bitflags 2.6.0", + "block2", + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-image" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55260963a527c99f1819c4f8e3b47fe04f9650694ef348ffd2227e8196d34c80" +dependencies = [ + "block2", + "objc2", + "objc2-foundation", + "objc2-metal", +] + +[[package]] +name = "objc2-core-location" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "000cfee34e683244f284252ee206a27953279d370e309649dc3ee317b37e5781" +dependencies = [ + "block2", + "objc2", + "objc2-contacts", + "objc2-foundation", +] + +[[package]] +name = "objc2-encode" +version = "4.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7891e71393cd1f227313c9379a26a584ff3d7e6e7159e988851f0934c993f0f8" + +[[package]] +name = "objc2-foundation" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8" +dependencies = [ + "bitflags 2.6.0", + "block2", + "dispatch", + "libc", + "objc2", +] + +[[package]] +name = "objc2-link-presentation" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1a1ae721c5e35be65f01a03b6d2ac13a54cb4fa70d8a5da293d7b0020261398" +dependencies = [ + "block2", + "objc2", + "objc2-app-kit", + "objc2-foundation", +] + +[[package]] +name = "objc2-metal" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6" +dependencies = [ + "bitflags 2.6.0", + "block2", + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-quartz-core" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a" +dependencies = [ + "bitflags 2.6.0", + "block2", + "objc2", + "objc2-foundation", + "objc2-metal", +] + +[[package]] +name = "objc2-symbols" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a684efe3dec1b305badae1a28f6555f6ddd3bb2c2267896782858d5a78404dc" +dependencies = [ + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-ui-kit" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8bb46798b20cd6b91cbd113524c490f1686f4c4e8f49502431415f3512e2b6f" +dependencies = [ + "bitflags 2.6.0", + "block2", + "objc2", + "objc2-cloud-kit", + "objc2-core-data", + "objc2-core-image", + "objc2-core-location", + "objc2-foundation", + "objc2-link-presentation", + "objc2-quartz-core", + "objc2-symbols", + "objc2-uniform-type-identifiers", + "objc2-user-notifications", +] + +[[package]] +name = "objc2-uniform-type-identifiers" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44fa5f9748dbfe1ca6c0b79ad20725a11eca7c2218bceb4b005cb1be26273bfe" +dependencies = [ + "block2", + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-user-notifications" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76cfcbf642358e8689af64cee815d139339f3ed8ad05103ed5eaf73db8d84cb3" +dependencies = [ + "bitflags 2.6.0", + "block2", + "objc2", + "objc2-core-location", + "objc2-foundation", +] + [[package]] name = "object" -version = "0.36.2" +version = "0.36.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f203fa8daa7bb185f760ae12bd8e097f63d17041dcdcaf675ac54cdf863170e" +checksum = "27b64972346851a39438c60b341ebc01bba47464ae329e55cf343eb93964efd9" dependencies = [ "memchr", ] @@ -1281,13 +1905,31 @@ version = "11.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b410bbe7e14ab526a0e86877eb47c6996a2bd7746f027ba551028c925390e4e9" +[[package]] +name = "orbclient" +version = "0.3.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52f0d54bde9774d3a51dcf281a5def240c71996bc6ca05d2c847ec8b2b216166" +dependencies = [ + "libredox", +] + [[package]] name = "owned_ttf_parser" version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05e6affeb1632d6ff6a23d2cd40ffed138e82f1532571a26f527c8a284bb2fbb" dependencies = [ - "ttf-parser", + "ttf-parser 0.15.2", +] + +[[package]] +name = "owned_ttf_parser" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "490d3a563d3122bf7c911a59b0add9389e5ec0f5f0c3ac6b91ff235a0e6a7f90" +dependencies = [ + "ttf-parser 0.24.1", ] [[package]] @@ -1328,9 +1970,9 @@ checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if 1.0.0", "libc", - "redox_syscall", + "redox_syscall 0.5.3", "smallvec", - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -1345,6 +1987,26 @@ version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" +[[package]] +name = "pin-project" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "pin-project-lite" version = "0.2.14" @@ -1418,6 +2080,21 @@ dependencies = [ "miniz_oxide", ] +[[package]] +name = "polling" +version = "3.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3ed00ed3fbf728b5816498ecd316d1716eecaced9c0c8d2c5a6740ca214985b" +dependencies = [ + "cfg-if 1.0.0", + "concurrent-queue", + "hermit-abi 0.4.0", + "pin-project-lite", + "rustix", + "tracing", + "windows-sys 0.52.0", +] + [[package]] name = "powerfmt" version = "0.2.0" @@ -1523,6 +2200,15 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" +[[package]] +name = "quick-xml" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f24d770aeca0eacb81ac29dfbc55ebcc09312fdd1f8bbecdc7e4a84e000e3b4" +dependencies = [ + "memchr", +] + [[package]] name = "quote" version = "1.0.36" @@ -1599,9 +2285,9 @@ dependencies = [ [[package]] name = "ravif" -version = "0.11.9" +version = "0.11.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5797d09f9bd33604689e87e8380df4951d4912f01b63f71205e2abd4ae25e6b6" +checksum = "a8f0bfd976333248de2078d350bfdf182ff96e168a24d23d2436cef320dd4bdd" dependencies = [ "avif-serialize", "imgref", @@ -1611,6 +2297,12 @@ dependencies = [ "rgb", ] +[[package]] +name = "raw-window-handle" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539" + [[package]] name = "rayon" version = "1.10.0" @@ -1631,6 +2323,15 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "redox_syscall" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +dependencies = [ + "bitflags 1.3.2", +] + [[package]] name = "redox_syscall" version = "0.5.3" @@ -1677,18 +2378,18 @@ checksum = "ba39f3699c378cd8970968dcbff9c43159ea4cfbd88d43c00b22f2ef10a435d2" [[package]] name = "rgb" -version = "0.8.45" +version = "0.8.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ade4539f42266ded9e755c605bdddf546242b2c961b03b06a7375260788a0523" +checksum = "0f86ae463694029097b846d8f99fd5536740602ae00022c0c50c5600720b2f71" dependencies = [ "bytemuck", ] [[package]] name = "rstest" -version = "0.21.0" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9afd55a67069d6e434a95161415f5beeada95a01c7b815508a82dcb0e1593682" +checksum = "7b423f0e62bdd61734b67cd21ff50871dfaeb9cc74f869dcd6af974fbcb19936" dependencies = [ "futures", "futures-timer", @@ -1698,9 +2399,9 @@ dependencies = [ [[package]] name = "rstest_macros" -version = "0.21.0" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4165dfae59a39dd41d8dec720d3cbfbc71f69744efb480a3920f5d4e0cc6798d" +checksum = "c5e1711e7d14f74b12a58411c542185ef7fb7f2e7f8ee6e2940a883628522b42" dependencies = [ "cfg-if 1.0.0", "glob", @@ -1744,7 +2445,7 @@ dependencies = [ "bitflags 2.6.0", "errno", "libc", - "linux-raw-sys", + "linux-raw-sys 0.4.14", "windows-sys 0.52.0", ] @@ -1755,15 +2456,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3ff8374aa04134254b7995b63ad3dc41c7f7236f69528b28553da7d72efaa967" dependencies = [ "ab_glyph_rasterizer", - "owned_ttf_parser", + "owned_ttf_parser 0.15.2", ] -[[package]] -name = "rustversion" -version = "1.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" - [[package]] name = "ryu" version = "1.0.18" @@ -1779,12 +2474,31 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "scoped-tls" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" + [[package]] name = "scopeguard" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "sctk-adwaita" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6277f0217056f77f1d8f49f2950ac6c278c0d607c45f5ee99328d792ede24ec" +dependencies = [ + "ab_glyph", + "log", + "memmap2", + "smithay-client-toolkit", + "tiny-skia", +] + [[package]] name = "semver" version = "1.0.23" @@ -1793,18 +2507,18 @@ checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" [[package]] name = "serde" -version = "1.0.204" +version = "1.0.205" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12" +checksum = "e33aedb1a7135da52b7c21791455563facbbcc43d0f0f66165b42c21b3dfb150" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.204" +version = "1.0.205" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222" +checksum = "692d6f5ac90220161d6774db30c662202721e64aed9058d2c394f451261420c1" dependencies = [ "proc-macro2", "quote", @@ -1886,6 +2600,40 @@ version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" +[[package]] +name = "smithay-client-toolkit" +version = "0.19.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3457dea1f0eb631b4034d61d4d8c32074caa6cd1ab2d59f2327bd8461e2c0016" +dependencies = [ + "bitflags 2.6.0", + "calloop", + "calloop-wayland-source", + "cursor-icon", + "libc", + "log", + "memmap2", + "rustix", + "thiserror", + "wayland-backend", + "wayland-client", + "wayland-csd-frame", + "wayland-cursor", + "wayland-protocols", + "wayland-protocols-wlr", + "wayland-scanner", + "xkeysym", +] + +[[package]] +name = "smol_str" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd538fb6910ac1099850255cf94a94df6551fbdd602454387d0adb2d1ca6dead" +dependencies = [ + "serde", +] + [[package]] name = "snafu" version = "0.8.4" @@ -1917,6 +2665,39 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "softbuffer" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d623bff5d06f60d738990980d782c8c866997d9194cfe79ecad00aa2f76826dd" +dependencies = [ + "as-raw-xcb-connection", + "bytemuck", + "cfg_aliases", + "core-graphics", + "drm", + "fastrand", + "foreign-types", + "js-sys", + "log", + "memmap2", + "objc2", + "objc2-app-kit", + "objc2-foundation", + "objc2-quartz-core", + "raw-window-handle", + "redox_syscall 0.5.3", + "rustix", + "tiny-xlib", + "wasm-bindgen", + "wayland-backend", + "wayland-client", + "wayland-sys", + "web-sys", + "windows-sys 0.52.0", + "x11rb", +] + [[package]] name = "spin" version = "0.9.8" @@ -1926,6 +2707,12 @@ dependencies = [ "lock_api", ] +[[package]] +name = "strict-num" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731" + [[package]] name = "strsim" version = "0.11.1" @@ -1997,20 +2784,6 @@ dependencies = [ "syn", ] -[[package]] -name = "thread-priority" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d3b04d33c9633b8662b167b847c7ab521f83d1ae20f2321b65b5b925e532e36" -dependencies = [ - "bitflags 2.6.0", - "cfg-if 1.0.0", - "libc", - "log", - "rustversion", - "winapi 0.3.9", -] - [[package]] name = "tiff" version = "0.9.1" @@ -2053,6 +2826,44 @@ dependencies = [ "time-core", ] +[[package]] +name = "tiny-skia" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83d13394d44dae3207b52a326c0c85a8bf87f1541f23b0d143811088497b09ab" +dependencies = [ + "arrayref", + "arrayvec", + "bytemuck", + "cfg-if 1.0.0", + "log", + "tiny-skia-path", +] + +[[package]] +name = "tiny-skia-path" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c9e7fc0c2e86a30b117d0462aa261b72b7a99b7ebd7deb3a14ceda95c5bdc93" +dependencies = [ + "arrayref", + "bytemuck", + "strict-num", +] + +[[package]] +name = "tiny-xlib" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d52f22673960ad13af14ff4025997312def1223bfa7c8e4949d099e6b3d5d1c" +dependencies = [ + "as-raw-xcb-connection", + "ctor-lite", + "libloading", + "pkg-config", + "tracing", +] + [[package]] name = "tiny_http" version = "0.10.0" @@ -2164,12 +2975,34 @@ dependencies = [ "winnow 0.6.18", ] +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" + [[package]] name = "ttf-parser" version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b3e06c9b9d80ed6b745c7159c40b311ad2916abb34a49e9be2653b90db0d8dd" +[[package]] +name = "ttf-parser" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5be21190ff5d38e8b4a2d3b6a3ae57f612cc39c96e83cedeaf7abc338a8bac4a" + [[package]] name = "unicode-bidi" version = "0.3.15" @@ -2191,6 +3024,12 @@ dependencies = [ "tinyvec", ] +[[package]] +name = "unicode-segmentation" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" + [[package]] name = "unicode-xid" version = "0.2.4" @@ -2231,6 +3070,12 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "852e951cb7832cb45cb1169900d19760cfa39b82bc0ea9c0e5a14ae88411c98b" +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + [[package]] name = "vncserver" version = "0.2.2" @@ -2282,6 +3127,18 @@ dependencies = [ "wasm-bindgen-shared", ] +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0" +dependencies = [ + "cfg-if 1.0.0", + "js-sys", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "wasm-bindgen-macro" version = "0.2.92" @@ -2311,6 +3168,115 @@ version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" +[[package]] +name = "wayland-backend" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90e11ce2ca99c97b940ee83edbae9da2d56a08f9ea8158550fd77fa31722993" +dependencies = [ + "cc", + "downcast-rs", + "rustix", + "scoped-tls", + "smallvec", + "wayland-sys", +] + +[[package]] +name = "wayland-client" +version = "0.31.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e321577a0a165911bdcfb39cf029302479d7527b517ee58ab0f6ad09edf0943" +dependencies = [ + "bitflags 2.6.0", + "rustix", + "wayland-backend", + "wayland-scanner", +] + +[[package]] +name = "wayland-csd-frame" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "625c5029dbd43d25e6aa9615e88b829a5cad13b2819c4ae129fdbb7c31ab4c7e" +dependencies = [ + "bitflags 2.6.0", + "cursor-icon", + "wayland-backend", +] + +[[package]] +name = "wayland-cursor" +version = "0.31.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ef9489a8df197ebf3a8ce8a7a7f0a2320035c3743f3c1bd0bdbccf07ce64f95" +dependencies = [ + "rustix", + "wayland-client", + "xcursor", +] + +[[package]] +name = "wayland-protocols" +version = "0.32.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62989625a776e827cc0f15d41444a3cea5205b963c3a25be48ae1b52d6b4daaa" +dependencies = [ + "bitflags 2.6.0", + "wayland-backend", + "wayland-client", + "wayland-scanner", +] + +[[package]] +name = "wayland-protocols-plasma" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f79f2d57c7fcc6ab4d602adba364bf59a5c24de57bd194486bf9b8360e06bfc4" +dependencies = [ + "bitflags 2.6.0", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "wayland-scanner", +] + +[[package]] +name = "wayland-protocols-wlr" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd993de54a40a40fbe5601d9f1fbcaef0aebcc5fda447d7dc8f6dcbaae4f8953" +dependencies = [ + "bitflags 2.6.0", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "wayland-scanner", +] + +[[package]] +name = "wayland-scanner" +version = "0.31.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7b56f89937f1cf2ee1f1259cf2936a17a1f45d8f0aa1019fae6d470d304cfa6" +dependencies = [ + "proc-macro2", + "quick-xml", + "quote", +] + +[[package]] +name = "wayland-sys" +version = "0.31.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43676fe2daf68754ecf1d72026e4e6c15483198b5d24e888b74d3f22f887a148" +dependencies = [ + "dlib", + "log", + "once_cell", + "pkg-config", +] + [[package]] name = "web-sys" version = "0.3.69" @@ -2321,6 +3287,16 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "weezl" version = "0.1.8" @@ -2389,7 +3365,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" dependencies = [ "windows-core", - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -2398,7 +3374,16 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", ] [[package]] @@ -2407,7 +3392,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -2416,7 +3401,37 @@ version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", ] [[package]] @@ -2425,28 +3440,64 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", ] +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + [[package]] name = "windows_i686_gnu" version = "0.52.6" @@ -2459,30 +3510,130 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + [[package]] name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "winit" +version = "0.30.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0be9e76a1f1077e04a411f0b989cbd3c93339e1771cb41e71ac4aee95bfd2c67" +dependencies = [ + "ahash", + "android-activity", + "atomic-waker", + "bitflags 2.6.0", + "block2", + "bytemuck", + "calloop", + "cfg_aliases", + "concurrent-queue", + "core-foundation", + "core-graphics", + "cursor-icon", + "dpi", + "js-sys", + "libc", + "memmap2", + "ndk", + "objc2", + "objc2-app-kit", + "objc2-foundation", + "objc2-ui-kit", + "orbclient", + "percent-encoding", + "pin-project", + "raw-window-handle", + "redox_syscall 0.4.1", + "rustix", + "sctk-adwaita", + "smithay-client-toolkit", + "smol_str", + "tracing", + "unicode-segmentation", + "wasm-bindgen", + "wasm-bindgen-futures", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "wayland-protocols-plasma", + "web-sys", + "web-time", + "windows-sys 0.52.0", + "x11-dl", + "x11rb", + "xkbcommon-dl", +] + [[package]] name = "winnow" version = "0.5.40" @@ -2501,6 +3652,63 @@ dependencies = [ "memchr", ] +[[package]] +name = "x11-dl" +version = "2.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38735924fedd5314a6e548792904ed8c6de6636285cb9fec04d5b1db85c1516f" +dependencies = [ + "libc", + "once_cell", + "pkg-config", +] + +[[package]] +name = "x11rb" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d91ffca73ee7f68ce055750bf9f6eca0780b8c85eff9bc046a3b0da41755e12" +dependencies = [ + "as-raw-xcb-connection", + "gethostname", + "libc", + "libloading", + "once_cell", + "rustix", + "x11rb-protocol", +] + +[[package]] +name = "x11rb-protocol" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec107c4503ea0b4a98ef47356329af139c0a4f7750e621cf2973cd3385ebcb3d" + +[[package]] +name = "xcursor" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d491ee231a51ae64a5b762114c3ac2104b967aadba1de45c86ca42cf051513b7" + +[[package]] +name = "xkbcommon-dl" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d039de8032a9a8856a6be89cea3e5d12fdd82306ab7c94d74e6deab2460651c5" +dependencies = [ + "bitflags 2.6.0", + "dlib", + "log", + "once_cell", + "xkeysym", +] + +[[package]] +name = "xkeysym" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9cc00251562a284751c9973bace760d86c0276c471b4be569fe6b068ee97a56" + [[package]] name = "zerocopy" version = "0.7.35" diff --git a/Cargo.toml b/Cargo.toml index 9606c3b..dbd05a2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,6 +10,7 @@ edition = "2021" repository = "https://github.com/sernauer/breakwater" [workspace.dependencies] +async-trait = "0.1" chrono = "0.4" clap = { version = "4.5", features = ["derive"] } const_format = "0.2" @@ -22,16 +23,17 @@ number_prefix = "0.4" page_size = "0.6" pixelbomber = "0.9" prometheus_exporter = "0.8" -rstest = "0.21" +rstest = "0.22" rusttype = "0.9" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" simple_moving_average = "1.0" snafu = "0.8" -thread-priority = "1.1" +softbuffer = "0.4" tokio = { version = "1.38", features = ["fs", "rt-multi-thread", "net", "io-util", "macros", "process", "signal", "sync", "time"] } trait-variant = "0.1" vncserver = "0.2" +winit = "0.30" # Uses the given path when used locally, and uses the specified version from crates.io when published. breakwater-core = { path = "breakwater-core", version = "0.15.0" } diff --git a/README.md b/README.md index 0a484df..21ff203 100644 --- a/README.md +++ b/README.md @@ -6,10 +6,11 @@ It claims to be the fastest Pixelflut server in existence - at least at the time # Features 1. Accepts Pixelflut commands -2. Can provide a VNC server so that everybody can watch -3. As an alternative it can stream to a RTMP sink, so that you can e.g. directly live-stream into Twitch or YouTube -4. Exposes Prometheus metrics -5. IPv6 and legacy IP support +2. It can start a native display window in your graphical environment +3. Simultaneously it can provide a VNC server so that everybody can watch +4. As an alternative it can stream to a RTMP sink, so that you can e.g. directly live-stream into Twitch or YouTube +5. Exposes Prometheus metrics +6. IPv6 and legacy IP support # Available Pixelflut commands Commands must be sent newline-separated, for more details see [Pixelflut](https://wiki.cccgoe.de/wiki/Pixelflut) @@ -26,13 +27,15 @@ Note: This command needs to be enabled using the `binary-sync-pixels` feature * `OFFSET x y`: Apply offset (x,y) to all further pixel draws on this connection. This can e.g. be used to pre-calculate an image/animation and simply use the OFFSET command to move it around the screen without the need to re-calculate it, e.g. `OFFSET 100 100` # Usage + The easiest way is to continue with the provided [Ready to use Docker setup](#run-in-docker-container) below. If you prefer the manual way (the best performance - as e.g. can use native SIMD instructions) you need to have [Rust installed](https://www.rust-lang.org/tools/install). You may need to install some additional packages with `sudo apt install pkg-config libvncserver-dev` Then you can directly run the server with + ```bash -cargo run --release +cargo run --release -- --vnc ``` The default settings should provide you with a ready-to-use server. @@ -67,7 +70,7 @@ Options: --network-buffer-size The size in bytes of the network buffer used for each open TCP connection. Please use at least 64 KB (64_000 bytes) [default: 262144] -t, --text - Text to display on the screen. The text will be followed by "on " [default: "Pixelflut server (breakwater)"] + Text to display on the screen [default: "Pixelflut server (breakwater)"] --font The font used to render the text on the screen. Should be a ttf file. If you use the default value a copy that ships with breakwater will be used - no need to download and provide the font [default: Arial.ttf] -p, --prometheus-listen-address @@ -82,10 +85,14 @@ Options: Enable rtmp streaming to configured address, e.g. `rtmp://127.0.0.1:1935/live/test` --video-save-folder Enable dump of video stream into file. File location will be `/pixelflut_dump_{timestamp}.mp4 - -v, --vnc-port - Port of the VNC server [default: 5900] -c, --connections-per-ip Allow only a certain number of connections per ip address + --vnc + Enabled a VNC server + -v, --vnc-port + Port of the VNC server [default: 5900] + --native-display + Enable native display output. This requires some form of graphical system (so will probably not work on your server) -h, --help Print help -V, --version @@ -96,10 +103,12 @@ Options: You can also build the binary with `cargo build --release`. The binary will be placed at `target/release/breakwater`. ## Compile time features -Breakwater also has some compile-time features for performance reasons. + +Breakwater also has some compile-time features for dependency or performance reasons. You can get the list of available features by looking at the [Cargo.toml](Cargo.toml). -As of writing the following features are supported +As of writing the following features are supported: +* `native-display` (enabled by default): Starts a graphical window on your local system. Please note that this requires a graphical environment. * `vnc` (enabled by default): Starts a VNC server, where users can connect to. Needs `libvncserver-dev` to be installed. Please note that the VNC server offers basically no latency, but consumes quite some CPU. * `alpha` (disabled by default): Respect alpha values during `PX` commands. Disabled by default as this can cause performance degradation. * `binary-set-pixel` (enabled by default): Allows use of the `PB` command. diff --git a/breakwater/Cargo.toml b/breakwater/Cargo.toml index 43eb225..5a2c837 100644 --- a/breakwater/Cargo.toml +++ b/breakwater/Cargo.toml @@ -14,6 +14,7 @@ path = "src/main.rs" [dependencies] breakwater-parser.workspace = true +async-trait.workspace = true chrono.workspace = true clap.workspace = true const_format.workspace = true @@ -28,19 +29,20 @@ serde_json.workspace = true serde.workspace = true simple_moving_average.workspace = true snafu.workspace = true -thread-priority.workspace = true +softbuffer = { workspace = true, optional = true } tokio.workspace = true vncserver = { workspace = true, optional = true } +winit = { workspace = true, optional = true } [dev-dependencies] rstest.workspace = true [features] # We don't enable binary-sync-pixels by default to make it a bit harder for clients ;) -default = ["vnc", "binary-set-pixel"] +default = ["vnc", "native-display", "binary-set-pixel"] vnc = ["dep:vncserver"] alpha = ["breakwater-parser/alpha"] +native-display = ["dep:softbuffer", "dep:winit"] binary-set-pixel = ["breakwater-parser/binary-set-pixel"] binary-sync-pixels = ["breakwater-parser/binary-sync-pixels"] - diff --git a/breakwater/src/cli_args.rs b/breakwater/src/cli_args.rs index caf7dcc..5507315 100644 --- a/breakwater/src/cli_args.rs +++ b/breakwater/src/cli_args.rs @@ -30,7 +30,6 @@ pub struct CliArgs { pub network_buffer_size: i64, /// Text to display on the screen. - /// The text will be followed by "on ". #[clap(short, long, default_value = "Pixelflut server (breakwater)")] pub text: String, @@ -66,12 +65,21 @@ pub struct CliArgs { #[clap(long)] pub video_save_folder: Option, + /// Allow only a certain number of connections per ip address + #[clap(short, long)] + pub connections_per_ip: Option, + + /// Enabled a VNC server + #[clap(long)] + pub vnc: bool, + /// Port of the VNC server. #[cfg(feature = "vnc")] #[clap(short, long, default_value_t = 5900)] pub vnc_port: u16, - /// Allow only a certain number of connections per ip address - #[clap(short, long)] - pub connections_per_ip: Option, + /// Enable native display output. This requires some form of graphical system (so will probably not work on your + /// server). + #[clap(long)] + pub native_display: bool, } diff --git a/breakwater/src/main.rs b/breakwater/src/main.rs index 01effae..f0340bd 100644 --- a/breakwater/src/main.rs +++ b/breakwater/src/main.rs @@ -2,24 +2,27 @@ use std::{env, num::TryFromIntError, sync::Arc}; use breakwater_parser::SimpleFrameBuffer; use clap::Parser; -use log::{info, trace}; +use log::info; use prometheus_exporter::PrometheusExporter; use sinks::ffmpeg::FfmpegSink; use snafu::{ResultExt, Snafu}; -use tokio::sync::{broadcast, mpsc, oneshot}; +use tokio::{ + sync::{broadcast, mpsc}, + task::JoinError, +}; use crate::{ cli_args::CliArgs, server::Server, + sinks::DisplaySink, statistics::{Statistics, StatisticsEvent, StatisticsInformationEvent, StatisticsSaveMode}, }; +#[cfg(feature = "native-display")] +use crate::sinks::native_display::NativeDisplaySink; + #[cfg(feature = "vnc")] -use { - crate::sinks::vnc::{self, VncServer}, - log::warn, - thread_priority::{ThreadBuilderExt, ThreadPriority}, -}; +use crate::sinks::vnc::VncSink; mod cli_args; mod prometheus_exporter; @@ -49,34 +52,22 @@ pub enum Error { network_buffer_size: i64, }, - #[snafu(display("ffmpeg dump thread error"))] - FfmpegDumpThread { source: sinks::ffmpeg::Error }, - - #[snafu(display("Failed to send ffmpg dump thread termination signal"))] - SendFfmpegDumpTerminationSignal {}, - - #[snafu(display("Failed to join ffmpg dump thread"))] - JoinFfmpegDumpThread { source: tokio::task::JoinError }, + #[snafu(display("Failed to send termination signal"))] + SendTerminationSignal { + source: broadcast::error::SendError<()>, + }, - #[cfg(feature = "vnc")] - #[snafu(display("Failed to spawn VNC server thread"))] - SpawnVncServerThread { source: std::io::Error }, + #[snafu(display("Failed to create sink"))] + CreateSink { source: sinks::Error }, - #[cfg(feature = "vnc")] - #[snafu(display("Failed to send VNC server shutdown signal"))] - SendVncServerShutdownSignal {}, - - #[cfg(feature = "vnc")] - #[snafu(display("Failed to stop VNC server thread"))] - StopVncServerThread {}, + #[snafu(display("Failed to run sink"))] + RunSink { source: sinks::Error }, - #[cfg(feature = "vnc")] - #[snafu(display("Failed to start VNC server"))] - StartVncServer { source: vnc::Error }, + #[snafu(display("Failed to join sink thread"))] + JoinSinkThread { source: JoinError }, - #[cfg(feature = "vnc")] - #[snafu(display("Failed to get cross-platform ThreadPriority. Please report this error message together with your operating system: {message}"))] - GetThreadPriority { message: String }, + #[snafu(display("Failed to stop sink"))] + StopSink { source: sinks::Error }, } #[tokio::main] @@ -95,14 +86,9 @@ async fn main() -> Result<(), Error> { // If we make the channel to big, stats will start to lag behind // TODO: Check performance impact in real-world scenario. Maybe the statistics thread blocks the other threads let (statistics_tx, statistics_rx) = mpsc::channel::(100); - let (statistics_information_tx, statistics_information_rx_for_prometheus_exporter) = + let (statistics_information_tx, statistics_information_rx) = broadcast::channel::(2); - let (ffmpeg_terminate_signal_tx, ffmpeg_terminate_signal_rx) = oneshot::channel(); - - #[cfg(feature = "vnc")] - let (vnc_terminate_signal_tx, vnc_terminate_signal_rx) = oneshot::channel(); - #[cfg(feature = "vnc")] - let statistics_information_rx_for_vnc_server = statistics_information_tx.subscribe(); + let (terminate_signal_tx, terminate_signal_rx) = broadcast::channel::<()>(1); let statistics_save_mode = if args.disable_statistics_save_file { StatisticsSaveMode::Disabled @@ -120,7 +106,7 @@ async fn main() -> Result<(), Error> { let mut server = Server::new( &args.listen_address, - Arc::clone(&fb), + fb.clone(), statistics_tx.clone(), args.network_buffer_size .try_into() @@ -132,9 +118,10 @@ async fn main() -> Result<(), Error> { ) .await .context(StartPixelflutServerSnafu)?; + let mut prometheus_exporter = PrometheusExporter::new( &args.prometheus_listen_address, - statistics_information_rx_for_prometheus_exporter, + statistics_information_rx.resubscribe(), ) .context(StartPrometheusExporterSnafu)?; @@ -142,86 +129,86 @@ async fn main() -> Result<(), Error> { let statistics_thread = tokio::spawn(async move { statistics.start().await }); let prometheus_exporter_thread = tokio::spawn(async move { prometheus_exporter.run().await }); - let ffmpeg_sink = FfmpegSink::new(&args, Arc::clone(&fb)); - let ffmpeg_thread = ffmpeg_sink.map(|sink| { - tokio::spawn(async move { - sink.run(ffmpeg_terminate_signal_rx) - .await - .context(FfmpegDumpThreadSnafu) - .unwrap(); - Ok::<(), Error>(()) - }) - }); + let mut display_sinks = Vec:: + Send>>::new(); + + #[cfg(feature = "native-display")] + { + if let Some(native_display_sink) = NativeDisplaySink::new( + fb.clone(), + &args, + statistics_tx.clone(), + statistics_information_rx.resubscribe(), + terminate_signal_rx.resubscribe(), + ) + .await + .context(CreateSinkSnafu)? + { + display_sinks.push(Box::new(native_display_sink)); + } + } #[cfg(feature = "vnc")] - let vnc_server_thread = { - let fb_for_vnc_server = Arc::clone(&fb); - let mut vnc_server = VncServer::new( - fb_for_vnc_server, - args.vnc_port, - args.fps, - statistics_tx, - statistics_information_rx_for_vnc_server, - vnc_terminate_signal_rx, - args.text, - args.font, + { + if let Some(vnc_sink) = VncSink::new( + fb.clone(), + &args, + statistics_tx.clone(), + statistics_information_rx.resubscribe(), + terminate_signal_rx.resubscribe(), ) - .context(StartVncServerSnafu)?; - - // TODO Use tokio::spawn instead of std::thread::spawn - // I was not able to get to work with async closure - // We than also need to think about setting a priority - std::thread::Builder::new() - .name("breakwater vnc server thread".to_owned()) - .spawn_with_priority( - ThreadPriority::Crossplatform(70.try_into().map_err(|err: &str| { - Error::GetThreadPriority { - message: err.to_string(), - } - })?), - move |_| vnc_server.run().context(StartVncServerSnafu), - ) + .await + .context(CreateSinkSnafu)? + { + display_sinks.push(Box::new(vnc_sink)); + } + } + + let mut ffmpeg_thread_present = false; + if let Some(ffmpeg_sink) = FfmpegSink::new( + fb, + &args, + statistics_tx.clone(), + statistics_information_rx, + terminate_signal_rx, + ) + .await + .context(CreateSinkSnafu)? + { + display_sinks.push(Box::new(ffmpeg_sink)); + ffmpeg_thread_present = true; + } + + let mut sink_threads = Vec::new(); + for mut sink in display_sinks { + sink_threads.push(tokio::spawn(async move { + sink.run().await?; + Ok::<(), sinks::Error>(()) + })); } - .context(SpawnVncServerThreadSnafu)?; tokio::signal::ctrl_c() .await .context(WaitForCtrlCSignalSnafu)?; + terminate_signal_tx + .send(()) + .context(SendTerminationSignalSnafu)?; + prometheus_exporter_thread.abort(); server_listener_thread.abort(); - let ffmpeg_thread_present = ffmpeg_thread.is_some(); - if let Some(ffmpeg_thread) = ffmpeg_thread { - ffmpeg_terminate_signal_tx - .send(()) - .map_err(|_| Error::SendFfmpegDumpTerminationSignal {})?; - - trace!("Waiting for thread dumping data into ffmpeg to terminate"); - ffmpeg_thread.await.context(JoinFfmpegDumpThreadSnafu)??; - trace!("thread dumping data into ffmpeg terminated"); - } - - #[cfg(feature = "vnc")] - { - trace!("Sending termination signal to vnc thread"); - if let Err(err) = vnc_terminate_signal_tx.send(()) { - warn!( - "Failed to send termination signal to vnc thread, it seems to already have terminated: {err:?}", - ) - } - trace!("Joining vnc thread"); - vnc_server_thread - .join() - .map_err(|_| Error::StopVncServerThread {})??; - trace!("Vnc thread terminated"); + for sink_thread in sink_threads { + sink_thread + .await + .context(JoinSinkThreadSnafu)? + .context(StopSinkSnafu)?; } // We need to stop this thread as the last, as others always try to send statistics to it statistics_thread.abort(); if ffmpeg_thread_present { - info!("Successfully shut down (there might still be a ffmped process running - it's complicated)"); + info!("Successfully shut down (there might still be a ffmpeg process running - it's complicated)"); } else { info!("Successfully shut down"); } diff --git a/breakwater/src/sinks/ffmpeg.rs b/breakwater/src/sinks/ffmpeg.rs index ae6e00e..68864a7 100644 --- a/breakwater/src/sinks/ffmpeg.rs +++ b/breakwater/src/sinks/ffmpeg.rs @@ -1,5 +1,6 @@ use std::{process::Stdio, sync::Arc, time::Duration}; +use async_trait::async_trait; use breakwater_parser::FrameBuffer; use chrono::Local; use log::debug; @@ -7,11 +8,11 @@ use snafu::{ResultExt, Snafu}; use tokio::{ io::AsyncWriteExt, process::Command, - sync::oneshot::Receiver, - time::{self}, + sync::{broadcast, mpsc}, + time, }; -use crate::cli_args::CliArgs; +use crate::{sinks::DisplaySink, statistics::StatisticsInformationEvent}; #[derive(Debug, Snafu)] pub enum Error { @@ -22,31 +23,41 @@ pub enum Error { }, #[snafu(display("Failed to write new data to ffmpeg via stdout"))] - WriteDataToFfmeg { source: std::io::Error }, + WriteDataToFfmpeg { source: std::io::Error }, } pub struct FfmpegSink { fb: Arc, + terminate_signal_rx: broadcast::Receiver<()>, + rtmp_address: Option, video_save_folder: Option, fps: u32, } -impl FfmpegSink { - pub fn new(args: &CliArgs, fb: Arc) -> Option { - if args.rtmp_address.is_some() || args.video_save_folder.is_some() { - Some(FfmpegSink { +#[async_trait] +impl DisplaySink for FfmpegSink { + async fn new( + fb: Arc, + cli_args: &crate::cli_args::CliArgs, + _statistics_tx: mpsc::Sender, + _statistics_information_rx: broadcast::Receiver, + terminate_signal_rx: broadcast::Receiver<()>, + ) -> Result, super::Error> { + if cli_args.rtmp_address.is_some() || cli_args.video_save_folder.is_some() { + Ok(Some(Self { fb, - rtmp_address: args.rtmp_address.clone(), - video_save_folder: args.video_save_folder.clone(), - fps: args.fps, - }) + terminate_signal_rx, + rtmp_address: cli_args.rtmp_address.clone(), + video_save_folder: cli_args.video_save_folder.clone(), + fps: cli_args.fps, + })) } else { - None + Ok(None) } } - pub async fn run(&self, mut terminate_signal_rx: Receiver<()>) -> Result<(), Error> { + async fn run(&mut self) -> Result<(), super::Error> { let mut ffmpeg_args: Vec = self .ffmpeg_input_args() .into_iter() @@ -119,7 +130,7 @@ impl FfmpegSink { let mut interval = time::interval(Duration::from_micros(1_000_000 / 30)); loop { - if terminate_signal_rx.try_recv().is_ok() { + if self.terminate_signal_rx.try_recv().is_ok() { // Normally we would send SIGINT to ffmpeg and let the process shutdown gracefully and afterwards call // `command.wait().await`. Hopever using the `nix` crate to send a `SIGINT` resulted in ffmpeg // [2024-05-14T21:35:25Z TRACE breakwater::sinks::ffmpeg] Sending SIGINT to ffmpeg process with pid 58786 @@ -157,11 +168,13 @@ impl FfmpegSink { stdin .write_all(bytes) .await - .context(WriteDataToFfmegSnafu)?; + .context(WriteDataToFfmpegSnafu)?; interval.tick().await; } } +} +impl FfmpegSink { fn ffmpeg_input_args(&self) -> Vec<(String, String)> { let video_size = format!("{}x{}", self.fb.get_width(), self.fb.get_height()); [ diff --git a/breakwater/src/sinks/mod.rs b/breakwater/src/sinks/mod.rs index c4838e4..4eeaec4 100644 --- a/breakwater/src/sinks/mod.rs +++ b/breakwater/src/sinks/mod.rs @@ -1,3 +1,49 @@ +use std::sync::Arc; + +use async_trait::async_trait; +use snafu::Snafu; +use tokio::sync::{broadcast, mpsc}; + +use crate::{ + cli_args::CliArgs, + statistics::{StatisticsEvent, StatisticsInformationEvent}, +}; + pub mod ffmpeg; +#[cfg(feature = "native-display")] +pub mod native_display; #[cfg(feature = "vnc")] pub mod vnc; + +#[allow(clippy::enum_variant_names)] +#[derive(Debug, Snafu)] +pub enum Error { + #[cfg(feature = "native-display")] + #[snafu(display("Native display error"), context(false))] + NativeDisplayError { source: native_display::Error }, + + #[cfg(feature = "vnc")] + #[snafu(display("VNC error"), context(false))] + VncError { source: vnc::Error }, + + #[snafu(display("ffmpeg error"), context(false))] + FfmpegError { source: ffmpeg::Error }, +} + +// The stabilization of async functions in traits in Rust 1.75 did not include support for using traits containing async +// functions as dyn Trait, so we still need to use async_trait here. +#[async_trait] +pub trait DisplaySink { + /// This function can return [`None`] in case this sink is not configured (by looking at the `cli_args`). + async fn new( + fb: Arc, + cli_args: &CliArgs, + statistics_tx: mpsc::Sender, + statistics_information_rx: broadcast::Receiver, + terminate_signal_rx: broadcast::Receiver<()>, + ) -> Result, Error> + where + Self: Sized; + + async fn run(&mut self) -> Result<(), Error>; +} diff --git a/breakwater/src/sinks/native_display.rs b/breakwater/src/sinks/native_display.rs new file mode 100644 index 0000000..bbbbf4a --- /dev/null +++ b/breakwater/src/sinks/native_display.rs @@ -0,0 +1,190 @@ +use std::{num::NonZero, sync::Arc}; + +use async_trait::async_trait; +use breakwater_parser::FrameBuffer; +use log::debug; +use snafu::{ResultExt, Snafu}; +use softbuffer::{Context, Surface}; +use tokio::{ + sync::{broadcast, mpsc}, + task::JoinError, +}; +use winit::{ + application::ApplicationHandler, + error::{EventLoopError, OsError}, + event::WindowEvent, + event_loop::{self, EventLoop}, + platform::wayland::EventLoopBuilderExtWayland, + raw_window_handle::{DisplayHandle, HandleError, HasDisplayHandle}, + window::{Window, WindowAttributes, WindowId}, +}; + +use crate::{ + cli_args::CliArgs, + sinks::DisplaySink, + statistics::{StatisticsEvent, StatisticsInformationEvent}, +}; + +#[derive(Debug, Snafu)] +pub enum Error { + #[snafu(display("Failed to create window"))] + CreateWindow { source: OsError }, + + #[snafu(display("Failed to get display handle"))] + GetDisplayHandle { source: HandleError }, + + #[snafu(display("Failed to join native display thread"))] + JoinNativeDisplayThread { source: JoinError }, + + #[snafu(display("Failed to build eventloop"))] + BuildEventLoop { source: EventLoopError }, + + #[snafu(display("Failed to run eventloop"))] + RunEventLoop { source: EventLoopError }, +} + +// Sorry! Help needed :) +unsafe impl Send for NativeDisplaySink {} + +pub struct NativeDisplaySink { + fb: Arc, + terminate_signal_rx: broadcast::Receiver<()>, + + surface: Option, Arc>>, +} + +#[async_trait] +impl DisplaySink for NativeDisplaySink { + async fn new( + fb: Arc, + cli_args: &CliArgs, + _statistics_tx: mpsc::Sender, + _statistics_information_rx: broadcast::Receiver, + terminate_signal_rx: broadcast::Receiver<()>, + ) -> Result, super::Error> { + if !cli_args.native_display { + return Ok(None); + } + + Ok(Some(Self { + fb, + terminate_signal_rx, + surface: None, + })) + } + + async fn run(&mut self) -> Result<(), super::Error> { + let fb_clone = self.fb.clone(); + let terminate_signal_rx = self.terminate_signal_rx.resubscribe(); + + tokio::task::spawn_blocking(move || { + // We need a owned self, so let's re-create one + let mut self_clone = Self { + fb: fb_clone, + terminate_signal_rx, + surface: None, + }; + + let event_loop = EventLoop::builder() + // FIXME: Can we get rid of this? + .with_any_thread(true) + .build() + .context(BuildEventLoopSnafu)?; + + event_loop + .run_app(&mut self_clone) + .context(RunEventLoopSnafu)?; + + Ok::<(), super::Error>(()) + }) + .await + .context(JoinNativeDisplayThreadSnafu)??; + + Ok(()) + } +} + +impl ApplicationHandler for NativeDisplaySink { + fn resumed(&mut self, event_loop: &winit::event_loop::ActiveEventLoop) { + let window = Arc::new( + event_loop + .create_window(self.window_attributes()) + .context(CreateWindowSnafu) + .unwrap(), + ); + self.surface = { + let context = Context::new(unsafe { + // Fiddling around with lifetimes + std::mem::transmute::( + event_loop + .display_handle() + .context(GetDisplayHandleSnafu) + .unwrap(), + ) + }) + .expect("Failed to create window context"); + Some(Surface::new(&context, window).expect("Failed to create surface")) + }; + } + + fn window_event( + &mut self, + event_loop: &event_loop::ActiveEventLoop, + _window_id: WindowId, + event: WindowEvent, + ) { + if self.terminate_signal_rx.try_recv().is_ok() { + event_loop.exit(); + return; + } + + let Some(surface) = self.surface.as_mut() else { + return; + }; + + match event { + WindowEvent::Resized(_size) => { + surface + .resize( + NonZero::new(self.fb.get_width() as u32).unwrap(), + NonZero::new(self.fb.get_height() as u32).unwrap(), + ) + .expect("Failed to resize surface"); + surface.window().request_redraw(); + } + WindowEvent::RedrawRequested => { + let window = surface.window().clone(); + let mut buffer = surface.buffer_mut().expect("Failed to get mutable buffer"); + + buffer.copy_from_slice( + &self + .fb + .as_pixels() + .iter() + .map(|pixel| (pixel << 8).swap_bytes()) + .collect::>(), + ); + window.pre_present_notify(); + buffer.present().expect("Failed to present buffer"); + window.request_redraw(); + } + WindowEvent::CursorMoved { .. } + | WindowEvent::CursorEntered { .. } + | WindowEvent::CursorLeft { .. } => (), + _ => { + debug!("Received window event: {event:?}"); + } + }; + } +} + +impl NativeDisplaySink { + fn window_attributes(&self) -> WindowAttributes { + Window::default_attributes() + .with_title("Pixelflut server (breakwater)") + .with_inner_size(winit::dpi::LogicalSize::new( + self.fb.get_width() as u32, + self.fb.get_height() as u32, + )) + } +} diff --git a/breakwater/src/sinks/vnc.rs b/breakwater/src/sinks/vnc.rs index 3c8adae..419a272 100644 --- a/breakwater/src/sinks/vnc.rs +++ b/breakwater/src/sinks/vnc.rs @@ -1,21 +1,25 @@ +use core::slice; use std::{sync::Arc, time::Duration}; +use async_trait::async_trait; use breakwater_parser::FrameBuffer; -use core::slice; use number_prefix::NumberPrefix; use rusttype::{point, Font, Scale}; use snafu::{OptionExt, ResultExt, Snafu}; -use tokio::sync::{ - broadcast, - mpsc::{self, Sender}, - oneshot, +use tokio::{ + sync::{broadcast, mpsc}, + time, }; use vncserver::{ rfb_framebuffer_malloc, rfb_get_screen, rfb_init_server, rfb_mark_rect_as_modified, rfb_run_event_loop, RfbScreenInfoPtr, }; -use crate::statistics::{StatisticsEvent, StatisticsInformationEvent}; +use crate::{ + cli_args::CliArgs, + sinks::DisplaySink, + statistics::{StatisticsEvent, StatisticsInformationEvent}, +}; const STATS_HEIGHT: usize = 35; @@ -42,34 +46,34 @@ pub enum Error { } // Sorry! Help needed :) -unsafe impl<'a, FB: FrameBuffer> Send for VncServer<'a, FB> {} +unsafe impl<'a, FB: FrameBuffer> Send for VncSink<'a, FB> {} -pub struct VncServer<'a, FB: FrameBuffer> { +pub struct VncSink<'a, FB: FrameBuffer> { fb: Arc, - screen: RfbScreenInfoPtr, - target_fps: u32, - - statistics_tx: Sender, + statistics_tx: mpsc::Sender, statistics_information_rx: broadcast::Receiver, - terminate_signal_tx: oneshot::Receiver<()>, + terminate_signal_rx: broadcast::Receiver<()>, + screen: RfbScreenInfoPtr, + target_fps: u32, text: String, font: Font<'a>, } -impl<'a, FB: FrameBuffer> VncServer<'a, FB> { - #[allow(clippy::too_many_arguments)] - pub fn new( +#[async_trait] +impl DisplaySink for VncSink<'_, FB> { + async fn new( fb: Arc, - port: u16, - target_fps: u32, - statistics_tx: Sender, + cli_args: &CliArgs, + statistics_tx: mpsc::Sender, statistics_information_rx: broadcast::Receiver, - terminate_signal_tx: oneshot::Receiver<()>, - text: String, - font: String, - ) -> Result { - let font = match font.as_str() { + terminate_signal_rx: broadcast::Receiver<()>, + ) -> Result, super::Error> { + if !cli_args.vnc { + return Ok(None); + } + + let font = match cli_args.font.as_str() { // We ship our own copy of Arial.ttf, so that users don't need to download and provide it "Arial.ttf" => { let font_bytes = include_bytes!("../../../Arial.ttf"); @@ -78,12 +82,12 @@ impl<'a, FB: FrameBuffer> VncServer<'a, FB> { })? } _ => { - let font_bytes = std::fs::read(&font).context(ReadFontFileSnafu { - font_file: font.to_string(), + let font_bytes = std::fs::read(&cli_args.font).context(ReadFontFileSnafu { + font_file: cli_args.font.clone(), })?; Font::try_from_vec(font_bytes).context(ConstructFontFromFontFileSnafu { - font_file: font.to_string(), + font_file: cli_args.font.clone(), })? } }; @@ -97,29 +101,28 @@ impl<'a, FB: FrameBuffer> VncServer<'a, FB> { (*screen).serverFormat.depth = 24; } unsafe { - (*screen).port = port as i32; - (*screen).ipv6port = port as i32; + (*screen).port = cli_args.vnc_port as i32; + (*screen).ipv6port = cli_args.vnc_port as i32; } rfb_framebuffer_malloc(screen, (fb.get_size() * 4/* bytes per pixel */) as u64); rfb_init_server(screen); rfb_run_event_loop(screen, 1, 1); - Ok(VncServer { + // FIXME: Only return Some in case VNC is enabled + Ok(Some(Self { fb, - screen, - target_fps, statistics_tx, statistics_information_rx, - terminate_signal_tx, - text, + terminate_signal_rx, + screen, + target_fps: cli_args.fps, + text: cli_args.text.clone(), font, - }) + })) } - pub fn run(&mut self) -> Result<(), Error> { - let target_loop_duration = Duration::from_micros(1_000_000 / self.target_fps as u64); - + async fn run(&mut self) -> Result<(), super::Error> { let vnc_fb_slice: &mut [u32] = unsafe { slice::from_raw_parts_mut((*self.screen).frameBuffer as *mut u32, self.fb.get_size()) }; @@ -128,12 +131,15 @@ impl<'a, FB: FrameBuffer> VncServer<'a, FB> { let height_up_to_stats_text = self.fb.get_height() - STATS_HEIGHT - 1; let fb_size_up_to_stats_text = self.fb.get_width() * height_up_to_stats_text; + let mut interval = + time::interval(Duration::from_micros(1_000_000 / self.target_fps as u64)); loop { - if self.terminate_signal_tx.try_recv().is_ok() { + if self.terminate_signal_rx.try_recv().is_ok() { return Ok(()); } - let start = std::time::Instant::now(); + // I don't think we need to use spawn_blocking or something like that, as this operation should hopefully be + // a quick memcpy. But I'm no expert on this. vnc_fb_slice[0..fb_size_up_to_stats_text] .copy_from_slice(&self.fb.as_pixels()[0..fb_size_up_to_stats_text]); @@ -146,7 +152,8 @@ impl<'a, FB: FrameBuffer> VncServer<'a, FB> { height_up_to_stats_text as i32, ); self.statistics_tx - .blocking_send(StatisticsEvent::FrameRendered) + .send(StatisticsEvent::VncFrameRendered) + .await .context(WriteToStatisticsChannelSnafu)?; if !self.statistics_information_rx.is_empty() { @@ -157,10 +164,12 @@ impl<'a, FB: FrameBuffer> VncServer<'a, FB> { self.display_stats(statistics_information_event); } - std::thread::sleep(target_loop_duration.saturating_sub(start.elapsed())); + interval.tick().await; } } +} +impl VncSink<'_, FB> { fn display_stats(&mut self, stats: StatisticsInformationEvent) { self.draw_rect( 0, diff --git a/breakwater/src/statistics.rs b/breakwater/src/statistics.rs index a47460d..7c58a17 100644 --- a/breakwater/src/statistics.rs +++ b/breakwater/src/statistics.rs @@ -45,7 +45,7 @@ pub enum StatisticsEvent { ConnectionClosed { ip: IpAddr }, ConnectionDenied { ip: IpAddr }, BytesRead { ip: IpAddr, bytes: u64 }, - FrameRendered, + VncFrameRendered, } pub enum StatisticsSaveMode { @@ -163,7 +163,7 @@ impl Statistics { StatisticsEvent::BytesRead { ip, bytes } => { *self.bytes_for_ip.entry(ip).or_insert(0) += bytes; } - StatisticsEvent::FrameRendered => self.frame += 1, + StatisticsEvent::VncFrameRendered => self.frame += 1, } // As there is an event for every frame we are guaranteed to land here every second diff --git a/default.nix b/default.nix index db4a67a..466cfc6 100644 --- a/default.nix +++ b/default.nix @@ -1,14 +1,26 @@ -{ nixpkgs ? import {} }: - -nixpkgs.mkShell { - buildInputs = [ - nixpkgs.pkg-config - nixpkgs.clang - nixpkgs.libclang - nixpkgs.libvncserver - nixpkgs.libvncserver.dev +{ pkgs ? import {} }: + +pkgs.stdenv.mkDerivation rec { + name = "dev-shell"; + + buildInputs = with pkgs; [ + pkg-config + clang + libclang + libvncserver + libvncserver.dev + + # Needed for native-display feature + wayland + libGL + libxkbcommon ]; - LIBCLANG_PATH = "${nixpkgs.libclang.lib}/lib"; - LIBVNCSERVER_HEADER_FILE = "${nixpkgs.libvncserver.dev}/include/rfb/rfb.h"; + LIBCLANG_PATH = "${pkgs.libclang.lib}/lib"; + LIBVNCSERVER_HEADER_FILE = "${pkgs.libvncserver.dev}/include/rfb/rfb.h"; + + # Needed for native-display feature + WINIT_UNIX_BACKEND = "wayland"; + LD_LIBRARY_PATH = "${pkgs.lib.makeLibraryPath buildInputs}"; + XDG_DATA_DIRS = builtins.getEnv "XDG_DATA_DIRS"; }