From e98ad34ab083f3ddb66140bd4287569f7759ccd0 Mon Sep 17 00:00:00 2001 From: Michael Aaron Murphy Date: Tue, 21 Apr 2026 16:43:58 +0200 Subject: [PATCH 1/4] feat(sound): rebase with varlink sound settings API --- Cargo.lock | 443 +++----- Cargo.toml | 2 +- cosmic-settings/Cargo.toml | 14 +- .../src/pages/sound/device_profiles.rs | 101 +- cosmic-settings/src/pages/sound/mod.rs | 206 ++-- crates/cosmic-pipewire/Cargo.toml | 21 - crates/cosmic-pipewire/LICENSE.md | 358 ------- crates/cosmic-pipewire/src/device.rs | 27 - crates/cosmic-pipewire/src/port.rs | 98 -- crates/cosmic-pipewire/src/spa_utils.rs | 150 --- debian/control | 3 +- pages/sound/Cargo.toml | 16 + pages/sound/src/lib.rs | 58 ++ pages/sound/src/model.rs | 453 +++++++++ subscriptions/pulse/Cargo.toml | 12 - subscriptions/pulse/src/lib.rs | 744 -------------- subscriptions/sound/Cargo.toml | 21 - subscriptions/sound/LICENSE.md | 359 ------- subscriptions/sound/src/lib.rs | 956 ------------------ 19 files changed, 865 insertions(+), 3177 deletions(-) delete mode 100644 crates/cosmic-pipewire/Cargo.toml delete mode 100644 crates/cosmic-pipewire/LICENSE.md delete mode 100644 crates/cosmic-pipewire/src/device.rs delete mode 100644 crates/cosmic-pipewire/src/port.rs delete mode 100644 crates/cosmic-pipewire/src/spa_utils.rs create mode 100644 pages/sound/Cargo.toml create mode 100644 pages/sound/src/lib.rs create mode 100644 pages/sound/src/model.rs delete mode 100644 subscriptions/pulse/Cargo.toml delete mode 100644 subscriptions/pulse/src/lib.rs delete mode 100644 subscriptions/sound/Cargo.toml delete mode 100644 subscriptions/sound/LICENSE.md delete mode 100644 subscriptions/sound/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index adddd0994..160b05b4d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -242,16 +242,6 @@ dependencies = [ "libc", ] -[[package]] -name = "annotate-snippets" -version = "0.11.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "710e8eae58854cdc1790fcb56cca04d712a17be849eeb81da2a724bf4bae2bc4" -dependencies = [ - "anstyle", - "unicode-width 0.2.2", -] - [[package]] name = "anstream" version = "1.0.0" @@ -288,7 +278,7 @@ version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" dependencies = [ - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -299,7 +289,7 @@ checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" dependencies = [ "anstyle", "once_cell_polyfill", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -651,7 +641,7 @@ dependencies = [ "anyhow", "arrayvec", "log", - "nom 8.0.0", + "nom", "num-rational", "v_frame", ] @@ -695,25 +685,6 @@ dependencies = [ "serde", ] -[[package]] -name = "bindgen" -version = "0.72.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895" -dependencies = [ - "annotate-snippets", - "bitflags 2.11.1", - "cexpr", - "clang-sys", - "itertools 0.13.0", - "proc-macro2", - "quote", - "regex", - "rustc-hash 2.1.2", - "shlex", - "syn 2.0.117", -] - [[package]] name = "bit-set" version = "0.8.0" @@ -997,15 +968,6 @@ dependencies = [ "shlex", ] -[[package]] -name = "cexpr" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" -dependencies = [ - "nom 7.1.3", -] - [[package]] name = "cfb" version = "0.7.3" @@ -1017,16 +979,6 @@ dependencies = [ "uuid", ] -[[package]] -name = "cfg-expr" -version = "0.20.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c6b04e07d8080154ed4ac03546d9a2b303cc2fe1901ba0b35b301516e289368" -dependencies = [ - "smallvec", - "target-lexicon", -] - [[package]] name = "cfg-if" version = "1.0.4" @@ -1078,17 +1030,6 @@ dependencies = [ "inout", ] -[[package]] -name = "clang-sys" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" -dependencies = [ - "glob", - "libc", - "libloading", -] - [[package]] name = "clap" version = "4.6.1" @@ -1272,21 +1213,6 @@ version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e57e3272f0190c3f1584272d613719ba5fc7df7f4942fe542e63d949cf3a649b" -[[package]] -name = "convert_case" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baaaa0ecca5b51987b9423ccdc971514dd8b0bb7b4060b983d3664dad3f1f89f" -dependencies = [ - "unicode-segmentation", -] - -[[package]] -name = "cookie-factory" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9885fa71e26b8ab7855e2ec7cae6e9b380edff76cd052e07c683a0319d51b3a2" - [[package]] name = "core-foundation" version = "0.9.4" @@ -1495,19 +1421,6 @@ dependencies = [ "xdg-shell-wrapper-config", ] -[[package]] -name = "cosmic-pipewire" -version = "1.0.7" -dependencies = [ - "intmap", - "libspa", - "libspa-sys", - "pipewire", - "serde", - "serde_json", - "tracing", -] - [[package]] name = "cosmic-protocols" version = "0.1.0" @@ -1570,12 +1483,13 @@ dependencies = [ "cosmic-settings-a11y-manager-subscription", "cosmic-settings-accessibility-subscription", "cosmic-settings-airplane-mode-subscription", + "cosmic-settings-audio-client", "cosmic-settings-bluetooth-subscription", "cosmic-settings-config", "cosmic-settings-daemon-config", "cosmic-settings-network-manager-subscription", "cosmic-settings-page", - "cosmic-settings-sound-subscription", + "cosmic-settings-sound", "cosmic-settings-upower-subscription", "cosmic-settings-wallpaper", "derive_setters", @@ -1593,7 +1507,8 @@ dependencies = [ "icu", "image", "indexmap 2.14.0", - "itertools 0.14.0", + "intmap", + "itertools", "itoa", "jiff", "libcosmic", @@ -1629,6 +1544,7 @@ dependencies = [ "xkeysym", "zbus", "zbus_polkit", + "zlink", ] [[package]] @@ -1667,6 +1583,33 @@ dependencies = [ "tokio", ] +[[package]] +name = "cosmic-settings-audio-client" +version = "1.0.0" +source = "git+https://github.com/pop-os/cosmic-settings-daemon?branch=varlink#f87f005c8183904f4127eb857e133f5385a45cdc" +dependencies = [ + "cosmic-settings-audio-core", + "dirs", + "futures-util", + "ron 0.12.1", + "serde", + "tokio", + "tokio-util", + "tracing", + "zlink", +] + +[[package]] +name = "cosmic-settings-audio-core" +version = "1.0.0" +source = "git+https://github.com/pop-os/cosmic-settings-daemon?branch=varlink#f87f005c8183904f4127eb857e133f5385a45cdc" +dependencies = [ + "dirs", + "ron 0.12.1", + "serde", + "zlink", +] + [[package]] name = "cosmic-settings-bluetooth-subscription" version = "1.0.7" @@ -1729,7 +1672,7 @@ dependencies = [ "cosmic-dbus-networkmanager", "futures", "iced_futures", - "itertools 0.14.0", + "itertools", "nm-secret-agent-manager", "secret-service", "secure-string", @@ -1753,26 +1696,12 @@ dependencies = [ ] [[package]] -name = "cosmic-settings-pulse-subscription" +name = "cosmic-settings-sound" version = "0.1.0" dependencies = [ - "futures", - "iced_futures", - "libpulse-binding", - "log", - "rustix 1.1.4", -] - -[[package]] -name = "cosmic-settings-sound-subscription" -version = "1.0.7" -dependencies = [ - "cosmic-pipewire", + "cosmic-settings-audio-client", "futures", "intmap", - "libcosmic", - "numtoa", - "rustix 1.1.4", "tokio", "tracing", ] @@ -2173,7 +2102,7 @@ dependencies = [ "libc", "option-ext", "redox_users", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -2357,7 +2286,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -2492,7 +2421,7 @@ version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "59a98bbaacea1c0eb6a0876280051b892eb73594fd90cf3b20e9c817029c57d2" dependencies = [ - "toml 0.5.11", + "toml", ] [[package]] @@ -2696,7 +2625,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc6d3a3635983a889f065aa9ce760384713f23a9b4a04f696f86c39a5d7a6a5a" dependencies = [ "indexmap 2.14.0", - "nom 8.0.0", + "nom", ] [[package]] @@ -2925,12 +2854,6 @@ version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "151665d9be52f9bb40fc7966565d39666f2d1e69233571b71b87791c7e0528b3" -[[package]] -name = "glob" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" - [[package]] name = "glow" version = "0.16.0" @@ -4087,15 +4010,6 @@ version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" -[[package]] -name = "itertools" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" -dependencies = [ - "either", -] - [[package]] name = "itertools" version = "0.14.0" @@ -4129,7 +4043,7 @@ dependencies = [ "portable-atomic", "portable-atomic-util", "serde_core", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -4471,7 +4385,7 @@ version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a1886916523694cd6ea3d175f03a1e5010699a2a4cc13696d83d7bea1d80638" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -4614,33 +4528,6 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" -[[package]] -name = "libpulse-binding" -version = "2.30.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "909eb3049e16e373680fe65afe6e2a722ace06b671250cc4849557bc57d6a397" -dependencies = [ - "bitflags 2.11.1", - "libc", - "libpulse-sys", - "num-derive", - "num-traits", - "winapi", -] - -[[package]] -name = "libpulse-sys" -version = "1.23.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d74371848b22e989f829cc1621d2ebd74960711557d8b45cfe740f60d0a05e61" -dependencies = [ - "libc", - "num-derive", - "num-traits", - "pkg-config", - "winapi", -] - [[package]] name = "libredox" version = "0.1.16" @@ -4653,34 +4540,6 @@ dependencies = [ "redox_syscall 0.7.4", ] -[[package]] -name = "libspa" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6b8cfa2a7656627b4c92c6b9ef929433acd673d5ab3708cda1b18478ac00df4" -dependencies = [ - "bitflags 2.11.1", - "cc", - "convert_case", - "cookie-factory", - "libc", - "libspa-sys", - "nix", - "nom 8.0.0", - "system-deps", -] - -[[package]] -name = "libspa-sys" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "901049455d2eb6decf9058235d745237952f4804bc584c5fcb41412e6adcc6e0" -dependencies = [ - "bindgen", - "cc", - "system-deps", -] - [[package]] name = "libudev-sys" version = "0.1.4" @@ -4961,12 +4820,6 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" -[[package]] -name = "minimal-lexical" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" - [[package]] name = "miniz_oxide" version = "0.8.9" @@ -5067,18 +4920,6 @@ version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" -[[package]] -name = "nix" -version = "0.30.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" -dependencies = [ - "bitflags 2.11.1", - "cfg-if", - "cfg_aliases 0.2.1", - "libc", -] - [[package]] name = "nm-secret-agent-manager" version = "0.1.0" @@ -5096,16 +4937,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "nom" -version = "7.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" -dependencies = [ - "memchr", - "minimal-lexical", -] - [[package]] name = "nom" version = "8.0.0" @@ -5163,7 +4994,7 @@ version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -5279,12 +5110,6 @@ dependencies = [ "syn 2.0.117", ] -[[package]] -name = "numtoa" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e4d8a81ede501fad07191e746a299f4d79f6dcd053bab1b97af4ff5a90099f2" - [[package]] name = "objc" version = "0.2.7" @@ -5812,34 +5637,6 @@ dependencies = [ "futures-io", ] -[[package]] -name = "pipewire" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9688b89abf11d756499f7c6190711d6dbe5a3acdb30c8fbf001d6596d06a8d44" -dependencies = [ - "anyhow", - "bitflags 2.11.1", - "libc", - "libspa", - "libspa-sys", - "nix", - "once_cell", - "pipewire-sys", - "thiserror 2.0.18", -] - -[[package]] -name = "pipewire-sys" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb028afee0d6ca17020b090e3b8fa2d7de23305aef975c7e5192a5050246ea36" -dependencies = [ - "bindgen", - "libspa-sys", - "system-deps", -] - [[package]] name = "pkg-config" version = "0.3.33" @@ -6201,7 +5998,7 @@ dependencies = [ "built", "cfg-if", "interpolate_name", - "itertools 0.14.0", + "itertools", "libc", "libfuzzer-sys", "log", @@ -6523,7 +6320,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.12.1", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -6550,6 +6347,12 @@ dependencies = [ "unicode-script", ] +[[package]] +name = "ryu" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" + [[package]] name = "same-file" version = "1.0.6" @@ -6716,15 +6519,6 @@ dependencies = [ "syn 2.0.117", ] -[[package]] -name = "serde_spanned" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6662b5879511e06e8999a8a235d848113e942c9124f211511b16466ee2995f26" -dependencies = [ - "serde_core", -] - [[package]] name = "serde_with" version = "3.18.0" @@ -6953,7 +6747,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" dependencies = [ "libc", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -7148,19 +6942,6 @@ dependencies = [ "windows 0.62.2", ] -[[package]] -name = "system-deps" -version = "7.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "396a35feb67335377e0251fcbc1092fc85c484bd4e3a7a54319399da127796e7" -dependencies = [ - "cfg-expr", - "heck 0.5.0", - "pkg-config", - "toml 1.1.2+spec-1.1.0", - "version-compare", -] - [[package]] name = "taffy" version = "0.9.2" @@ -7173,12 +6954,6 @@ dependencies = [ "slotmap", ] -[[package]] -name = "target-lexicon" -version = "0.13.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df7f62577c25e07834649fc3b39fafdc597c0a3527dc1c60129201ccfcbaa50c" - [[package]] name = "temp-dir" version = "0.1.16" @@ -7195,7 +6970,7 @@ dependencies = [ "getrandom 0.3.4", "once_cell", "rustix 1.1.4", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -7411,30 +7186,29 @@ dependencies = [ "futures-core", "pin-project-lite", "tokio", + "tokio-util", ] [[package]] -name = "toml" -version = "0.5.11" +name = "tokio-util" +version = "0.7.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" +checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" dependencies = [ - "serde", + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", ] [[package]] name = "toml" -version = "1.1.2+spec-1.1.0" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81f3d15e84cbcd896376e6730314d59fb5a87f31e4b038454184435cd57defee" +checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" dependencies = [ - "indexmap 2.14.0", - "serde_core", - "serde_spanned", - "toml_datetime", - "toml_parser", - "toml_writer", - "winnow 1.0.1", + "serde", ] [[package]] @@ -7467,12 +7241,6 @@ dependencies = [ "winnow 1.0.1", ] -[[package]] -name = "toml_writer" -version = "1.1.1+spec-1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "756daf9b1013ebe47a8776667b466417e2d4c5679d441c26230efd9ef78692db" - [[package]] name = "tracing" version = "0.1.44" @@ -7595,7 +7363,7 @@ checksum = "f2f6fb2847f6742cd76af783a2a2c49e9375d0a111c7bef6f71cd9e738c72d6e" dependencies = [ "memoffset", "tempfile", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -7800,12 +7568,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" -[[package]] -name = "version-compare" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03c2856837ef78f57382f06b2b8563a2f512f7185d732608fd9176cb3b8edf0e" - [[package]] name = "version_check" version = "0.9.5" @@ -8263,7 +8025,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.61.2", ] [[package]] @@ -9369,6 +9131,73 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "zlink" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b895b99588dceb73f4d349b8323eabad9a97d48ce83698d475c7223727c6148" +dependencies = [ + "zlink-smol", + "zlink-tokio", +] + +[[package]] +name = "zlink-core" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd12701bd1d42a982b931f0159cf5054bf13d90e7828a8377dfc02ed4b00342d" +dependencies = [ + "futures-util", + "itoa", + "libc", + "pin-project-lite", + "rustix 1.1.4", + "ryu", + "serde", + "serde_json", + "tracing", + "zlink-macros", +] + +[[package]] +name = "zlink-macros" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6f2416a5f504dfd7e04fee49f31abafe3314a3f62b4ddaa8e9a5fd496d4dd50" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "zlink-smol" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bc53cd0d636ad753f759aab0abb1f456e985c3938a279d11ebda92340ae37b1" +dependencies = [ + "async-broadcast", + "async-channel", + "async-io", + "futures-lite", + "futures-util", + "pin-project-lite", + "zlink-core", +] + +[[package]] +name = "zlink-tokio" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28cbf366ac77ab41bf9a8d43535d3d620a072f7957813e03355d3d010c16cc4f" +dependencies = [ + "futures-util", + "pin-project-lite", + "tokio", + "tokio-stream", + "zlink-core", +] + [[package]] name = "zmij" version = "1.0.21" diff --git a/Cargo.toml b/Cargo.toml index f4f5b4372..51a808a2e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [workspace] -members = ["cosmic-settings", "crates/*", "page", "pages/*", "subscriptions/*"] +members = ["cosmic-settings", "page", "pages/*", "subscriptions/*"] default-members = ["cosmic-settings"] resolver = "3" diff --git a/cosmic-settings/Cargo.toml b/cosmic-settings/Cargo.toml index 7a50cf7c7..ea1d3f4fb 100644 --- a/cosmic-settings/Cargo.toml +++ b/cosmic-settings/Cargo.toml @@ -35,10 +35,8 @@ cosmic-settings-a11y-manager-subscription = { path = "../subscriptions/a11y-mana cosmic-settings-airplane-mode-subscription = { path = "../subscriptions/airplane-mode", optional = true } cosmic-settings-bluetooth-subscription = { path = "../subscriptions/bluetooth", optional = true } cosmic-settings-network-manager-subscription = { path = "../subscriptions/network-manager", optional = true } +cosmic-settings-sound = { path = "../pages/sound", optional = true } cosmic-settings-upower-subscription = { path = "../subscriptions/upower", optional = true } -cosmic-settings-sound-subscription = { path = "../subscriptions/sound", optional = true, features = [ - "auto-profile-init", -] } cosmic-settings-wallpaper = { path = "../pages/wallpapers" } cosmic-settings-daemon-config = { git = "https://github.com/pop-os/cosmic-settings-daemon", optional = true } derive_setters = "0.1.9" @@ -98,6 +96,14 @@ gettext-rs = { version = "0.7.7", features = [ num-traits = "0.2" pwhash = "1" which = "8.0.0" +zlink = "0.5.0" +intmap = "3.1.3" + +[dependencies.cosmic-settings-audio-client] +git = "https://github.com/pop-os/cosmic-settings-daemon" +branch = "varlink" +features = ["codec"] +optional = true [dependencies.icu] version = "2.1.1" @@ -183,7 +189,7 @@ page-region = [ "dep:zbus", "dep:accounts-zbus", ] -page-sound = ["dep:cosmic-settings-sound-subscription"] +page-sound = ["dep:cosmic-settings-audio-client", "dep:cosmic-settings-sound"] page-users = ["xdg-portal", "dep:accounts-zbus", "dep:zbus", "dep:zbus_polkit"] page-window-management = ["cosmic-comp-config", "dep:cosmic-settings-config"] page-workspaces = ["cosmic-comp-config"] diff --git a/cosmic-settings/src/pages/sound/device_profiles.rs b/cosmic-settings/src/pages/sound/device_profiles.rs index 4e4e62774..66c104560 100644 --- a/cosmic-settings/src/pages/sound/device_profiles.rs +++ b/cosmic-settings/src/pages/sound/device_profiles.rs @@ -1,13 +1,26 @@ // Copyright 2025 System76 // SPDX-License-Identifier: GPL-3.0-only -use cosmic::{Apply, widget}; +use super::model; +use cosmic::iced::futures; +use cosmic::{Apply, iced, widget}; +use cosmic_settings_audio_client::{self as audio_client, CosmicAudioProxy}; use cosmic_settings_page::{self as page, Section, section}; -use cosmic_settings_sound_subscription::{self as subscription}; +use futures::executor::block_on; use slotmap::SlotMap; +use std::cell::RefCell; +use std::rc::Rc; +use std::sync::Arc; #[derive(Clone, Debug)] -pub enum Message {} +pub enum Message { + /// Update for the model. + Model(cosmic_settings_sound::Message), + /// Set the profile of a sound device. + SetProfile(u32, u32), + /// Surface Action + Surface(cosmic::surface::Action), +} impl From for crate::pages::Message { fn from(message: Message) -> Self { @@ -24,6 +37,8 @@ impl From for crate::Message { #[derive(Default)] pub struct Page { entity: page::Entity, + model: model::Model, + client: Option>>, } impl page::AutoBind for Page {} @@ -41,55 +56,83 @@ impl page::Page for Page { Some(vec![sections.insert(view())]) } - fn on_leave(&mut self) -> cosmic::Task { - cosmic::Task::done(crate::pages::Message::Sound(super::Message::Reload)) - } - fn set_id(&mut self, entity: cosmic_settings_page::Entity) { self.entity = entity; } - fn subscription( - &self, - _core: &cosmic::Core, - ) -> cosmic::iced::Subscription { - cosmic::iced::Subscription::run(subscription::watch) - .map(|message| super::Message::Subscription(message).into()) + fn on_leave(&mut self) -> cosmic::Task { + *self = Page { + entity: self.entity, + ..Page::default() + }; + cosmic::Task::none() + } + + fn subscription(&self, _core: &cosmic::Core) -> iced::Subscription { + iced::Subscription::run(|| { + iced::stream::channel( + 1, + move |emitter: futures::channel::mpsc::Sender| async move { + cosmic_settings_sound::subscribe(emitter, |m| Message::Model(m).into()).await + }, + ) + }) } } impl Page { - pub fn update(&mut self, _message: Message) -> cosmic::Task { + pub fn update(&mut self, message: Message) -> cosmic::Task { + match message { + Message::Model(cosmic_settings_sound::Message::Subscription(message)) => { + self.model.update(message); + } + + Message::Model(cosmic_settings_sound::Message::Client(client)) => { + if let Some(client) = Arc::into_inner(client) { + self.client = Some(Rc::new(RefCell::new(client))); + self.model = model::Model { + text: model::Text { + hd_audio: fl!("sound-hd-audio"), + usb_audio: fl!("sound-usb-audio"), + }, + ..model::Model::default() + }; + } + } + + Message::Surface(a) => return cosmic::task::message(crate::app::Message::Surface(a)), + + Message::SetProfile(id, index) => { + if let Some(client) = self.client.clone() { + block_on(async move { + _ = client.borrow_mut().conn.set_profile(id, index, true).await; + }); + } + } + } + cosmic::Task::none() } } pub fn view() -> Section { - Section::default().view::(move |binder, _page, _section| { - let sound_page_id = binder.find_page_by_id("sound").unwrap().0; - let sound_page = binder.page[sound_page_id] - .downcast_ref::() - .unwrap(); - - let devices = sound_page - .model - .device_profile_dropdowns - .iter() - .cloned() - .map(|(device_id, name, active_profile, indexes, descriptions)| { + Section::default().view::(move |_, page, _section| { + let devices = page.model.device_profile_dropdowns.iter().cloned().map( + |(device_id, name, active_profile, indexes, descriptions)| { let dropdown = widget::dropdown::popup_dropdown( descriptions, active_profile, - move |id| super::Message::SetProfile(device_id, indexes[id]), + move |id| Message::SetProfile(device_id, indexes[id]), cosmic::iced::window::Id::RESERVED, - super::Message::Surface, + Message::Surface, crate::Message::from, ) .apply(cosmic::Element::from) .map(crate::pages::Message::from); widget::settings::item::builder(name).control(dropdown) - }); + }, + ); widget::settings::section().extend(devices).into() }) diff --git a/cosmic-settings/src/pages/sound/mod.rs b/cosmic-settings/src/pages/sound/mod.rs index 0e7816d5d..d0c842bed 100644 --- a/cosmic-settings/src/pages/sound/mod.rs +++ b/cosmic-settings/src/pages/sound/mod.rs @@ -3,13 +3,19 @@ pub mod device_profiles; -use cosmic::iced::{Alignment, Length, window}; +use std::cell::RefCell; +use std::rc::Rc; +use std::sync::Arc; + +use cosmic::iced::{self, Alignment, Length, window}; use cosmic::widget::space::horizontal as horizontal_space; use cosmic::widget::{self, settings}; use cosmic::{Apply, Element, Task, surface}; use cosmic_config::{Config, ConfigGet, ConfigSet}; +use cosmic_settings_audio_client::{self as audio_client, CosmicAudioProxy}; use cosmic_settings_page::{self as page, Section, section}; -use cosmic_settings_sound_subscription as subscription; +use cosmic_settings_sound::model; +use futures::executor::block_on; use slotmap::SlotMap; const AUDIO_CONFIG: &str = "com.system76.CosmicAudio"; @@ -18,22 +24,18 @@ const AMPLIFICATION_SOURCE: &str = "amplification_source"; #[derive(Clone, Debug)] pub enum Message { - /// Reload the model - Reload, + /// Updates for the model. + Model(cosmic_settings_sound::Message), /// Change the default output. SetDefaultSink(usize), /// Change the default input output. SetDefaultSource(usize), - /// Set the profile of a sound device. - SetProfile(u32, u32), /// Change the balance of the active sink. - SetSinkBalance(u32), + SetSinkBalance(f32), /// Request to change the default output volume. SetSinkVolume(u32), /// Request to change the input volume. SetSourceVolume(u32), - /// Messages handled by the sound module in cosmic-settings-subscriptions - Subscription(subscription::Message), /// Surface Action Surface(surface::Action), /// Toggle the mute status of the output. @@ -58,16 +60,11 @@ impl From for crate::Message { } } -impl From for Message { - fn from(val: subscription::Message) -> Self { - Message::Subscription(val) - } -} - pub struct Page { entity: page::Entity, device_profiles: page::Entity, - pub(self) model: subscription::Model, + client: Option>>, + model: model::Model, sound_config: Option, amplification_sink: bool, amplification_source: bool, @@ -75,14 +72,17 @@ pub struct Page { impl Default for Page { fn default() -> Self { - let mut model = subscription::Model::default(); - model.unplugged_text = fl!("sound-device-port-unplugged"); - model.hd_audio_text = fl!("sound-hd-audio"); - model.usb_audio_text = fl!("sound-usb-audio"); Self { entity: page::Entity::default(), device_profiles: page::Entity::default(), - model, + client: None, + model: model::Model { + text: model::Text { + hd_audio: fl!("sound-hd-audio"), + usb_audio: fl!("sound-usb-audio"), + }, + ..model::Model::default() + }, sound_config: None, amplification_sink: false, amplification_source: false, @@ -129,12 +129,15 @@ impl page::Page for Page { self.entity = entity; } - fn subscription( - &self, - _core: &cosmic::Core, - ) -> cosmic::iced::Subscription { - cosmic::iced::Subscription::run(subscription::watch) - .map(|message| Message::Subscription(message).into()) + fn subscription(&self, _core: &cosmic::Core) -> iced::Subscription { + iced::Subscription::run(|| { + iced::stream::channel( + 1, + move |emitter: futures::channel::mpsc::Sender| async move { + cosmic_settings_sound::subscribe(emitter, |m| Message::Model(m).into()).await + }, + ) + }) } fn on_leave(&mut self) -> Task { @@ -164,50 +167,88 @@ impl Page { match message { Message::Surface(a) => return cosmic::task::message(crate::app::Message::Surface(a)), - Message::Subscription(message) => { - return self - .model - .update(message) - .map(|message| Message::Subscription(message).into()); + Message::Model(cosmic_settings_sound::Message::Subscription(message)) => { + self.model.update(message); } - Message::SetSinkBalance(balance) => { - return self - .model - .set_sink_balance(balance) - .map(|message| Message::Subscription(message).into()); + Message::Model(cosmic_settings_sound::Message::Client(client)) => { + if let Some(client) = Arc::into_inner(client) { + self.client = Some(Rc::new(RefCell::new(client))); + self.model = model::Model { + text: model::Text { + hd_audio: fl!("sound-hd-audio"), + usb_audio: fl!("sound-usb-audio"), + }, + ..model::Model::default() + }; + } } Message::SetDefaultSink(pos) => { - return self - .model - .set_default_sink(pos) - .map(|message| Message::Subscription(message).into()); + if let Some(&node_id) = self.model.sinks.id.get(pos) { + if let Some(client) = self.client.as_mut() { + block_on(async { + _ = client.borrow_mut().conn.set_default(node_id, true).await; + }); + } + } } Message::SetDefaultSource(pos) => { - return self - .model - .set_default_source(pos) - .map(|message| Message::Subscription(message).into()); + if let Some(&node_id) = self.model.sources.id.get(pos) { + if let Some(client) = self.client.as_mut() { + block_on(async { + _ = client.borrow_mut().conn.set_default(node_id, true).await; + }); + } + } } - Message::ToggleSinkMute => self.model.toggle_sink_mute(), + Message::ToggleSinkMute => { + if let Some(ref mut client) = self.client { + block_on(async { + _ = client.borrow_mut().conn.sink_mute_toggle().await; + }); + } + } - Message::ToggleSourceMute => self.model.toggle_source_mute(), + Message::ToggleSourceMute => { + if let Some(ref mut client) = self.client { + block_on(async { + _ = client.borrow_mut().conn.source_mute_toggle().await; + }); + } + } Message::SetSinkVolume(volume) => { - return self - .model - .set_sink_volume(volume) - .map(|message| Message::Subscription(message).into()); + if let Some(ref mut client) = self.client { + self.model.active_sink.volume = volume; + self.model.active_sink.volume_text = volume.to_string(); + block_on(async { + _ = client.borrow_mut().conn.set_sink_volume(volume).await; + }); + } } Message::SetSourceVolume(volume) => { - return self - .model - .set_source_volume(volume) - .map(|message| Message::Subscription(message).into()); + if let Some(ref mut client) = self.client { + self.model.active_source.volume = volume; + self.model.active_source.volume_text = volume.to_string(); + block_on(async { + _ = client.borrow_mut().conn.set_source_volume(volume).await; + }); + } + } + + Message::SetSinkBalance(balance) => { + if let Some((client, sink_id)) = self.client.as_ref().zip(self.model.default_sink) { + block_on(async { + _ = client + .borrow_mut() + .conn + .set_node_volume_balance(sink_id, Some(balance)); + }); + } } Message::ToggleOverAmplificationSink(enabled) => { @@ -229,18 +270,6 @@ impl Page { tracing::error!(?why, "Failed to save over amplification setting"); } } - - Message::SetProfile(object_id, index) => { - self.model.set_profile(object_id, index, true); - } - - Message::Reload => { - let mut model = subscription::Model::default(); - model.hd_audio_text = std::mem::take(&mut self.model.hd_audio_text); - model.unplugged_text = std::mem::take(&mut self.model.unplugged_text); - model.usb_audio_text = std::mem::take(&mut self.model.usb_audio_text); - self.model = model; - } } Task::none() @@ -260,17 +289,17 @@ fn input() -> Section { .title(fl!("sound-input")) .descriptions(descriptions) .view::(move |_binder, page, section| { - if page.model.sources().is_empty() { + if page.model.sources.id.is_empty() { return widget::space().into(); } let slider = if page.amplification_source { - widget::slider(0..=150, page.model.source_volume, |change| { + widget::slider(0..=150, page.model.active_source.volume, |change| { Message::SetSourceVolume(change).into() }) .breakpoints(&[100]) } else { - widget::slider(0..=100, page.model.source_volume, |change| { + widget::slider(0..=100, page.model.active_source.volume, |change| { Message::SetSourceVolume(change).into() }) } @@ -281,23 +310,25 @@ fn input() -> Section { let volume_control = widget::row::with_capacity(4) .align_y(Alignment::Center) .push( - widget::button::icon(widget::icon::from_name(if page.model.source_mute { - "microphone-sensitivity-muted-symbolic" - } else { - "audio-input-microphone-symbolic" - })) + widget::button::icon(widget::icon::from_name( + if page.model.active_source.mute { + "microphone-sensitivity-muted-symbolic" + } else { + "audio-input-microphone-symbolic" + }, + )) .on_press(Message::ToggleSourceMute.into()), ) .push( - widget::text::body(&page.model.source_volume_text) + widget::text::body(&page.model.active_source.volume_text) .width(Length::Fixed(22.0)) .align_x(Alignment::Center), ) .push(horizontal_space().width(8.)) .push(slider); let devices = widget::dropdown::popup_dropdown( - page.model.sources(), - Some(page.model.active_source().unwrap_or(0)), + &page.model.sources.display, + Some(page.model.sources.active.unwrap_or(0)), Message::SetDefaultSource, window::Id::RESERVED, Message::Surface, @@ -344,12 +375,12 @@ fn output() -> Section { .descriptions(descriptions) .view::(move |_binder, page, section| { let slider = if page.amplification_sink { - widget::slider(0..=150, page.model.sink_volume, |change| { + widget::slider(0..=150, page.model.active_sink.volume, |change| { Message::SetSinkVolume(change).into() }) .breakpoints(&[100]) } else { - widget::slider(0..=100, page.model.sink_volume, |change| { + widget::slider(0..=100, page.model.active_sink.volume, |change| { Message::SetSinkVolume(change).into() }) } @@ -360,7 +391,7 @@ fn output() -> Section { let volume_control = widget::row::with_capacity(4) .align_y(Alignment::Center) .push( - widget::button::icon(if page.model.sink_mute { + widget::button::icon(if page.model.active_sink.mute { widget::icon::from_name("audio-volume-muted-symbolic") } else { widget::icon::from_name("audio-volume-high-symbolic") @@ -368,7 +399,7 @@ fn output() -> Section { .on_press(Message::ToggleSinkMute.into()), ) .push( - widget::text::body(&page.model.sink_volume_text) + widget::text::body(&page.model.active_sink.volume_text) .width(Length::Fixed(22.0)) .align_x(Alignment::Center), ) @@ -376,8 +407,8 @@ fn output() -> Section { .push(slider); let devices = widget::dropdown::popup_dropdown( - page.model.sinks(), - Some(page.model.active_sink().unwrap_or(0)), + &page.model.sinks.display, + Some(page.model.sinks.active.unwrap_or(0)), Message::SetDefaultSink, window::Id::RESERVED, Message::Surface, @@ -410,12 +441,11 @@ fn output() -> Section { .push(horizontal_space().width(8.)) .push( widget::slider( - 0..=200, - (page.model.sink_balance.unwrap_or(1.0).max(0.) * 100.).round() - as u32, + 0.0..=1.0, + page.model.active_sink.balance.unwrap_or(0.5).min(1.), |change| Message::SetSinkBalance(change).into(), ) - .breakpoints(&[100]), + .breakpoints(&[0.5]), ) .push(horizontal_space().width(8.)) .push( diff --git a/crates/cosmic-pipewire/Cargo.toml b/crates/cosmic-pipewire/Cargo.toml deleted file mode 100644 index bcea9fbe0..000000000 --- a/crates/cosmic-pipewire/Cargo.toml +++ /dev/null @@ -1,21 +0,0 @@ -[package] -name = "cosmic-pipewire" -version = "1.0.7" -edition = "2024" -repository = "https://github.com/pop-os/cosmic-settings" -rust-version.workspace = true -license = "MPL-2.0" -publish = true - -[dependencies] -intmap = "3.1.3" -libspa = "0.9.2" -libspa-sys = "0.9.2" -pipewire = "0.9" -serde = { version = "1.0.228", features = ["derive"]} -serde_json = "1.0.149" -tracing = "0.1.44" - -[features] -# Cache route port types -route-port-type = [] diff --git a/crates/cosmic-pipewire/LICENSE.md b/crates/cosmic-pipewire/LICENSE.md deleted file mode 100644 index d141fe4c9..000000000 --- a/crates/cosmic-pipewire/LICENSE.md +++ /dev/null @@ -1,358 +0,0 @@ -Mozilla Public License Version 2.0 -================================== - -## 1. Definitions - -### 1.1. "Contributor" -means each individual or legal entity that creates, contributes to -the creation of, or owns Covered Software. - -### 1.2. "Contributor Version" -means the combination of the Contributions of others (if any) used -by a Contributor and that particular Contributor's Contribution. - -### 1.3. "Contribution" -means Covered Software of a particular Contributor. - -### 1.4. "Covered Software" -means Source Code Form to which the initial Contributor has attached -the notice in Exhibit A, the Executable Form of such Source Code -Form, and Modifications of such Source Code Form, in each case -including portions thereof. - -### 1.5. "Incompatible With Secondary Licenses" -means - -+ (a) that the initial Contributor has attached the notice described -in Exhibit B to the Covered Software; or - -+ (b) that the Covered Software was made available under the terms of -version 1.1 or earlier of the License, but not also under the -terms of a Secondary License. - -### 1.6. "Executable Form" -means any form of the work other than Source Code Form. - -### 1.7. "Larger Work" -means a work that combines Covered Software with other material, in -a separate file or files, that is not Covered Software. - -### 1.8. "License" -means this document. - -### 1.9. "Licensable" -means having the right to grant, to the maximum extent possible, -whether at the time of the initial grant or subsequently, any and -all of the rights conveyed by this License. - -### 1.10. "Modifications" -means any of the following: - -+ (a) any file in Source Code Form that results from an addition to, -deletion from, or modification of the contents of Covered -Software; or - -+ (b) any new file in Source Code Form that contains any Covered -Software. - -### 1.11. "Patent Claims" of a Contributor -means any patent claim(s), including without limitation, method, -process, and apparatus claims, in any patent Licensable by such -Contributor that would be infringed, but for the grant of the -License, by the making, using, selling, offering for sale, having -made, import, or transfer of either its Contributions or its -Contributor Version. - -### 1.12. "Secondary License" -means either the GNU General Public License, Version 2.0, the GNU -Lesser General Public License, Version 2.1, the GNU Affero General -Public License, Version 3.0, or any later versions of those -licenses. - -### 1.13. "Source Code Form" -means the form of the work preferred for making modifications. - -### 1.14. "You" (or "Your") -means an individual or a legal entity exercising rights under this -License. For legal entities, "You" includes any entity that -controls, is controlled by, or is under common control with You. For -purposes of this definition, "control" means (a) the power, direct -or indirect, to cause the direction or management of such entity, -whether by contract or otherwise, or (b) ownership of more than -fifty percent (50%) of the outstanding shares or beneficial -ownership of such entity. - -## 2. License Grants and Conditions - -### 2.1. Grants - -Each Contributor hereby grants You a world-wide, royalty-free, -non-exclusive license: - -+ (a) under intellectual property rights (other than patent or trademark) -Licensable by such Contributor to use, reproduce, make available, -modify, display, perform, distribute, and otherwise exploit its -Contributions, either on an unmodified basis, with Modifications, or -as part of a Larger Work; and - -+ (b) under Patent Claims of such Contributor to make, use, sell, offer -for sale, have made, import, and otherwise transfer either its -Contributions or its Contributor Version. - -### 2.2. Effective Date - -The licenses granted in Section 2.1 with respect to any Contribution -become effective for each Contribution on the date the Contributor first -distributes such Contribution. - -### 2.3. Limitations on Grant Scope - -The licenses granted in this Section 2 are the only rights granted under -this License. No additional rights or licenses will be implied from the -distribution or licensing of Covered Software under this License. -Notwithstanding Section 2.1(b) above, no patent license is granted by a -Contributor: - -+ (a) for any code that a Contributor has removed from Covered Software; -or - -+ (b) for infringements caused by: (i) Your and any other third party's -modifications of Covered Software, or (ii) the combination of its -Contributions with other software (except as part of its Contributor -Version); or - -+ (c) under Patent Claims infringed by Covered Software in the absence of -its Contributions. - -This License does not grant any rights in the trademarks, service marks, -or logos of any Contributor (except as may be necessary to comply with -the notice requirements in Section 3.4). - -### 2.4. Subsequent Licenses - -No Contributor makes additional grants as a result of Your choice to -distribute the Covered Software under a subsequent version of this -License (see Section 10.2) or under the terms of a Secondary License (if -permitted under the terms of Section 3.3). - -### 2.5. Representation - -Each Contributor represents that the Contributor believes its -Contributions are its original creation(s) or it has sufficient rights -to grant the rights to its Contributions conveyed by this License. - -### 2.6. Fair Use - -This License is not intended to limit any rights You have under -applicable copyright doctrines of fair use, fair dealing, or other -equivalents. - -### 2.7. Conditions - -Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted -in Section 2.1. - -## 3. Responsibilities - -### 3.1. Distribution of Source Form - -All distribution of Covered Software in Source Code Form, including any -Modifications that You create or to which You contribute, must be under -the terms of this License. You must inform recipients that the Source -Code Form of the Covered Software is governed by the terms of this -License, and how they can obtain a copy of this License. You may not -attempt to alter or restrict the recipients' rights in the Source Code -Form. - -### 3.2. Distribution of Executable Form - -If You distribute Covered Software in Executable Form then: - -+ (a) such Covered Software must also be made available in Source Code -Form, as described in Section 3.1, and You must inform recipients of -the Executable Form how they can obtain a copy of such Source Code -Form by reasonable means in a timely manner, at a charge no more -than the cost of distribution to the recipient; and - -+ (b) You may distribute such Executable Form under the terms of this -License, or sublicense it under different terms, provided that the -license for the Executable Form does not attempt to limit or alter -the recipients' rights in the Source Code Form under this License. - -### 3.3. Distribution of a Larger Work - -You may create and distribute a Larger Work under terms of Your choice, -provided that You also comply with the requirements of this License for -the Covered Software. If the Larger Work is a combination of Covered -Software with a work governed by one or more Secondary Licenses, and the -Covered Software is not Incompatible With Secondary Licenses, this -License permits You to additionally distribute such Covered Software -under the terms of such Secondary License(s), so that the recipient of -the Larger Work may, at their option, further distribute the Covered -Software under the terms of either this License or such Secondary -License(s). - -### 3.4. Notices - -You may not remove or alter the substance of any license notices -(including copyright notices, patent notices, disclaimers of warranty, -or limitations of liability) contained within the Source Code Form of -the Covered Software, except that You may alter any license notices to -the extent required to remedy known factual inaccuracies. - -### 3.5. Application of Additional Terms - -You may choose to offer, and to charge a fee for, warranty, support, -indemnity or liability obligations to one or more recipients of Covered -Software. However, You may do so only on Your own behalf, and not on -behalf of any Contributor. You must make it absolutely clear that any -such warranty, support, indemnity, or liability obligation is offered by -You alone, and You hereby agree to indemnify every Contributor for any -liability incurred by such Contributor as a result of warranty, support, -indemnity or liability terms You offer. You may include additional -disclaimers of warranty and limitations of liability specific to any -jurisdiction. - -## 4. Inability to Comply Due to Statute or Regulation - -If it is impossible for You to comply with any of the terms of this -License with respect to some or all of the Covered Software due to -statute, judicial order, or regulation then You must: (a) comply with -the terms of this License to the maximum extent possible; and (b) -describe the limitations and the code they affect. Such description must -be placed in a text file included with all distributions of the Covered -Software under this License. Except to the extent prohibited by statute -or regulation, such description must be sufficiently detailed for a -recipient of ordinary skill to be able to understand it. - -## 5. Termination - -5.1. The rights granted under this License will terminate automatically -if You fail to comply with any of its terms. However, if You become -compliant, then the rights granted under this License from a particular -Contributor are reinstated (a) provisionally, unless and until such -Contributor explicitly and finally terminates Your grants, and (b) on an -ongoing basis, if such Contributor fails to notify You of the -non-compliance by some reasonable means prior to 60 days after You have -come back into compliance. Moreover, Your grants from a particular -Contributor are reinstated on an ongoing basis if such Contributor -notifies You of the non-compliance by some reasonable means, this is the -first time You have received notice of non-compliance with this License -from such Contributor, and You become compliant prior to 30 days after -Your receipt of the notice. - -5.2. If You initiate litigation against any entity by asserting a patent -infringement claim (excluding declaratory judgment actions, -counter-claims, and cross-claims) alleging that a Contributor Version -directly or indirectly infringes any patent, then the rights granted to -You by any and all Contributors for the Covered Software under Section -2.1 of this License shall terminate. - -5.3. In the event of termination under Sections 5.1 or 5.2 above, all -end user license agreements (excluding distributors and resellers) which -have been validly granted by You or Your distributors under this License -prior to termination shall survive termination. - - -## 6. Disclaimer of Warranty - -**Covered Software is provided under this License on an "as is" -basis, without warranty of any kind, either expressed, implied, or -statutory, including, without limitation, warranties that the -Covered Software is free of defects, merchantable, fit for a -particular purpose or non-infringing. The entire risk as to the -quality and performance of the Covered Software is with You. -Should any Covered Software prove defective in any respect, You -(not any Contributor) assume the cost of any necessary servicing, -repair, or correction. This disclaimer of warranty constitutes an -essential part of this License. No use of any Covered Software is -authorized under this License except under this disclaimer.** - - -#7. Limitation of Liability - -**Under no circumstances and under no legal theory, whether tort -(including negligence), contract, or otherwise, shall any -Contributor, or anyone who distributes Covered Software as -permitted above, be liable to You for any direct, indirect, -special, incidental, or consequential damages of any character -including, without limitation, damages for lost profits, loss of -goodwill, work stoppage, computer failure or malfunction, or any -and all other commercial damages or losses, even if such party -shall have been informed of the possibility of such damages. This -limitation of liability shall not apply to liability for death or -personal injury resulting from such party's negligence to the -extent applicable law prohibits such limitation. Some -jurisdictions do not allow the exclusion or limitation of -incidental or consequential damages, so this exclusion and -limitation may not apply to You.** - - -## 8. Litigation - -Any litigation relating to this License may be brought only in the -courts of a jurisdiction where the defendant maintains its principal -place of business and such litigation shall be governed by laws of that -jurisdiction, without reference to its conflict-of-law provisions. -Nothing in this Section shall prevent a party's ability to bring -cross-claims or counter-claims. - -## 9. Miscellaneous - -This License represents the complete agreement concerning the subject -matter hereof. If any provision of this License is held to be -unenforceable, such provision shall be reformed only to the extent -necessary to make it enforceable. Any law or regulation which provides -that the language of a contract shall be construed against the drafter -shall not be used to construe this License against a Contributor. - -## 10. Versions of the License - -### 10.1. New Versions - -Mozilla Foundation is the license steward. Except as provided in Section -10.3, no one other than the license steward has the right to modify or -publish new versions of this License. Each version will be given a -distinguishing version number. - -### 10.2. Effect of New Versions - -You may distribute the Covered Software under the terms of the version -of the License under which You originally received the Covered Software, -or under the terms of any subsequent version published by the license -steward. - -### 10.3. Modified Versions - -If you create software not governed by this License, and you want to -create a new license for such software, you may create and use a -modified version of this License if you rename the license and remove -any references to the name of the license steward (except to note that -such modified license differs from this License). - -### 10.4. Distributing Source Code Form that is Incompatible With Secondary -Licenses - -If You choose to distribute Source Code Form that is Incompatible With -Secondary Licenses under the terms of this version of the License, the -notice described in Exhibit B of this License must be attached. - -## Exhibit A - Source Code Form License Notice - - - This Source Code Form is subject to the terms of the Mozilla Public - License, v. 2.0. If a copy of the MPL was not distributed with this - file, You can obtain one at http://mozilla.org/MPL/2.0/. - -If it is not possible or desirable to put the notice in a particular -file, then You may include the notice in a location (such as a LICENSE -file in a relevant directory) where a recipient would be likely to look -for such a notice. - -You may add additional accurate notices of copyright ownership. - -## Exhibit B - "Incompatible With Secondary Licenses" Notice - - - This Source Code Form is "Incompatible With Secondary Licenses", as - defined by the Mozilla Public License, v. 2.0. diff --git a/crates/cosmic-pipewire/src/device.rs b/crates/cosmic-pipewire/src/device.rs deleted file mode 100644 index a456fd511..000000000 --- a/crates/cosmic-pipewire/src/device.rs +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2025 System76 -// SPDX-License-Identifier: MPL-2.0 - -use pipewire::device::DeviceInfoRef; - -/// Device information -#[must_use] -#[derive(Clone, Debug)] -pub struct Device { - pub id: u32, - pub name: String, -} - -impl Device { - /// Attains process info from a pipewire info node. - #[must_use] - pub fn from_device(info: &DeviceInfoRef) -> Option { - let props = info.props()?; - - let device = Device { - id: props.get("object.id")?.parse::().ok()?, - name: props.get("device.description")?.to_owned(), - }; - - Some(device) - } -} diff --git a/crates/cosmic-pipewire/src/port.rs b/crates/cosmic-pipewire/src/port.rs deleted file mode 100644 index e2fc9577d..000000000 --- a/crates/cosmic-pipewire/src/port.rs +++ /dev/null @@ -1,98 +0,0 @@ -// Copyright 2025 System76 -// SPDX-License-Identifier: MPL-2.0 - -//! Currently unusued - -use crate::pipewire::Direction; -use pipewire::port::PortInfoRef; - -#[must_use] -#[derive(Clone, Debug)] -pub struct Port { - pub node_id: u32, - pub object_id: u32, - pub port_id: u32, - pub audio_channel: String, - pub format_dsp: String, - pub object_path: String, - pub port_direction: Direction, - pub port_group: String, - pub port_name: String, - pub port_alias: String, - pub port_physical: bool, - pub port_terminal: bool, - pub port_monitor: bool, -} - -impl Port { - /// Attains process info from a pipewire info port. - #[must_use] - pub fn from_port(info: &PortInfoRef) -> Option { - let props = info.props()?; - let object_id = info.id(); - let port_direction = match info.direction() { - libspa::utils::Direction::Input => Direction::Input, - libspa::utils::Direction::Output => Direction::Output, - _ => return None, - }; - - let mut node_id = 0; - let mut port_id = 0; - let mut port_monitor = false; - let mut port_physical = false; - let mut port_terminal = false; - - let mut audio_channel = String::new(); - let mut format_dsp = String::new(); - let mut object_path = String::new(); - let mut port_alias = String::new(); - let mut port_group = String::new(); - let mut port_name = String::new(); - - for (entry, value) in props.iter() { - match entry { - // 32 bit float mono audio - "format.dsp" => format_dsp = value.to_owned(), - // FR - "audio.channel" => audio_channel = value.to_owned(), - // playback - "port.group" => port_group = value.to_owned(), - // 1 - "port.id" => port_id = value.parse::().ok()?, - // false - "port.monitor" => port_monitor = value == "true", - // true - "port.physical" => port_physical = value == "true", - // true - "port.terminal" => port_terminal = value == "true", - // alsa:acp:Device:3:playback:playback_1 - "object.path" => object_path = value.to_owned(), - // playback_FR - "port.name" => port_name = value.to_owned(), - // MosArt USB Audio Device:playback_FR - "port.alias" => port_alias = value.to_owned(), - // 59 - "node.id" => node_id = value.parse::().ok()?, - _ => (), - } - } - - let port = Port { - format_dsp, - audio_channel, - port_id, - port_direction, - object_path, - port_name, - port_alias, - port_group, - port_monitor, - port_physical, - port_terminal, - node_id, - object_id, - }; - - Some(port) - } -} diff --git a/crates/cosmic-pipewire/src/spa_utils.rs b/crates/cosmic-pipewire/src/spa_utils.rs deleted file mode 100644 index 0593dfe1d..000000000 --- a/crates/cosmic-pipewire/src/spa_utils.rs +++ /dev/null @@ -1,150 +0,0 @@ -// Copyright 2025 System76 -// SPDX-License-Identifier: MPL-2.0 - -use libspa::pod::Pod; -use std::ffi::CStr; - -/// Read a `Pod`'s string if it contains a string. -pub fn string_from_pod(pod: &Pod) -> Option { - if !pod.is_string() { - return None; - } - - let mut cstr = std::ptr::null(); - - unsafe { - // SAFETY: Pod is checked to be a string beforehand - if libspa_sys::spa_pod_get_string(pod.as_raw_ptr(), &mut cstr) == 0 && !cstr.is_null() { - return Some(String::from_utf8_lossy(CStr::from_ptr(cstr).to_bytes()).into_owned()); - } - } - - None -} - -/// SAFETY: Must be absolutely certain that the array is a compatible array. -pub unsafe fn array_from_pod(pod: &Pod) -> Option> { - if !pod.is_array() { - return None; - } - - let mut len = 0; - - unsafe { - let array: *mut CType = libspa_sys::spa_pod_get_array(pod.as_raw_ptr(), &mut len).cast(); - - if array.is_null() { - return None; - } - - Some(std::slice::from_raw_parts(array, len as usize).to_vec()) - } -} - -#[repr(u32)] -#[derive(Copy, Clone, Debug, Default, Hash, Eq, PartialEq)] -pub enum Channel { - #[default] - UNKNOWN = 0, // unspecified - NA, // N/A, silent - MONO, // mono stream - FL, // front left - FR, // front right - FC, // front center - LFE, // LFE - SL, // side left - SR, // side right - FLC, // front left center - FRC, // front right center - RC, // rear center - RL, // rear left - RR, // rear right - TC, // top center - TFL, // top front left - TFC, // top front center - TFR, // top front right - TRL, // top rear left - TRC, // top rear center - TRR, // top rear right - RLC, // rear left center - RRC, // rear right center - FLW, // front left wide - FRW, // front right wide - LFE2, // LFE 2 - FLH, // front left high - FCH, // front center high - FRH, // front right high - TFLC, // top front left center - TFRC, // top front right center - TSL, // top side left - TSR, // top side right - LLFE, // left LFE - RLFE, // right LFE - BC, // bottom center - BLC, // bottom left center - BRC = 37, // bottom right center - AUX0 = 4096, // aux channels - AUX1, - AUX2, - AUX3, - AUX4, - AUX5, - AUX6, - AUX7, - AUX8, - AUX9, - AUX10, - AUX11, - AUX12, - AUX13, - AUX14, - AUX15, - AUX16, - AUX17, - AUX18, - AUX19, - AUX20, - AUX21, - AUX22, - AUX23, - AUX24, - AUX25, - AUX26, - AUX27, - AUX28, - AUX29, - AUX30, - AUX31, - AUX32, - AUX33, - AUX34, - AUX35, - AUX36, - AUX37, - AUX38, - AUX39, - AUX40, - AUX41, - AUX42, - AUX43, - AUX44, - AUX45, - AUX46, - AUX47, - AUX48, - AUX49, - AUX50, - AUX51, - AUX52, - AUX53, - AUX54, - AUX55, - AUX56, - AUX57, - AUX58, - AUX59, - AUX60, - AUX61, - AUX62, - AUX63 = 4159, -} diff --git a/debian/control b/debian/control index c04ab7863..644e98e55 100644 --- a/debian/control +++ b/debian/control @@ -12,11 +12,9 @@ Build-Depends: libfontconfig-dev, libfreetype-dev, libinput-dev, - libpipewire-0.3-dev, libudev-dev, libwayland-dev, libxkbcommon-dev, - mold, pkg-config, rust-all, Standards-Version: 4.6.2 @@ -29,6 +27,7 @@ Depends: ${shlibs:Depends}, accountsservice, cosmic-randr, + cosmic-settings-daemon, gettext, iso-codes, network-manager-gnome, diff --git a/pages/sound/Cargo.toml b/pages/sound/Cargo.toml new file mode 100644 index 000000000..916c2e682 --- /dev/null +++ b/pages/sound/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "cosmic-settings-sound" +version = "0.1.0" +edition = "2024" +rust-version.workspace = true + +[dependencies] +futures = "0.3.32" +intmap = "3.1.3" +tokio = { workspace = true, features = ["time"] } +tracing = "0.1.44" + +[dependencies.cosmic-settings-audio-client] +git = "https://github.com/pop-os/cosmic-settings-daemon" +branch = "varlink" +features = ["codec"] diff --git a/pages/sound/src/lib.rs b/pages/sound/src/lib.rs new file mode 100644 index 000000000..575ef4cf3 --- /dev/null +++ b/pages/sound/src/lib.rs @@ -0,0 +1,58 @@ +// Copyright 2026 System76 +// SPDX-License-Identifier: GPL-3.0-only + +pub mod model; +use std::sync::Arc; + +pub use cosmic_settings_audio_client as audio_client; +use futures::{SinkExt, StreamExt}; + +pub async fn subscribe( + mut emitter: futures::channel::mpsc::Sender, + apply_fn: fn(Message) -> T, +) { + loop { + let mut client = match audio_client::connect().await { + Ok(client) => client, + Err(why) => { + if let audio_client::zlink::Error::Io(ref why) = why + && why.kind() == std::io::ErrorKind::NotFound + { + tracing::error!("cosmic-settings-daemon varlink service not found."); + } else { + tracing::error!( + ?why, + "failed to connect to cosmic-settings's varlink service" + ); + } + + tokio::time::sleep(std::time::Duration::from_secs(3)).await; + continue; + } + }; + + if let Ok(Ok(mut stream)) = client.recv_events().await { + _ = emitter + .send(apply_fn(Message::Client(Arc::new(client)))) + .await; + while let Some(message) = stream.next().await { + match message { + Ok(event) => { + _ = emitter.send(apply_fn(Message::Subscription(event))).await; + } + Err(why) => { + tracing::error!(?why, "event error"); + } + } + } + } + } +} + +#[derive(Clone, Debug)] +pub enum Message { + /// Connection to `com.system76.CosmicSettings`. + Client(Arc), + /// Messages from the varlink audio client, + Subscription(audio_client::Event), +} diff --git a/pages/sound/src/model.rs b/pages/sound/src/model.rs new file mode 100644 index 000000000..d44ff1761 --- /dev/null +++ b/pages/sound/src/model.rs @@ -0,0 +1,453 @@ +// Copyright 2026 System76 +// SPDX-License-Identifier: GPL-3.0-only + +use cosmic_settings_audio_client::{self as audio_client, Availability, ProfileInfo, RouteInfo}; +use intmap::IntMap; + +pub type DeviceId = u32; +pub type NodeId = u32; + +#[derive(Debug, Default)] +pub struct Model { + #[allow(clippy::type_complexity)] + pub device_profile_dropdowns: Vec<(DeviceId, String, Option, Vec, Vec)>, + pub device_names: IntMap, + pub device_profiles: IntMap>, + pub device_profiles_active: IntMap, + pub device_routes: IntMap>, + pub node_devices: IntMap>, + pub sinks: Nodes, + pub sources: Nodes, + pub active_sink: ActiveNode, + pub active_source: ActiveNode, + pub default_sink: Option, + pub default_source: Option, + pub text: Text, +} + +#[derive(Debug, Default)] +pub struct Text { + pub hd_audio: String, + pub usb_audio: String, +} + +#[derive(Debug, Default)] +pub struct Nodes { + pub active: Option, + pub balance: Vec>, + pub card_profile_device: Vec>, + pub description: Vec, + pub devices: Vec>, + pub display: Vec, + pub mute: Vec, + pub name: Vec, + pub id: Vec, + pub volume: Vec, +} + +impl Nodes { + pub fn remove(&mut self, node_id: u32) -> bool { + let Some(pos) = self.id.iter().position(|id| node_id == *id) else { + return false; + }; + self.balance.remove(pos); + self.card_profile_device.remove(pos); + self.description.remove(pos); + self.devices.remove(pos); + self.display.remove(pos); + self.mute.remove(pos); + self.name.remove(pos); + self.id.remove(pos); + self.volume.remove(pos); + if self.active == Some(pos) { + self.active = None; + } + true + } +} + +#[derive(Debug, Default)] +pub struct ActiveNode { + pub volume_text: String, + pub volume: u32, + pub balance: Option, + pub mute: bool, +} + +impl Model { + pub fn update(&mut self, event: audio_client::Event) { + tracing::info!(target: "sound", ?event, "update"); + match event { + audio_client::Event::NodeMute(node_id, mute) => { + if let Some(pos) = self.sinks.id.iter().position(|id| node_id == *id) { + self.sinks.mute[pos] = mute; + if self.sinks.active == Some(pos) { + self.active_sink.mute = mute; + } + } else if let Some(pos) = self.sources.id.iter().position(|id| node_id == *id) { + self.sources.mute[pos] = mute; + if self.sources.active == Some(pos) { + self.active_source.mute = mute; + } + } + } + + audio_client::Event::NodeVolume(node_id, volume, balance) => { + if let Some(pos) = self.sinks.id.iter().position(|id| node_id == *id) { + self.sinks.volume[pos] = volume; + self.sinks.balance[pos] = balance; + if self.default_sink.as_ref().is_some_and(|&id| id == node_id) + && let Some(pos) = self.sinks.active + { + self.active_sink.mute = self.sinks.mute[pos]; + self.active_sink.balance = balance; + self.active_sink.volume = self.sinks.volume[pos]; + self.active_sink.volume_text = self.active_sink.volume.to_string(); + } + } else if let Some(pos) = self.sources.id.iter().position(|id| node_id == *id) { + self.sources.volume[pos] = volume; + self.sources.balance[pos] = balance; + if self + .default_source + .as_ref() + .is_some_and(|&id| id == node_id) + && let Some(pos) = self.sources.active + { + self.active_source.mute = self.sources.mute[pos]; + self.active_source.volume = self.sources.volume[pos]; + self.active_source.volume_text = self.active_source.volume.to_string(); + } + } + } + + audio_client::Event::DefaultSink(node_id) => { + self.default_sink = Some(node_id); + if let Some(pos) = self.sinks.id.iter().position(|&id| id == node_id) { + self.sinks.active = Some(pos); + self.active_sink.mute = self.sinks.mute[pos]; + self.active_sink.volume = self.sinks.volume[pos]; + self.active_sink.volume_text = self.active_sink.volume.to_string(); + } + } + + audio_client::Event::DefaultSource(node_id) => { + self.default_source = Some(node_id); + if let Some(pos) = self.sources.id.iter().position(|&id| id == node_id) { + self.sources.active = Some(pos); + self.active_source.mute = self.sources.mute[pos]; + self.active_source.volume = self.sources.volume[pos]; + self.active_source.volume_text = self.active_source.volume.to_string(); + } + } + + audio_client::Event::Device(device_id, device) => { + self.device_names + .insert(device_id, self.translate(&device.description)); + } + + audio_client::Event::Node(node_id, node) => { + self.node_devices.insert(node_id, node.device_id); + if node.is_sink { + let pos = if let Some(pos) = self.sinks.id.iter().position(|&id| id == node_id) + { + self.sinks.description[pos] = self.translate(&node.description); + self.sinks.name[pos] = node.name; + self.sinks.card_profile_device[pos] = node.card_profile_device; + pos + } else { + self.sinks.display.push(String::new()); + self.sinks + .description + .push(self.translate(&node.description)); + self.sinks.id.push(node_id); + self.sinks.volume.push(0); + self.sinks.balance.push(None); + self.sinks.mute.push(false); + self.sinks.name.push(node.name); + self.sinks.devices.push(node.device_id); + self.sinks + .card_profile_device + .push(node.card_profile_device); + self.sinks.id.len() - 1 + }; + + self.sinks.display[pos] = node + .device_id + .zip(node.card_profile_device) + .and_then(|(device_id, node_card_profile_device)| { + let routes = self.device_routes.get(device_id)?; + for route in routes { + if matches!(route.availability, Availability::No) || !route.is_sink + { + continue; + } + + if route.devices.contains(&node_card_profile_device) { + return Some( + [ + &*self.translate(&route.description), + " - ", + &self.sinks.description[pos], + ] + .concat(), + ); + } + } + + None + }) + .unwrap_or_else(|| { + [ + &node.device_profile_description, + " - ", + &*self.sinks.description[pos], + ] + .concat() + }); + + if let Some(default_node_id) = self.default_sink + && default_node_id == node_id + { + self.sinks.active = Some(pos); + self.active_sink.mute = self.sinks.mute[pos]; + self.active_sink.volume = self.sinks.volume[pos]; + self.active_sink.volume_text = self.active_sink.volume.to_string(); + } + } else { + let pos = + if let Some(pos) = self.sources.id.iter().position(|&id| id == node_id) { + self.sources.description[pos] = self.translate(&node.description); + self.sources.name[pos] = node.name; + self.sources.card_profile_device[pos] = node.card_profile_device; + pos + } else { + self.sources + .description + .push(self.translate(&node.description)); + self.sources.display.push(String::new()); + self.sources.id.push(node_id); + self.sources.volume.push(0); + self.sources.balance.push(None); + self.sources.mute.push(false); + self.sources.name.push(node.name); + self.sources.devices.push(node.device_id); + self.sources + .card_profile_device + .push(node.card_profile_device); + self.sources.id.len() - 1 + }; + + if let Some(name) = node.device_id.zip(node.card_profile_device).map_or_else( + || { + Some( + [ + &node.device_profile_description, + " - ", + &*self.sources.description[pos], + ] + .concat(), + ) + }, + |(device_id, node_card_profile_device)| { + let routes = self.device_routes.get(device_id)?; + for route in routes { + if route.is_sink || matches!(route.availability, Availability::No) { + continue; + } + + if route.devices.contains(&node_card_profile_device) { + return Some( + [ + &*self.translate(&route.description), + " - ", + &self.sources.description[pos], + ] + .concat(), + ); + } + } + + None + }, + ) { + self.sources.display[pos] = name; + } else { + // Remove sources that are unplugged. + self.sources.remove(node_id); + return; + } + + if let Some(default_node_id) = self.default_source + && default_node_id == node_id + { + self.sources.active = Some(pos); + self.active_source.mute = self.sources.mute[pos]; + self.active_source.volume = self.sources.volume[pos]; + self.active_source.volume_text = self.active_source.volume.to_string(); + } + } + } + + audio_client::Event::ActiveRoute(device_id, _index, route) => { + self.update_device_names(device_id, &route); + } + + audio_client::Event::Route(device_id, index, route) => { + let routes = self.device_routes.entry(device_id).or_default(); + if routes.len() < index as usize + 1 { + let additional = (index as usize + 1) - routes.capacity(); + routes.reserve_exact(additional); + routes.extend(std::iter::repeat_n(RouteInfo::default(), additional)); + } + routes[index as usize] = route.clone(); + // self.update_device_names(device_id, &route); + } + + audio_client::Event::ActiveProfile(device_id, profile) => { + self.device_profiles_active.insert(device_id, profile); + self.update_device_profile_dropdowns(); + } + + audio_client::Event::Profile(device_id, index, profile) => { + let profiles = self.device_profiles.entry(device_id).or_default(); + if profiles.len() < index as usize + 1 { + let additional = (index as usize + 1) - profiles.capacity(); + profiles.reserve_exact(additional); + profiles.extend(std::iter::repeat_n(ProfileInfo::default(), additional)); + } + + profiles[index as usize] = profile; + self.update_device_profile_dropdowns(); + } + + audio_client::Event::RemoveNode(node_id) => { + self.node_devices.remove(node_id); + + if !self.sinks.remove(node_id) { + self.sources.remove(node_id); + } + } + + audio_client::Event::RemoveDevice(device_id) => { + self.device_names.remove(device_id); + self.device_profiles.remove(device_id); + self.device_profiles_active.remove(device_id); + self.device_routes.remove(device_id); + self.update_device_profile_dropdowns(); + } + + _ => (), + } + } + + pub fn translate(&self, description: &str) -> String { + description + .replace("High Definition", "HD") + .replace("DisplayPort", "DP") + .replace("Controller", "") + .replace("HD Audio", &self.text.hd_audio) + .replace("USB Audio", &self.text.usb_audio) + } + + fn update_device_names(&mut self, device_id: DeviceId, route: &RouteInfo) { + if matches!(route.availability, Availability::No) { + return; + } + + let compatible_nodes = self.node_devices.iter().filter_map(|(node, &dev_id)| { + if dev_id? == device_id { + Some(node) + } else { + None + } + }); + + if route.is_sink { + for n_id in compatible_nodes { + let Some(pos) = self.sinks.id.iter().position(|&node| node == n_id) else { + continue; + }; + + let Some(card_profile_device) = self.sinks.card_profile_device[pos] else { + continue; + }; + + if route.devices.contains(&card_profile_device) { + self.sinks.display[pos] = + [&route.description, " - ", &self.sinks.description[pos]].concat(); + break; + } + } + } else { + for n_id in compatible_nodes { + let Some(pos) = self.sources.id.iter().position(|&node| node == n_id) else { + continue; + }; + + let Some(card_profile_device) = self.sources.card_profile_device[pos] else { + continue; + }; + + if route.devices.contains(&card_profile_device) { + self.sources.display[pos] = + [&route.description, " - ", &self.sources.description[pos]].concat(); + break; + } + } + } + } + + fn update_device_profile_dropdowns(&mut self) { + self.device_profile_dropdowns = self + .device_profiles + .iter() + .filter_map(|(device_id, profiles)| { + let name = self.device_names.get(device_id)?.as_str(); + let (active_profile, indexes, descriptions) = self + .device_profiles_active + .get(device_id) + .map(|profile| { + let (indexes, descriptions): (Vec<_>, Vec<_>) = profiles + .iter() + .filter(|p| { + p.index == profile.index + || !matches!(p.availability, audio_client::Availability::No) + }) + .map(|p| (p.index, p.description.clone())) + .collect(); + + let pos = profiles + .iter() + .filter(|p| { + p.index == profile.index + || !matches!(p.availability, audio_client::Availability::No) + }) + .enumerate() + .find(|(_, p)| p.index == profile.index) + .map(|(pos, _)| pos); + + (pos, indexes, descriptions) + }) + .unwrap_or_else(|| { + let (indexes, descriptions): (Vec<_>, Vec<_>) = profiles + .iter() + .filter(|p| !matches!(p.availability, audio_client::Availability::No)) + .map(|p| (p.index, p.description.clone())) + .collect(); + + (None, indexes, descriptions) + }); + + Some(( + device_id, + name.to_owned(), + active_profile, + indexes, + descriptions, + )) + }) + .collect::>(); + + self.device_profile_dropdowns.sort_by(|a, b| a.1.cmp(&b.1)); + } +} diff --git a/subscriptions/pulse/Cargo.toml b/subscriptions/pulse/Cargo.toml deleted file mode 100644 index cc4760bf2..000000000 --- a/subscriptions/pulse/Cargo.toml +++ /dev/null @@ -1,12 +0,0 @@ -[package] -name = "cosmic-settings-pulse-subscription" -version = "0.1.0" -edition = "2024" -rust-version.workspace = true - -[dependencies] -libpulse-binding = { version = "2.30.1" } -rustix = { version = "1.1.3", features = ["pipe"] } -iced_futures = { git = "https://github.com/pop-os/libcosmic" } -futures = "0.3.32" -log = "0.4.27" diff --git a/subscriptions/pulse/src/lib.rs b/subscriptions/pulse/src/lib.rs deleted file mode 100644 index b2a777909..000000000 --- a/subscriptions/pulse/src/lib.rs +++ /dev/null @@ -1,744 +0,0 @@ -// Copyright 2024 System76 -// SPDX-License-Identifier: MPL-2.0 - -// Make sure not to fail if pulse not found, and reconnect? -// change to device shouldn't send osd? - -use futures::SinkExt; -use futures::executor::block_on; -use iced_futures::{Subscription, stream}; -use libpulse_binding::callbacks::ListResult; -use libpulse_binding::channelmap::Map; -use libpulse_binding::context::introspect::{ - CardInfo, CardProfileInfo, Introspector, ServerInfo, SinkInfo, SourceInfo, -}; -use libpulse_binding::context::subscribe::{Facility, InterestMaskSet, Operation}; -use libpulse_binding::context::{Context, FlagSet, State}; -use libpulse_binding::def::{PortAvailable, Retval}; -use libpulse_binding::mainloop::api::MainloopApi; -use libpulse_binding::mainloop::events::io::IoEventInternal; -use libpulse_binding::mainloop::standard::{IterateResult, Mainloop}; -use libpulse_binding::volume::{ChannelVolumes, Volume}; -use std::borrow::Cow; -use std::cell::{Cell, RefCell}; -use std::convert::Infallible; -use std::io::{Read, Write}; -use std::os::fd::{FromRawFd, IntoRawFd, RawFd}; -use std::os::raw::c_void; -use std::rc::Rc; -use std::str::FromStr; -use std::sync::mpsc; - -pub fn subscription() -> iced_futures::Subscription { - Subscription::run_with("pulse", |_| { - stream::channel(20, |sender| async { - std::thread::spawn(move || thread(sender)); - futures::future::pending().await - }) - }) -} - -pub fn thread(sender: futures::channel::mpsc::Sender) { - let Some(mut main_loop) = Mainloop::new() else { - log::error!("Failed to create PA main loop"); - return; - }; - - let Some(mut context) = Context::new(&main_loop, "cosmic-osd") else { - log::error!("Failed to create PA context"); - return; - }; - - let data = Rc::new(Data { - main_loop: RefCell::new(Mainloop { - _inner: Rc::clone(&main_loop._inner), - }), - introspector: context.introspect(), - sink_volume: Cell::new(None), - sink_mute: Cell::new(None), - source_volume: Cell::new(None), - source_mute: Cell::new(None), - default_sink_name: RefCell::new(None), - default_source_name: RefCell::new(None), - sender: RefCell::new(sender.clone()), - }); - - let data_clone = data.clone(); - context.set_subscribe_callback(Some(Box::new(move |facility, operation, index| { - data_clone.subscribe_cb(facility.unwrap(), operation, index); - }))); - - let _ = context.connect(None, FlagSet::NOFAIL, None); - - loop { - if sender.is_closed() { - return; - } - - match main_loop.iterate(false) { - IterateResult::Success(_) => {} - IterateResult::Err(_e) => { - return; - } - IterateResult::Quit(_e) => { - return; - } - } - - if context.get_state() == State::Ready { - break; - } - } - - // Inspect all available cards on startup - data.introspector.get_card_info_list({ - let data_weak = Rc::downgrade(&data); - move |card_info_res| { - if let Some(data) = data_weak.upgrade() { - data.card_info_cb(card_info_res) - } - } - }); - - data.get_server_info(); - context.subscribe( - InterestMaskSet::SERVER | InterestMaskSet::SINK | InterestMaskSet::SOURCE, - |_| {}, - ); - - if let Err((err, retval)) = main_loop.run() { - log::error!("PA main loop returned {:?}, error {}", retval, err); - } -} - -#[derive(Clone, Debug)] -pub enum Event { - Balance(Option), - CardInfo(Card), - DefaultSink(String), - DefaultSource(String), - SinkVolume(u32), - Channels(PulseChannels), - SinkMute(bool), - SourceVolume(u32), - SourceMute(bool), -} - -enum Request { - Volume(u32, f32), - Balance(u32, f32), - Quit, -} - -#[derive(Debug)] -pub struct PulseChannels { - tx: mpsc::Sender, - pipe_tx: std::fs::File, - index: u32, -} - -impl Clone for PulseChannels { - fn clone(&self) -> Self { - Self { - tx: self.tx.clone(), - pipe_tx: self - .pipe_tx - .try_clone() - .expect("failed to clone PulseChannels pipe writer"), - index: self.index, - } - } -} - -/// Data used by the [`handle_balance_io_new`] callback. -struct HandleBalanceData( - Context, - ChannelVolumes, - Map, - std::sync::mpsc::Receiver, -); - -/// Callback for creating an IO event source [`MainloopApi::io_new`]. -extern "C" fn handle_balance_io_new( - api: *const MainloopApi, - event: *mut IoEventInternal, - reader_fd: RawFd, - _flags: libpulse_binding::mainloop::events::io::FlagSet, - data: *mut c_void, -) { - // Take ownership of the data and borrow its contents. - let mut data = unsafe { Box::::from_raw(data as _) }; - let HandleBalanceData(ctx, volumes, map, rx) = data.as_mut(); - - // Return early if the context is not ready, and give the data back. - if ctx.get_state() != State::Ready { - let _ = Box::leak(data); - return; - } - - // If the first byte cannot be read, destroy this event source with its reader and data. - let mut buf = [0u8; 1]; - let mut reader = unsafe { std::fs::File::from_raw_fd(reader_fd) }; - if reader.read_exact(&mut buf).is_err() { - (unsafe { &*api }) - .io_free - .as_ref() - .expect("io_free function is missing")(event); - return; - } - - // Give ownership of the reader back. - _ = reader.into_raw_fd(); - - while let Ok(req) = rx.try_recv() { - match req { - Request::Volume(index, volume_scale) => { - let mut intro = ctx.introspect(); - - let new_scale = Volume((volume_scale * Volume::NORMAL.0 as f32).round() as u32); - - if let Some(v) = volumes.scale(new_scale) { - _ = intro.set_sink_volume_by_index( - index, - v, - Some(Box::new(|success| { - if !success { - log::error!("Failed to set sink balance"); - } - })), - ); - } - } - Request::Balance(index, new_balance) => { - if map.can_balance() { - if let Some(v) = volumes.set_balance(&map, new_balance) { - let mut intro = ctx.introspect(); - - _ = intro.set_sink_volume_by_index( - index, - v, - Some(Box::new(|success| { - if !success { - log::error!("Failed to set sink balance"); - } - })), - ); - } - } - } - Request::Quit => unsafe { &*api } - .quit - .as_ref() - .expect("quit function missing")(api, 0), - } - } - - let _ = Box::leak(data); -} - -impl PulseChannels { - fn new( - volumes: ChannelVolumes, - map: Map, - api: &MainloopApi, - index: u32, - ctx: Context, - ) -> PulseChannels { - let (reader, writer) = rustix::pipe::pipe_with(rustix::pipe::PipeFlags::CLOEXEC) - .expect("failed to crate pipe"); - - let (tx, rx) = mpsc::channel::(); - - // Create IO event source object for handling speaker balance. - let event_source = api.io_new.as_ref().unwrap()( - api as *const _, - reader.into_raw_fd(), - libpulse_binding::mainloop::events::io::FlagSet::INPUT, - Some(handle_balance_io_new), - Box::into_raw(Box::new(HandleBalanceData(ctx, volumes, map, rx))) as *mut c_void, - ); - - if let Some(enable) = api.io_enable.as_ref() { - enable( - event_source, - libpulse_binding::mainloop::events::io::FlagSet::INPUT, - ); - } - - Self { - tx, - pipe_tx: std::fs::File::from(writer), - index, - } - } - - /// Change the active index. - #[inline] - pub fn set_index(&mut self, index: u32) { - self.index = index; - } - - /// Set the speaker balance of the active sink. - pub fn set_balance(&mut self, balance: f32) { - if let Err(err) = self.tx.send(Request::Balance(self.index, balance)) { - log::error!("Failed to send new balance to channel"); - } else { - self.pipe_tx - .write_all(&[1]) - .expect("PulseChannels pipe write failed"); - } - } - - /// Set the volume of the active sink. - pub fn set_volume(&mut self, volume: f32) { - if let Err(err) = self.tx.send(Request::Volume(self.index, volume)) { - log::error!("Failed to send new volume to channel"); - } else { - self.pipe_tx - .write_all(&[1]) - .expect("PulseChannels pipe write failed"); - } - } - - /// Request the pulse thread to quit. - pub fn quit(mut self) { - _ = self.tx.send(Request::Quit); - _ = self.pipe_tx.write_all(&[1]); - } -} - -#[derive(Clone, Debug, Hash, Eq, PartialEq)] -pub struct Card { - pub object_id: u32, - pub name: String, - pub product_name: String, - pub variant: DeviceVariant, - pub ports: Vec, - pub profiles: Vec, - pub active_profile: Option, -} - -#[derive(Clone, Debug, Hash, Eq, PartialEq)] -pub struct CardPort { - pub name: String, - pub description: String, - pub direction: Direction, - pub port_type: PortType, - pub profile_port: u32, - pub priority: u32, - pub profiles: Vec, - pub availability: Availability, -} - -#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)] -pub enum Availability { - Unknown, - No, - Yes, -} - -impl From for Availability { - fn from(pa: PortAvailable) -> Self { - match pa { - PortAvailable::Unknown => Availability::Unknown, - PortAvailable::No => Availability::No, - PortAvailable::Yes => Availability::Yes, - } - } -} - -#[derive(Clone, Debug, Hash, Eq, PartialEq)] -pub struct CardProfile { - pub name: String, - pub description: String, - pub available: bool, - pub n_sinks: u32, - pub n_sources: u32, - pub priority: u32, -} - -#[derive(Clone, Debug, Hash, Eq, PartialEq)] -pub enum DeviceVariant { - Alsa { alsa_card: u32 }, - Bluez5 { address: String }, -} - -#[derive(Clone, Debug, Hash, Eq, PartialEq)] -pub enum Direction { - Input, - Output, - Both, -} - -#[derive(Default, Clone, Debug, Hash, Eq, PartialEq)] -pub enum PortType { - Mic, - Speaker, - Headphones, - Headset, - Digital, - #[default] - Unknown, -} - -impl FromStr for PortType { - type Err = Infallible; - - fn from_str(s: &str) -> Result { - match s { - "mic" => Ok(PortType::Mic), - "speaker" => Ok(PortType::Speaker), - "headphones" => Ok(PortType::Headphones), - "headset" => Ok(PortType::Headset), - "digital" => Ok(PortType::Digital), - _ => Ok(PortType::Unknown), - } - } -} - -struct Data { - main_loop: RefCell, - default_sink_name: RefCell>, - default_source_name: RefCell>, - sink_volume: Cell>, - sink_mute: Cell>, - source_volume: Cell>, - source_mute: Cell>, - introspector: Introspector, - sender: RefCell>, -} - -impl Data { - fn card_info_cb(self: &Rc, card_info: ListResult<&CardInfo>) { - if let ListResult::Item(card_info) = card_info { - let Some(object_id) = card_info - .proplist - .get_str("object.id") - .and_then(|v| v.parse::().ok()) - else { - return; - }; - - let variant = if let Some(alsa_card) = card_info - .proplist - .get_str("alsa.card") - .and_then(|v| v.parse::().ok()) - { - DeviceVariant::Alsa { alsa_card } - } else if let Some(address) = card_info.proplist.get_str("api.bluez5.address") { - DeviceVariant::Bluez5 { address } - } else { - return; - }; - - let card = Card { - name: card_info - .name - .as_ref() - .map(Cow::to_string) - .unwrap_or_default(), - product_name: card_info - .proplist - .get_str("device.product.name") - .unwrap_or_default(), - object_id, - variant, - ports: card_info - .ports - .iter() - .map(|port| CardPort { - name: port.name.as_ref().map(Cow::to_string).unwrap_or_default(), - description: port - .description - .as_ref() - .map(Cow::to_string) - .unwrap_or_default(), - direction: match port.direction.bits() { - x if x == libpulse_binding::direction::FlagSet::INPUT.bits() => { - Direction::Input - } - x if x == libpulse_binding::direction::FlagSet::OUTPUT.bits() => { - Direction::Output - } - _ => Direction::Both, - }, - port_type: port - .proplist - .get_str("port.type") - .as_deref() - .map(|s| PortType::from_str(s).unwrap()) - .unwrap_or_default(), - profile_port: port - .proplist - .get_str("card.profile.port") - .and_then(|v| v.parse::().ok()) - .unwrap_or(0), - priority: port.priority, - profiles: collect_profiles(&port.profiles), - availability: port.available.into(), - }) - .collect(), - profiles: collect_profiles(&card_info.profiles), - active_profile: card_info.active_profile.as_deref().map(CardProfile::from), - }; - - if block_on(self.sender.borrow_mut().send(Event::CardInfo(card))).is_err() { - self.main_loop.borrow_mut().quit(Retval(0)); - } - } - } - - fn server_info_cb(self: &Rc, server_info: &ServerInfo) { - let new_default_sink_name = server_info - .default_sink_name - .as_ref() - .map(|x| x.clone().into_owned()); - let mut default_sink_name = self.default_sink_name.borrow_mut(); - if new_default_sink_name != *default_sink_name { - if let Some(name) = &new_default_sink_name { - _ = block_on( - self.sender - .borrow_mut() - .send(Event::DefaultSink(name.clone())), - ); - self.get_sink_info_by_name(name); - } - *default_sink_name = new_default_sink_name; - } - - let new_default_source_name = server_info - .default_source_name - .as_ref() - .map(|x| x.clone().into_owned()); - let mut default_source_name = self.default_source_name.borrow_mut(); - if new_default_source_name != *default_source_name { - if let Some(name) = &new_default_source_name { - _ = block_on( - self.sender - .borrow_mut() - .send(Event::DefaultSource(name.clone())), - ); - self.get_source_info_by_name(name); - } - *default_source_name = new_default_source_name; - } - } - - fn get_server_info(self: &Rc) { - let data = self.clone(); - self.introspector - .get_server_info(move |server_info| data.server_info_cb(server_info)); - } - - fn sink_info_cb(&self, sink_info_res: ListResult<&SinkInfo>) { - if let ListResult::Item(sink_info) = sink_info_res { - if sink_info.name.as_deref() != self.default_sink_name.borrow().as_deref() { - return; - } - let balance = (sink_info.channel_map.can_balance() - && sink_info.base_volume.is_normal()) - .then(|| sink_info.volume.get_balance(&sink_info.channel_map)); - - let volume = sink_info.volume.max().0 / (Volume::NORMAL.0 / 100); - if self.sink_mute.get() != Some(sink_info.mute) { - self.sink_mute.set(Some(sink_info.mute)); - if block_on( - self.sender - .borrow_mut() - .send(Event::SinkMute(sink_info.mute)), - ) - .is_err() - { - self.main_loop.borrow_mut().quit(Retval(0)); - } - } - if self.sink_volume.get() != Some(volume) { - self.sink_volume.set(Some(volume)); - if block_on(self.sender.borrow_mut().send(Event::SinkVolume(volume))).is_err() { - self.main_loop.borrow_mut().quit(Retval(0)); - } - } - if block_on(self.sender.borrow_mut().send(Event::Balance(balance))).is_err() { - self.main_loop.borrow_mut().quit(Retval(0)); - } - let mut main_loop = self.main_loop.borrow_mut(); - let api = main_loop.get_api(); - if let Some(mut ctx) = Context::new(&*main_loop, "balance") { - let _ = ctx.connect(None, FlagSet::NOFAIL, None); - - let channels = PulseChannels::new( - sink_info.volume, - sink_info.channel_map, - api, - sink_info.index, - ctx, - ); - - if block_on(self.sender.borrow_mut().send(Event::Channels(channels))).is_err() { - main_loop.quit(Retval(0)); - } - } - } - } - - fn source_info_cb(&self, source_info_res: ListResult<&SourceInfo>) { - if let ListResult::Item(source_info) = source_info_res { - if source_info.name.as_deref() != self.default_source_name.borrow().as_deref() { - return; - } - let volume = source_info.volume.max().0 / (Volume::NORMAL.0 / 100); - if self.source_mute.get() != Some(source_info.mute) { - self.source_mute.set(Some(source_info.mute)); - if block_on( - self.sender - .borrow_mut() - .send(Event::SourceMute(source_info.mute)), - ) - .is_err() - { - self.main_loop.borrow_mut().quit(Retval(0)); - } - } - if self.source_volume.get() != Some(volume) { - self.source_volume.set(Some(volume)); - if block_on(self.sender.borrow_mut().send(Event::SourceVolume(volume))).is_err() { - self.main_loop.borrow_mut().quit(Retval(0)); - } - } - } - } - - fn get_card_info_by_index(self: &Rc, index: u32) { - let data = self.clone(); - self.introspector - .get_card_info_by_index(index, move |card_info_res| { - data.card_info_cb(card_info_res); - }); - } - - fn get_sink_info_by_index(self: &Rc, index: u32) { - let data = self.clone(); - self.introspector.get_sink_info_by_index( - index, - move |sink_info_res: ListResult<&SinkInfo<'_>>| { - if let ListResult::Item(ref info) = sink_info_res { - if let Some(card_index) = info.card { - let data_clone = data.clone(); - data.introspector.get_card_info_by_index( - card_index, - move |card_info_res| { - data_clone.card_info_cb(card_info_res); - }, - ); - } - } - data.sink_info_cb(sink_info_res); - }, - ); - } - - fn get_sink_info_by_name(self: &Rc, name: &str) { - let data = self.clone(); - self.introspector - .get_sink_info_by_name(name, move |sink_info_res| { - if let ListResult::Item(ref info) = sink_info_res { - if let Some(card_index) = info.card { - let data_clone = data.clone(); - data.introspector.get_card_info_by_index( - card_index, - move |card_info_res| { - data_clone.card_info_cb(card_info_res); - }, - ); - } - } - data.sink_info_cb(sink_info_res); - }); - } - - fn get_source_info_by_index(self: &Rc, index: u32) { - let data = self.clone(); - self.introspector - .get_source_info_by_index(index, move |source_info_res| { - if let ListResult::Item(ref info) = source_info_res { - if let Some(card_index) = info.card { - let data_clone = data.clone(); - data.introspector.get_card_info_by_index( - card_index, - move |card_info_res| { - data_clone.card_info_cb(card_info_res); - }, - ); - } - } - data.source_info_cb(source_info_res); - }); - } - - fn get_source_info_by_name(self: &Rc, name: &str) { - let data = self.clone(); - self.introspector - .get_source_info_by_name(name, move |source_info_res| { - if let ListResult::Item(ref info) = source_info_res { - if let Some(card_index) = info.card { - let data_clone = data.clone(); - data.introspector.get_card_info_by_index( - card_index, - move |card_info_res| { - data_clone.card_info_cb(card_info_res); - }, - ); - } - } - data.source_info_cb(source_info_res); - }); - } - - fn subscribe_cb( - self: &Rc, - facility: Facility, - _operation: Option, - index: u32, - ) { - match facility { - Facility::Server => { - self.get_server_info(); - } - Facility::Sink => { - self.get_sink_info_by_index(index); - } - Facility::Source => { - self.get_source_info_by_index(index); - } - Facility::Card => { - self.get_card_info_by_index(index); - } - _ => {} - } - } -} - -fn collect_profiles(profiles: &[CardProfileInfo]) -> Vec { - profiles.iter().map(CardProfile::from).collect() -} - -impl From<&CardProfileInfo<'_>> for CardProfile { - fn from(profile: &CardProfileInfo) -> Self { - CardProfile { - name: profile - .name - .as_ref() - .map(Cow::to_string) - .unwrap_or_default(), - description: profile - .description - .as_ref() - .map(Cow::to_string) - .unwrap_or_default(), - available: profile.available, - n_sinks: profile.n_sinks, - n_sources: profile.n_sources, - priority: profile.priority, - } - } -} diff --git a/subscriptions/sound/Cargo.toml b/subscriptions/sound/Cargo.toml deleted file mode 100644 index adf589142..000000000 --- a/subscriptions/sound/Cargo.toml +++ /dev/null @@ -1,21 +0,0 @@ -[package] -name = "cosmic-settings-sound-subscription" -version = "1.0.7" -edition = "2024" -rust-version.workspace = true -license = "MPL-2.0" -publish = true - -[dependencies] -cosmic-pipewire = { path = "../../crates/cosmic-pipewire" } -futures = "0.3.32" -intmap = "3.1.3" -libcosmic = { git = "https://github.com/pop-os/libcosmic", default-features = false } -numtoa = "1.0.0-alpha1" -rustix = "1.1.3" -tokio = { version = "1.49.0", features = ["process", "rt", "time"] } -tracing = { version = "0.1.44", default-features = false } - -[features] -# Set profile on first load -auto-profile-init = [] diff --git a/subscriptions/sound/LICENSE.md b/subscriptions/sound/LICENSE.md deleted file mode 100644 index 8dc5b15d9..000000000 --- a/subscriptions/sound/LICENSE.md +++ /dev/null @@ -1,359 +0,0 @@ -Mozilla Public License Version 2.0 -================================== - -## 1. Definitions - -### 1.1. "Contributor" -means each individual or legal entity that creates, contributes to -the creation of, or owns Covered Software. - -### 1.2. "Contributor Version" -means the combination of the Contributions of others (if any) used -by a Contributor and that particular Contributor's Contribution. - -### 1.3. "Contribution" -means Covered Software of a particular Contributor. - -### 1.4. "Covered Software" -means Source Code Form to which the initial Contributor has attached -the notice in Exhibit A, the Executable Form of such Source Code -Form, and Modifications of such Source Code Form, in each case -including portions thereof. - -### 1.5. "Incompatible With Secondary Licenses" -means - -+ (a) that the initial Contributor has attached the notice described -in Exhibit B to the Covered Software; or - -+ (b) that the Covered Software was made available under the terms of -version 1.1 or earlier of the License, but not also under the -terms of a Secondary License. - -### 1.6. "Executable Form" -means any form of the work other than Source Code Form. - -### 1.7. "Larger Work" -means a work that combines Covered Software with other material, in -a separate file or files, that is not Covered Software. - -### 1.8. "License" -means this document. - -### 1.9. "Licensable" -means having the right to grant, to the maximum extent possible, -whether at the time of the initial grant or subsequently, any and -all of the rights conveyed by this License. - -### 1.10. "Modifications" -means any of the following: - -+ (a) any file in Source Code Form that results from an addition to, -deletion from, or modification of the contents of Covered -Software; or - -+ (b) any new file in Source Code Form that contains any Covered -Software. - -### 1.11. "Patent Claims" of a Contributor -means any patent claim(s), including without limitation, method, -process, and apparatus claims, in any patent Licensable by such -Contributor that would be infringed, but for the grant of the -License, by the making, using, selling, offering for sale, having -made, import, or transfer of either its Contributions or its -Contributor Version. - -### 1.12. "Secondary License" -means either the GNU General Public License, Version 2.0, the GNU -Lesser General Public License, Version 2.1, the GNU Affero General -Public License, Version 3.0, or any later versions of those -licenses. - -### 1.13. "Source Code Form" -means the form of the work preferred for making modifications. - -### 1.14. "You" (or "Your") -means an individual or a legal entity exercising rights under this -License. For legal entities, "You" includes any entity that -controls, is controlled by, or is under common control with You. For -purposes of this definition, "control" means (a) the power, direct -or indirect, to cause the direction or management of such entity, -whether by contract or otherwise, or (b) ownership of more than -fifty percent (50%) of the outstanding shares or beneficial -ownership of such entity. - -## 2. License Grants and Conditions - -### 2.1. Grants - -Each Contributor hereby grants You a world-wide, royalty-free, -non-exclusive license: - -+ (a) under intellectual property rights (other than patent or trademark) -Licensable by such Contributor to use, reproduce, make available, -modify, display, perform, distribute, and otherwise exploit its -Contributions, either on an unmodified basis, with Modifications, or -as part of a Larger Work; and - -+ (b) under Patent Claims of such Contributor to make, use, sell, offer -for sale, have made, import, and otherwise transfer either its -Contributions or its Contributor Version. - -### 2.2. Effective Date - -The licenses granted in Section 2.1 with respect to any Contribution -become effective for each Contribution on the date the Contributor first -distributes such Contribution. - -### 2.3. Limitations on Grant Scope - -The licenses granted in this Section 2 are the only rights granted under -this License. No additional rights or licenses will be implied from the -distribution or licensing of Covered Software under this License. -Notwithstanding Section 2.1(b) above, no patent license is granted by a -Contributor: - -+ (a) for any code that a Contributor has removed from Covered Software; -or - -+ (b) for infringements caused by: (i) Your and any other third party's -modifications of Covered Software, or (ii) the combination of its -Contributions with other software (except as part of its Contributor -Version); or - -+ (c) under Patent Claims infringed by Covered Software in the absence of -its Contributions. - -This License does not grant any rights in the trademarks, service marks, -or logos of any Contributor (except as may be necessary to comply with -the notice requirements in Section 3.4). - -### 2.4. Subsequent Licenses - -No Contributor makes additional grants as a result of Your choice to -distribute the Covered Software under a subsequent version of this -License (see Section 10.2) or under the terms of a Secondary License (if -permitted under the terms of Section 3.3). - -### 2.5. Representation - -Each Contributor represents that the Contributor believes its -Contributions are its original creation(s) or it has sufficient rights -to grant the rights to its Contributions conveyed by this License. - -### 2.6. Fair Use - -This License is not intended to limit any rights You have under -applicable copyright doctrines of fair use, fair dealing, or other -equivalents. - -### 2.7. Conditions - -Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted -in Section 2.1. - -## 3. Responsibilities - -### 3.1. Distribution of Source Form - -All distribution of Covered Software in Source Code Form, including any -Modifications that You create or to which You contribute, must be under -the terms of this License. You must inform recipients that the Source -Code Form of the Covered Software is governed by the terms of this -License, and how they can obtain a copy of this License. You may not -attempt to alter or restrict the recipients' rights in the Source Code -Form. - -### 3.2. Distribution of Executable Form - -If You distribute Covered Software in Executable Form then: - -+ (a) such Covered Software must also be made available in Source Code -Form, as described in Section 3.1, and You must inform recipients of -the Executable Form how they can obtain a copy of such Source Code -Form by reasonable means in a timely manner, at a charge no more -than the cost of distribution to the recipient; and - -+ (b) You may distribute such Executable Form under the terms of this -License, or sublicense it under different terms, provided that the -license for the Executable Form does not attempt to limit or alter -the recipients' rights in the Source Code Form under this License. - -### 3.3. Distribution of a Larger Work - -You may create and distribute a Larger Work under terms of Your choice, -provided that You also comply with the requirements of this License for -the Covered Software. If the Larger Work is a combination of Covered -Software with a work governed by one or more Secondary Licenses, and the -Covered Software is not Incompatible With Secondary Licenses, this -License permits You to additionally distribute such Covered Software -under the terms of such Secondary License(s), so that the recipient of -the Larger Work may, at their option, further distribute the Covered -Software under the terms of either this License or such Secondary -License(s). - -### 3.4. Notices - -You may not remove or alter the substance of any license notices -(including copyright notices, patent notices, disclaimers of warranty, -or limitations of liability) contained within the Source Code Form of -the Covered Software, except that You may alter any license notices to -the extent required to remedy known factual inaccuracies. - -### 3.5. Application of Additional Terms - -You may choose to offer, and to charge a fee for, warranty, support, -indemnity or liability obligations to one or more recipients of Covered -Software. However, You may do so only on Your own behalf, and not on -behalf of any Contributor. You must make it absolutely clear that any -such warranty, support, indemnity, or liability obligation is offered by -You alone, and You hereby agree to indemnify every Contributor for any -liability incurred by such Contributor as a result of warranty, support, -indemnity or liability terms You offer. You may include additional -disclaimers of warranty and limitations of liability specific to any -jurisdiction. - -## 4. Inability to Comply Due to Statute or Regulation - -If it is impossible for You to comply with any of the terms of this -License with respect to some or all of the Covered Software due to -statute, judicial order, or regulation then You must: (a) comply with -the terms of this License to the maximum extent possible; and (b) -describe the limitations and the code they affect. Such description must -be placed in a text file included with all distributions of the Covered -Software under this License. Except to the extent prohibited by statute -or regulation, such description must be sufficiently detailed for a -recipient of ordinary skill to be able to understand it. - -## 5. Termination - -5.1. The rights granted under this License will terminate automatically -if You fail to comply with any of its terms. However, if You become -compliant, then the rights granted under this License from a particular -Contributor are reinstated (a) provisionally, unless and until such -Contributor explicitly and finally terminates Your grants, and (b) on an -ongoing basis, if such Contributor fails to notify You of the -non-compliance by some reasonable means prior to 60 days after You have -come back into compliance. Moreover, Your grants from a particular -Contributor are reinstated on an ongoing basis if such Contributor -notifies You of the non-compliance by some reasonable means, this is the -first time You have received notice of non-compliance with this License -from such Contributor, and You become compliant prior to 30 days after -Your receipt of the notice. - -5.2. If You initiate litigation against any entity by asserting a patent -infringement claim (excluding declaratory judgment actions, -counter-claims, and cross-claims) alleging that a Contributor Version -directly or indirectly infringes any patent, then the rights granted to -You by any and all Contributors for the Covered Software under Section -2.1 of this License shall terminate. - -5.3. In the event of termination under Sections 5.1 or 5.2 above, all -end user license agreements (excluding distributors and resellers) which -have been validly granted by You or Your distributors under this License -prior to termination shall survive termination. - - -## 6. Disclaimer of Warranty - -**Covered Software is provided under this License on an "as is" -basis, without warranty of any kind, either expressed, implied, or -statutory, including, without limitation, warranties that the -Covered Software is free of defects, merchantable, fit for a -particular purpose or non-infringing. The entire risk as to the -quality and performance of the Covered Software is with You. -Should any Covered Software prove defective in any respect, You -(not any Contributor) assume the cost of any necessary servicing, -repair, or correction. This disclaimer of warranty constitutes an -essential part of this License. No use of any Covered Software is -authorized under this License except under this disclaimer.** - - -#7. Limitation of Liability - -**Under no circumstances and under no legal theory, whether tort -(including negligence), contract, or otherwise, shall any -Contributor, or anyone who distributes Covered Software as -permitted above, be liable to You for any direct, indirect, -special, incidental, or consequential damages of any character -including, without limitation, damages for lost profits, loss of -goodwill, work stoppage, computer failure or malfunction, or any -and all other commercial damages or losses, even if such party -shall have been informed of the possibility of such damages. This -limitation of liability shall not apply to liability for death or -personal injury resulting from such party's negligence to the -extent applicable law prohibits such limitation. Some -jurisdictions do not allow the exclusion or limitation of -incidental or consequential damages, so this exclusion and -limitation may not apply to You.** - - -## 8. Litigation - -Any litigation relating to this License may be brought only in the -courts of a jurisdiction where the defendant maintains its principal -place of business and such litigation shall be governed by laws of that -jurisdiction, without reference to its conflict-of-law provisions. -Nothing in this Section shall prevent a party's ability to bring -cross-claims or counter-claims. - -## 9. Miscellaneous - -This License represents the complete agreement concerning the subject -matter hereof. If any provision of this License is held to be -unenforceable, such provision shall be reformed only to the extent -necessary to make it enforceable. Any law or regulation which provides -that the language of a contract shall be construed against the drafter -shall not be used to construe this License against a Contributor. - -## 10. Versions of the License - -### 10.1. New Versions - -Mozilla Foundation is the license steward. Except as provided in Section -10.3, no one other than the license steward has the right to modify or -publish new versions of this License. Each version will be given a -distinguishing version number. - -### 10.2. Effect of New Versions - -You may distribute the Covered Software under the terms of the version -of the License under which You originally received the Covered Software, -or under the terms of any subsequent version published by the license -steward. - -### 10.3. Modified Versions - -If you create software not governed by this License, and you want to -create a new license for such software, you may create and use a -modified version of this License if you rename the license and remove -any references to the name of the license steward (except to note that -such modified license differs from this License). - -### 10.4. Distributing Source Code Form that is Incompatible With Secondary -Licenses - -If You choose to distribute Source Code Form that is Incompatible With -Secondary Licenses under the terms of this version of the License, the -notice described in Exhibit B of this License must be attached. - -## Exhibit A - Source Code Form License Notice - - - This Source Code Form is subject to the terms of the Mozilla Public - License, v. 2.0. If a copy of the MPL was not distributed with this - file, You can obtain one at http://mozilla.org/MPL/2.0/. - -If it is not possible or desirable to put the notice in a particular -file, then You may include the notice in a location (such as a LICENSE -file in a relevant directory) where a recipient would be likely to look -for such a notice. - -You may add additional accurate notices of copyright ownership. - -## Exhibit B - "Incompatible With Secondary Licenses" Notice - - - This Source Code Form is "Incompatible With Secondary Licenses", as - defined by the Mozilla Public License, v. 2.0. - diff --git a/subscriptions/sound/src/lib.rs b/subscriptions/sound/src/lib.rs deleted file mode 100644 index f6a66d038..000000000 --- a/subscriptions/sound/src/lib.rs +++ /dev/null @@ -1,956 +0,0 @@ -// Copyright 2024 System76 -// SPDX-License-Identifier: MPL-2.0 - -use cosmic::Task; -use cosmic::iced::stream; -use cosmic_pipewire as pipewire; -use futures::{SinkExt, Stream}; -use intmap::IntMap; -use pipewire::Availability; -use std::process::Stdio; -use std::sync::{Arc, Mutex}; -use std::time::Duration; - -pub type DeviceId = u32; -pub type NodeId = u32; -pub type ProfileId = i32; -pub type RouteId = u32; - -pub fn watch() -> impl Stream + Send + 'static { - stream::channel( - 1, - |mut emitter: futures::channel::mpsc::Sender| async move { - loop { - let (cancel_tx, cancel_rx) = futures::channel::oneshot::channel::<()>(); - let sender = Arc::new((Mutex::new(Vec::new()), tokio::sync::Notify::const_new())); - let receiver = sender.clone(); - - _ = emitter - .send(Message::SubHandle(Arc::new(SubscriptionHandle { - cancel_tx, - pipewire: pipewire::run(move |event| { - sender.0.lock().unwrap().push(event); - sender.1.notify_one(); - }), - }))) - .await; - - let forwarder = Box::pin(async { - loop { - _ = receiver.1.notified().await; - let events = std::mem::take(&mut *receiver.0.lock().unwrap()); - if !events.is_empty() { - _ = emitter.send(Message::Server(Arc::from(events))).await; - tokio::time::sleep(Duration::from_millis(64)).await; - } - } - }); - - futures::future::select(cancel_rx, forwarder).await; - } - }, - ) -} - -#[derive(Default)] -pub struct Model { - subscription_handle: Option, - - pub device_profile_dropdowns: Vec<(DeviceId, String, Option, Vec, Vec)>, - - // Translated text - pub unplugged_text: String, - pub hd_audio_text: String, - pub usb_audio_text: String, - - device_ids: IntMap, - node_names: IntMap, - card_profile_devices: IntMap, - node_route_indexes: IntMap, - - device_names: IntMap, - device_profiles: IntMap>, - active_profiles: IntMap, - device_routes: IntMap>, - - /** Sink devices */ - - /// Description of a sink device and its port - sinks: Vec, - /// Node IDs for sinks - sink_node_ids: Vec, - /// Index of active sink device. - active_sink: Option, - /// Node ID of active sink device. - active_sink_node: Option, - /// Device ID of active sink device. - active_sink_device: Option, - /// Device identifier of the default sink. - active_sink_node_name: String, - - /** Source devices */ - - /// Product names for source devices. - sources: Vec, - /// Node IDs for sources - source_node_ids: Vec, - /// Index of active source device. - active_source: Option, - /// Node ID of active source device. - active_source_node: Option, - /// Device ID of active source device. - active_source_device: Option, - /// Node identifier of the default source. - active_source_node_name: String, - - changing_sink_device: Option, - changing_source_device: Option, - - pub sink_volume_text: String, - pub source_volume_text: String, - pub sink_balance: Option, - - pub sink_volume: u32, - pub source_volume: u32, - - pub sink_mute: bool, - sink_volume_debounce: bool, - pub source_mute: bool, - source_volume_debounce: bool, -} - -impl Model { - pub fn active_sink(&self) -> Option { - self.active_sink - } - - pub fn active_source(&self) -> Option { - self.active_source - } - - pub fn sinks(&self) -> &[String] { - &self.sinks - } - - pub fn sources(&self) -> &[String] { - &self.sources - } - - pub fn clear(&mut self) { - if let Some(handle) = self.subscription_handle.take() { - _ = handle.cancel_tx.send(()); - _ = handle.pipewire.send(pipewire::Request::Quit); - } - } - - /// Send a message to the pipewire-rs thread. - pub fn pipewire_send(&self, request: pipewire::Request) { - if let Some(handle) = self.subscription_handle.as_ref() { - _ = handle.pipewire.send(request); - } - } - - /// Sets and applies a profile to a device with wpctl. - /// - /// Requires using the device ID rather than a node ID. - pub fn set_profile(&mut self, device_id: DeviceId, index: u32, save: bool) { - if save { - self.changing_sink_device = self - .device_ids - .iter() - .find(|(node_id, _device)| self.active_sink_node == Some(*node_id)) - .and_then(|(_node_id, &device)| { - if device == device_id { - Some(device_id) - } else { - None - } - }); - - self.changing_source_device = self - .device_ids - .iter() - .find(|(node_id, _device)| self.active_source_node == Some(*node_id)) - .and_then(|(_node_id, &device)| { - if device == device_id { - Some(device_id) - } else { - None - } - }); - } - - let mut update = false; - - if let Some(profiles) = self.device_profiles.get(device_id) { - for profile in profiles { - if profile.index as u32 == index { - self.active_profiles.insert(device_id, profile.clone()); - self.pipewire_send(pipewire::Request::SetProfile(device_id, index, save)); - update = true; - } - } - - if update { - self.update_ui_profiles(); - } - - // Use pw-cli as a fallback in case it wasn't set correctly. - tokio::spawn(async move { - set_profile(device_id, index, save).await; - }); - } - } - - /// Change the balance of channel volumes on the sink device. - pub fn set_sink_balance(&mut self, balance: u32) -> Task { - self.sink_balance = (balance != 100).then(|| balance as f32 / 100.); - if self.sink_volume_debounce { - return Task::none(); - } - - if let Some(id) = self.active_sink_node { - self.sink_volume_debounce = true; - return cosmic::Task::future(async move { - tokio::time::sleep(Duration::from_millis(128)).await; - Message::SinkVolumeApply(id) - }); - } - - Task::none() - } - - /// Change the default sink device - pub fn set_default_sink(&mut self, pos: usize) -> Task { - if let Some(&node_id) = self.sink_node_ids.get(pos) { - self.set_default_sink_node_id(node_id); - } - - Task::none() - } - - pub fn set_default_sink_node_id(&mut self, node_id: NodeId) { - tracing::debug!(target: "sound", "set default sink node {node_id}"); - self.set_default_sink_id(node_id); - - // Use pactl if the node is not a device node. - let virtual_sink_name: Option = - if let Some(device) = self.device_ids.get(node_id).cloned() { - // Get route index of the selected node and apply it to the device. - if let Some((card_profile_device, route_index)) = self - .card_profile_devices - .get(node_id) - .cloned() - .zip(self.node_route_indexes.get(node_id).cloned()) - { - self.pipewire_send(pipewire::Request::SetRoute( - device, - card_profile_device, - route_index as u32, - )); - } - - None - } else { - self.node_names.get(node_id).cloned() - }; - - tokio::task::spawn(async move { - if let Some(node_name) = virtual_sink_name { - pactl_set_default_sink(&node_name).await - } else { - set_default(node_id).await - } - }); - } - - /// Toggle the mute property of the sink device. - pub fn toggle_sink_mute(&mut self) { - self.sink_mute = !self.sink_mute; - if let Some(node_id) = self.active_sink_node { - let mute = self.sink_mute; - if let Some(handle) = self.subscription_handle.as_mut() { - _ = handle - .pipewire - .send(pipewire::Request::SetNodeMute(node_id, mute)); - } - } - } - - /// Change the sink device's volume. - pub fn set_sink_volume(&mut self, volume: u32) -> Task { - self.sink_volume = volume; - self.sink_volume_text = numtoa::BaseN::<10>::u32(volume).as_str().to_owned(); - if self.sink_volume_debounce { - return Task::none(); - } - - // Wait for the debounce duration before applying the volume change. - if let Some(node_id) = self.active_sink_node { - self.sink_volume_debounce = true; - return cosmic::Task::future(async move { - tokio::time::sleep(Duration::from_millis(128)).await; - Message::SinkVolumeApply(node_id) - }); - } - - Task::none() - } - - /// Change the default source device. - pub fn set_default_source(&mut self, pos: usize) -> Task { - if let Some(&node_id) = self.source_node_ids.get(pos) { - self.set_default_source_node_id(node_id); - } - - Task::none() - } - - pub fn set_default_source_node_id(&mut self, node_id: NodeId) { - tracing::debug!(target: "sound", "set default source node {node_id}"); - self.set_default_source_id(node_id); - - // Use pactl if the node is not a device node. - let virtual_source_name: Option = - if let Some(device) = self.device_ids.get(node_id).cloned() { - // Get route index of the selected node and apply it to the device. - if let Some((card_profile_device, route_index)) = self - .card_profile_devices - .get(node_id) - .cloned() - .zip(self.node_route_indexes.get(node_id).cloned()) - { - self.pipewire_send(pipewire::Request::SetRoute( - device, - card_profile_device, - route_index as u32, - )); - } - - None - } else { - self.node_names.get(node_id).cloned() - }; - - tokio::task::spawn(async move { - if let Some(node_name) = virtual_source_name { - pactl_set_default_source(&node_name).await - } else { - set_default(node_id).await - } - }); - } - - /// Toggle the mute property of the source device. - pub fn toggle_source_mute(&mut self) { - self.source_mute = !self.source_mute; - if let Some(node_id) = self.active_source_node { - let mute = self.source_mute; - if let Some(handle) = self.subscription_handle.as_mut() { - _ = handle - .pipewire - .send(pipewire::Request::SetNodeMute(node_id, mute)); - } - } - } - - /// Change the source device's volume. - pub fn set_source_volume(&mut self, volume: u32) -> Task { - self.source_volume = volume; - self.source_volume_text = numtoa::BaseN::<10>::u32(volume).as_str().to_owned(); - if self.source_volume_debounce { - return Task::none(); - } - - // Wait for the debounce duration before applying the volume change. - if let Some(node_id) = self.active_source_node { - self.source_volume_debounce = true; - return cosmic::Task::future(async move { - tokio::time::sleep(Duration::from_millis(128)).await; - Message::SourceVolumeApply(node_id) - }); - } - - Task::none() - } - - pub fn update(&mut self, message: Message) -> Task { - match message { - Message::Server(events) => { - Arc::into_inner(events) - .into_iter() - .flatten() - .for_each(|event| self.pipewire_update(event)); - } - - Message::SinkVolumeApply(node_id) => { - self.sink_volume_debounce = false; - self.pipewire_send(pipewire::Request::SetNodeVolume( - node_id, - self.sink_volume as f32 / 100.0, - self.sink_balance, - )); - } - - Message::SourceVolumeApply(node_id) => { - self.source_volume_debounce = false; - self.pipewire_send(pipewire::Request::SetNodeVolume( - node_id, - self.source_volume as f32 / 100.0, - None, - )); - } - - Message::SubHandle(handle) => { - if let Some(handle) = Arc::into_inner(handle) { - self.subscription_handle = Some(handle); - } - } - } - - Task::none() - } - - fn pipewire_update(&mut self, event: pipewire::Event) { - match event { - pipewire::Event::NodeProperties(id, props) => { - if self.active_sink_node == Some(id) { - if self.sink_volume_debounce { - return; - } - - if let Some(mute) = props.mute { - self.sink_mute = mute; - } - - if let Some(channel_volumes) = props.channel_volumes { - let (volume, balance) = - pipewire::volume::from_channel_volumes(&channel_volumes); - - self.sink_balance = balance; - self.sink_volume = (volume * 100.0) as u32; - self.sink_volume_text = numtoa::BaseN::<10>::u32(self.sink_volume) - .as_str() - .to_owned(); - } - } else if self.active_source_node == Some(id) { - if self.source_volume_debounce { - return; - } - - if let Some(mute) = props.mute { - self.source_mute = mute; - } - - if let Some(channel_volumes) = props.channel_volumes { - let (volume, _balance) = - pipewire::volume::from_channel_volumes(&channel_volumes); - self.source_volume = (volume * 100.0) as u32; - self.source_volume_text = numtoa::BaseN::<10>::u32(self.source_volume) - .as_str() - .to_owned(); - } - } - } - - pipewire::Event::ActiveProfile(id, profile) => { - tracing::debug!( - target: "sound", - "Device {id} active profile changed to {}: {}", - profile.index, - profile.description - ); - - let prev = self.active_profiles.insert(id, profile.clone()); - self.update_ui_profiles(); - if let Some(prev) = prev { - if prev.index == profile.index { - return; - } - - tracing::debug!( - target: "sound", - "Device {id} profile changed from {} to {}: {}", - prev.index, profile.index, profile.description - ); - } else { - #[cfg(feature = "auto-profile-init")] - if profile.index != 0 { - // Use pw-cli to re-set the profile in case wireplumber has invalid state. - // Profiles set by us do not need to use this. Only sets if profile is not `Off`. - tracing::debug!( - target: "sound", - "Device {id} initialized with profile {}: {}", profile.index, profile.description - ); - - self.set_profile(id, profile.index as u32, false); - } - } - } - - pipewire::Event::ActiveRoute(id, _index, route) => { - tracing::debug!( - target: "sound", - "Device {id} active route changed to {}: {}", - route.index, - route.description - ); - - self.update_device_route_name(&route, id); - - let (active_device, node_ids, set_default_node): ( - Option, - &[NodeId], - fn(&mut Self, NodeId), - ) = match route.direction { - pipewire::Direction::Output => ( - self.active_sink_device, - &self.sink_node_ids, - Self::set_default_sink_id, - ), - pipewire::Direction::Input => ( - self.active_source_device, - &self.source_node_ids, - Self::set_default_source_id, - ), - }; - - if active_device == Some(id) { - for (node_id, &device) in &self.device_ids { - if device == id && node_ids.contains(&node_id) { - set_default_node(self, node_id); - break; - } - } - } - } - - pipewire::Event::AddProfile(id, index, profile) => { - if let Some(p) = self.active_profiles.get_mut(id) - && p.index == profile.index - { - *p = profile.clone(); - } - - let profiles = self.device_profiles.entry(id).or_default(); - if profiles.len() < index as usize + 1 { - let additional = (index as usize + 1) - profiles.capacity(); - profiles.reserve_exact(additional); - profiles.extend(std::iter::repeat_n( - pipewire::Profile::default(), - additional, - )); - } - - profiles[index as usize] = profile; - - self.update_ui_profiles(); - } - - pipewire::Event::AddRoute(id, index, route) => self.add_route(id, index, route), - - pipewire::Event::AddDevice(device) => { - tracing::debug!(target: "sound", "Device {} added: {}", device.id, device.name); - self.device_names - .insert(device.id, self.translate_device_name(&device.name)); - } - - pipewire::Event::AddNode(node) => { - tracing::debug!(target: "sound", "Node {} added: {}", node.object_id, node.node_name); - // Device nodes will have device and card profile device IDs. - // Virtual sinks/sources do not have these. - if let Some(device_id) = node.device_id { - self.device_ids.insert(node.object_id, device_id); - - // This is the device number of the route. This is used with the - // device ID to set properties for a route. - if let Some(card_profile_device) = node.card_profile_device { - self.card_profile_devices - .insert(node.object_id, card_profile_device); - } - } - - let description = self.translate_device_name(&node.description); - - // The default sink/source is defined by a node's name. We use this when setting - // virtual sink/source nodes with pactl; and when pipewire notifies us of a new - // default sink/source. - if self - .node_names - .insert(node.object_id, node.node_name.clone()) - .is_none() - { - // Use the device.profile.description as the route name by default for the UI. - let name = if node.device_profile_description.is_empty() { - description - } else { - [&node.device_profile_description, " - ", &description].concat() - }; - - // Check if the node is a sink or a source, and append it to the relevant collections. - match node.media_class { - pipewire::MediaClass::Sink => { - self.sinks.push(name); - self.sink_node_ids.push(node.object_id); - - // Set the sink as the default if it matches the server. - if self.active_sink_node_name == node.node_name { - tracing::debug!( - target: "sound", - "Node {} ({}) was the default sink", - node.object_id, - node.node_name - ); - self.set_default_sink_node_id(node.object_id); - } else if let Some(device_id) = self.changing_sink_device { - for (node_id, &device) in &self.device_ids { - if device == device_id && self.sink_node_ids.contains(&node_id) - { - self.changing_sink_device = None; - self.set_default_sink_node_id(node_id); - return; - } - } - } - } - - pipewire::MediaClass::Source => { - self.sources.push(name); - self.source_node_ids.push(node.object_id); - - // Set the source as the default if it matches the server. - if self.active_source_node_name == node.node_name { - tracing::debug!( - target: "sound", - "Node {} ({}) was the default source", - node.object_id, - node.node_name - ); - self.set_default_source_node_id(node.object_id); - } else if let Some(device_id) = self.changing_source_device { - for (node_id, &device) in &self.device_ids { - if device == device_id - && self.source_node_ids.contains(&node_id) - { - self.changing_source_device = None; - self.set_default_source_node_id(node_id); - return; - } - } - } - } - } - } - } - - pipewire::Event::DefaultSink(node_name) => { - tracing::debug!(target: "sound", "default sink node changed to {node_name}"); - if self.active_sink_node_name == node_name { - return; - } - - if let Some(id) = self.node_id_from_name(&node_name) { - self.set_default_sink_id(id); - } - - self.active_sink_node_name = node_name; - } - - pipewire::Event::DefaultSource(node_name) => { - tracing::debug!(target: "sound", "default source node changed to {node_name}"); - if self.active_source_node_name == node_name { - return; - } - - if let Some(id) = self.node_id_from_name(&node_name) { - self.set_default_source_id(id); - } - - self.active_source_node_name = node_name; - } - - pipewire::Event::RemoveDevice(id) => self.remove_device(id), - pipewire::Event::RemoveNode(id) => self.remove_node(id), - } - } - - fn add_route(&mut self, id: DeviceId, index: u32, route: pipewire::Route) { - self.update_device_route_name(&route, id); - - tracing::debug!(target: "sound", - "Device {} added route {} ({:?}); {:?}", - id, - route.name, - route.direction, - route.available - ); - - let routes = self.device_routes.entry(id).or_default(); - if routes.len() < index as usize + 1 { - let additional = (index as usize + 1) - routes.capacity(); - routes.reserve_exact(additional); - routes.extend(std::iter::repeat_n(pipewire::Route::default(), additional)); - } - - routes[index as usize] = route; - } - - fn node_id_from_name(&self, name: &str) -> Option { - self.node_names - .iter() - .find(|&(_, n)| *n == name) - .map(|(id, _)| id) - } - - fn remove_device(&mut self, id: DeviceId) { - tracing::debug!(target: "sound", "Device {id} removed"); - _ = self.device_names.remove(id); - _ = self.device_profiles.remove(id); - _ = self.active_profiles.remove(id); - _ = self.device_routes.remove(id); - } - - fn remove_node(&mut self, id: NodeId) { - tracing::debug!(target: "sound", "Node {id} removed"); - if let Some(pos) = self.sink_node_ids.iter().position(|&node_id| node_id == id) { - self.sink_node_ids.remove(pos); - self.sinks.remove(pos); - if let Some(node_id) = self.active_sink_node - && id == node_id - { - self.active_sink = None; - self.active_sink_node = None; - self.active_sink_node_name.clear(); - } - } else if let Some(pos) = self - .source_node_ids - .iter() - .position(|&node_id| node_id == id) - { - self.source_node_ids.remove(pos); - self.sources.remove(pos); - if let Some(node_id) = self.active_source_node - && id == node_id - { - self.active_source = None; - self.active_source_node = None; - self.active_source_node_name.clear(); - } - } - - _ = self.device_ids.remove(id); - _ = self.node_names.remove(id); - _ = self.card_profile_devices.remove(id); - } - - /// Set the default sink device by its the node ID. - fn set_default_sink_id(&mut self, node_id: NodeId) { - self.active_sink = self.sink_node_ids.iter().position(|&id| id == node_id); - self.active_sink_node = Some(node_id); - self.active_sink_node_name = self.node_names.get(node_id).cloned().unwrap_or_default(); - self.active_sink_device = self - .device_ids - .iter() - .find_map(|(nid, did)| if nid == node_id { Some(*did) } else { None }); - } - - /// Set the default source device by its the node ID. - fn set_default_source_id(&mut self, node_id: NodeId) { - self.active_source = self.source_node_ids.iter().position(|&id| id == node_id); - self.active_source_node = Some(node_id); - self.active_source_node_name = self.node_names.get(node_id).cloned().unwrap_or_default(); - self.active_source_device = self - .device_ids - .iter() - .find_map(|(nid, did)| if nid == node_id { Some(*did) } else { None }); - } - - fn update_device_route_name(&mut self, route: &pipewire::Route, id: DeviceId) { - if matches!(route.available, Availability::No) { - return; - } - - let (devices, node_ids) = match route.direction { - pipewire::Direction::Output => (&mut self.sinks, &self.sink_node_ids), - pipewire::Direction::Input => (&mut self.sources, &self.source_node_ids), - }; - - for (pos, &node) in node_ids.iter().enumerate() { - let Some(&device) = self.device_ids.get(node) else { - continue; - }; - - if device != id { - continue; - } - - let Some(profile) = self.active_profiles.get(id) else { - continue; - }; - - if !profile.name.starts_with("pro-audio") { - let Some(&card_profile_device) = self.card_profile_devices.get(node) else { - continue; - }; - - if !route.devices.contains(&(card_profile_device as i32)) { - continue; - } - } - - let Some(device_name) = self.device_names.get(id) else { - continue; - }; - - tracing::debug!(target: "sound", "matched route {} on {}: {}", route.index, id, route.description); - devices[pos] = [&route.description, " - ", device_name].concat(); - self.node_route_indexes.insert(node, route.index); - - break; - } - } - - // Update the cached profiles for the UI. - fn update_ui_profiles(&mut self) { - self.device_profile_dropdowns = self - .device_profiles - .iter() - .filter_map(|(device_id, profiles)| { - let name = self.device_names.get(device_id)?.as_str(); - let (active_profile, indexes, descriptions) = self - .active_profiles - .get(device_id) - .map(|profile| { - let (indexes, descriptions): (Vec<_>, Vec<_>) = profiles - .iter() - .filter(|p| { - p.index == profile.index - || !matches!(p.available, pipewire::Availability::No) - }) - .map(|p| (p.index as u32, p.description.clone())) - .collect(); - - let pos = profiles - .iter() - .filter(|p| { - p.index == profile.index - || !matches!(p.available, pipewire::Availability::No) - }) - .enumerate() - .find(|(_, p)| p.index == profile.index) - .map(|(pos, _)| pos); - - (pos, indexes, descriptions) - }) - .unwrap_or_else(|| { - let (indexes, descriptions): (Vec<_>, Vec<_>) = profiles - .iter() - .filter(|p| !matches!(p.available, pipewire::Availability::No)) - .map(|p| (p.index as u32, p.description.clone())) - .collect(); - - (None, indexes, descriptions) - }); - - Some(( - device_id, - name.to_owned(), - active_profile, - indexes, - descriptions, - )) - }) - .collect::>(); - - self.device_profile_dropdowns.sort_by(|a, b| a.1.cmp(&b.1)); - } - - fn translate_device_name(&self, input: &str) -> String { - input - .replacen(" Controller", "", 1) - .replacen("High Definition Audio", &self.hd_audio_text, 1) - .replacen("HD Audio", &self.hd_audio_text, 1) - .replacen("USB Audio Device", &self.usb_audio_text, 1) - } -} - -#[derive(Clone, Debug)] -pub enum Message { - /// Handle messages from the sound server. - Server(Arc>), - /// Change the output volume. - SinkVolumeApply(NodeId), - /// Change the input volume. - SourceVolumeApply(NodeId), - /// On init of the subscription, channels for closing background threads are given to the app. - SubHandle(Arc), -} - -pub struct SubscriptionHandle { - cancel_tx: futures::channel::oneshot::Sender<()>, - pipewire: pipewire::Sender, -} - -impl std::fmt::Debug for SubscriptionHandle { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.write_str("SubscriptionHandle") - } -} - -// TODO: Use pipewire library -pub async fn set_default(id: u32) { - tracing::debug!(target: "sound", "setting default node {id}"); - let id = numtoa::BaseN::<10>::u32(id); - _ = tokio::process::Command::new("wpctl") - .args(["set-default", id.as_str()]) - .stdout(Stdio::null()) - .stderr(Stdio::null()) - .status() - .await; -} - -/// Use this to set a virtual sink as a default. -/// TODO: We should be able to set this with pipewire-rs somehow. -pub async fn pactl_set_default_sink(node_name: &str) { - tracing::debug!(target: "sound", "setting default virtual node {node_name}"); - _ = tokio::process::Command::new("pactl") - .args(["set-default-sink", node_name]) - .stdout(Stdio::null()) - .stderr(Stdio::null()) - .status() - .await; -} - -/// Use this to set a virtual sink as a default. -/// TODO: We should be able to set this with pipewire-rs somehow. -pub async fn pactl_set_default_source(node_name: &str) { - _ = tokio::process::Command::new("pactl") - .args(["set-default-source", node_name]) - .stdout(Stdio::null()) - .stderr(Stdio::null()) - .status() - .await; -} - -// TODO: Use pipewire library -pub async fn set_profile(id: u32, index: u32, save: bool) { - let id = numtoa::BaseN::<10>::u32(id); - let index = numtoa::BaseN::<10>::u32(index); - let value = [ - "{ index: ", - index.as_str(), - if save { - ", save: true }" - } else { - ", save: false }" - }, - ] - .concat(); - - _ = tokio::process::Command::new("pw-cli") - .args(["s", id.as_str(), "Profile", &value]) - .stdout(Stdio::null()) - .stderr(Stdio::null()) - .status() - .await; -} From cee3caf3d6fdda31ee2059d3a1a57810b7740659 Mon Sep 17 00:00:00 2001 From: Michael Aaron Murphy Date: Wed, 3 Jun 2026 21:09:08 +0200 Subject: [PATCH 2/4] fix(sound): apply balance slider updates --- cosmic-settings/src/pages/sound/mod.rs | 7 +++++-- pages/sound/src/model.rs | 2 ++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/cosmic-settings/src/pages/sound/mod.rs b/cosmic-settings/src/pages/sound/mod.rs index d0c842bed..37e0b9a34 100644 --- a/cosmic-settings/src/pages/sound/mod.rs +++ b/cosmic-settings/src/pages/sound/mod.rs @@ -242,11 +242,13 @@ impl Page { Message::SetSinkBalance(balance) => { if let Some((client, sink_id)) = self.client.as_ref().zip(self.model.default_sink) { + // self.model.active_sink.balance = Some(balance); block_on(async { _ = client .borrow_mut() .conn - .set_node_volume_balance(sink_id, Some(balance)); + .set_node_volume_balance(sink_id, Some(balance)) + .await; }); } } @@ -442,9 +444,10 @@ fn output() -> Section { .push( widget::slider( 0.0..=1.0, - page.model.active_sink.balance.unwrap_or(0.5).min(1.), + page.model.active_sink.balance.unwrap_or(0.5), |change| Message::SetSinkBalance(change).into(), ) + .step(0.01) .breakpoints(&[0.5]), ) .push(horizontal_space().width(8.)) diff --git a/pages/sound/src/model.rs b/pages/sound/src/model.rs index d44ff1761..e7c4d00fa 100644 --- a/pages/sound/src/model.rs +++ b/pages/sound/src/model.rs @@ -93,6 +93,7 @@ impl Model { } audio_client::Event::NodeVolume(node_id, volume, balance) => { + eprintln!("node volume {node_id} {volume} {balance:?}"); if let Some(pos) = self.sinks.id.iter().position(|id| node_id == *id) { self.sinks.volume[pos] = volume; self.sinks.balance[pos] = balance; @@ -146,6 +147,7 @@ impl Model { } audio_client::Event::Node(node_id, node) => { + eprintln!("insert node {node_id}"); self.node_devices.insert(node_id, node.device_id); if node.is_sink { let pos = if let Some(pos) = self.sinks.id.iter().position(|&id| id == node_id) From c8e655e364b520ec0497a54a604053615116b3b6 Mon Sep 17 00:00:00 2001 From: Michael Aaron Murphy Date: Thu, 4 Jun 2026 21:01:08 +0200 Subject: [PATCH 3/4] fix(sound): same as previous commit --- cosmic-settings/src/pages/sound/mod.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cosmic-settings/src/pages/sound/mod.rs b/cosmic-settings/src/pages/sound/mod.rs index 37e0b9a34..56e323421 100644 --- a/cosmic-settings/src/pages/sound/mod.rs +++ b/cosmic-settings/src/pages/sound/mod.rs @@ -164,6 +164,7 @@ impl page::AutoBind for Page { impl Page { pub fn update(&mut self, message: Message) -> Task { + tracing::debug!(target: "sound", ?message, "update"); match message { Message::Surface(a) => return cosmic::task::message(crate::app::Message::Surface(a)), @@ -242,7 +243,7 @@ impl Page { Message::SetSinkBalance(balance) => { if let Some((client, sink_id)) = self.client.as_ref().zip(self.model.default_sink) { - // self.model.active_sink.balance = Some(balance); + self.model.active_sink.balance = Some(balance); block_on(async { _ = client .borrow_mut() From 18083eaaf81aa0a47b13b810119abb17c53cd667 Mon Sep 17 00:00:00 2001 From: Michael Aaron Murphy Date: Thu, 4 Jun 2026 21:11:42 +0200 Subject: [PATCH 4/4] chore: update dependencies --- Cargo.lock | 790 +++++++++++++++++++++++++++++++++-------------------- 1 file changed, 495 insertions(+), 295 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 160b05b4d..feec5c1eb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -215,7 +215,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0f2a1bb052857d5dd49572219344a7332b31b76405648eabac5bc68978251bcd" dependencies = [ "android-properties", - "bitflags 2.11.1", + "bitflags 2.12.1", "cc", "jni", "libc", @@ -608,9 +608,9 @@ dependencies = [ [[package]] name = "autocfg" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" +checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" [[package]] name = "av-scenechange" @@ -648,9 +648,9 @@ dependencies = [ [[package]] name = "avif-serialize" -version = "0.8.8" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "375082f007bd67184fb9c0374614b29f9aaa604ec301635f72338bb65386a53d" +checksum = "e7178fe5f7d460b13895ebb9dcb28a3a6216d2df2574a0806cb51b555d297f38" dependencies = [ "arrayvec", ] @@ -714,9 +714,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.11.1" +version = "2.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3" +checksum = "84d7ced0ae9557296835c32bf1b1e02b44c746701f898460fb000d7eaa84f00a" dependencies = [ "serde_core", ] @@ -828,14 +828,23 @@ dependencies = [ [[package]] name = "brotli-decompressor" -version = "5.0.0" +version = "5.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "874bb8112abecc98cbd6d81ea4fa7e94fb9449648c93cc89aa40c81c24d7de03" +checksum = "5962523e1b92ce1b5e793d9169b9943eece10d39f62550bc04bb605d75b94924" dependencies = [ "alloc-no-stdlib", "alloc-stdlib", ] +[[package]] +name = "bs58" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4" +dependencies = [ + "tinyvec", +] + [[package]] name = "bstr" version = "1.12.1" @@ -858,15 +867,15 @@ dependencies = [ [[package]] name = "built" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4ad8f11f288f48ca24471bbd51ac257aaeaaa07adae295591266b792902ae64" +checksum = "5c0e531d93d39c34eef561e929e8a7f86d77a5af08aac4f6d6e39976c51858e9" [[package]] name = "bumpalo" -version = "3.20.2" +version = "3.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" +checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" [[package]] name = "by_address" @@ -928,7 +937,7 @@ version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4dbf9978365bac10f54d1d4b04f7ce4427e51f71d61f2fe15e3fed5166474df7" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", "polling", "rustix 1.1.4", "slab", @@ -958,14 +967,14 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.60" +version = "1.2.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43c5703da9466b66a946814e1adf53ea2c90f10063b86290cc9eb67ce3478a20" +checksum = "556e016178bb5662a08681bbe0f00f8e17631781a4dfc8c45e466e4b185ec27f" dependencies = [ "find-msvc-tools", "jobserver", "libc", - "shlex", + "shlex 2.0.1", ] [[package]] @@ -999,9 +1008,9 @@ checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" [[package]] name = "chrono" -version = "0.4.44" +version = "0.4.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0" +checksum = "1aa79e62e7697b8e29b513a68abacf485adcd1fe8284a4316c5ae868e6633327" dependencies = [ "iana-time-zone", "js-sys", @@ -1269,7 +1278,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", "core-foundation 0.10.1", "libc", ] @@ -1286,7 +1295,7 @@ dependencies = [ [[package]] name = "cosmic-bg-config" version = "1.0.3" -source = "git+https://github.com/pop-os/cosmic-bg#06970d5945b45a634b9ed314f5ca3a86a8502fd8" +source = "git+https://github.com/pop-os/cosmic-bg#b1ca4c180ab29dd185472b777ab0abdb1f96ccaf" dependencies = [ "cosmic-config", "derive_setters", @@ -1300,7 +1309,7 @@ name = "cosmic-client-toolkit" version = "0.1.0" source = "git+https://github.com/pop-os/cosmic-protocols//?rev=d0e95be#d0e95be25e423cfe523b11111a3666ed7aaf0dc4" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", "cosmic-protocols", "libc", "smithay-client-toolkit", @@ -1311,7 +1320,7 @@ dependencies = [ [[package]] name = "cosmic-comp-config" version = "1.0.0" -source = "git+https://github.com/pop-os/cosmic-comp#4df95190db07f7410666cec4d3bf648de90f81dd" +source = "git+https://github.com/pop-os/cosmic-comp#2f7c34f29ae8d1d5b7d147bdb69ae443bd630e98" dependencies = [ "cosmic-config", "input", @@ -1323,7 +1332,7 @@ dependencies = [ [[package]] name = "cosmic-config" version = "1.0.0" -source = "git+https://github.com/pop-os/libcosmic#9c2a86a8f4d6faff2fe28627820c29c5430d95d5" +source = "git+https://github.com/pop-os/libcosmic#f0f68933f1552857e2165fc0fa953228107bddef" dependencies = [ "atomicwrites", "cosmic-config-derive", @@ -1344,7 +1353,7 @@ dependencies = [ [[package]] name = "cosmic-config-derive" version = "1.0.0" -source = "git+https://github.com/pop-os/libcosmic#9c2a86a8f4d6faff2fe28627820c29c5430d95d5" +source = "git+https://github.com/pop-os/libcosmic#f0f68933f1552857e2165fc0fa953228107bddef" dependencies = [ "quote", "syn 2.0.117", @@ -1363,7 +1372,7 @@ name = "cosmic-dbus-networkmanager" version = "0.1.0" source = "git+https://github.com/pop-os/dbus-settings-bindings#eed01dd3609e90e3c8cd043656734c500956c793" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", "derive_builder", "jiff", "procfs", @@ -1374,7 +1383,7 @@ dependencies = [ [[package]] name = "cosmic-freedesktop-icons" version = "0.4.0" -source = "git+https://github.com/pop-os/freedesktop-icons#9c562fe3ecf03241a46a60c0078cd6ea10bd75ce" +source = "git+https://github.com/pop-os/freedesktop-icons#cb0a2f299dbc443697ce1674065e0d4f82915c02" dependencies = [ "bstr", "btoi", @@ -1410,7 +1419,7 @@ dependencies = [ [[package]] name = "cosmic-panel-config" version = "0.1.0" -source = "git+https://github.com/pop-os/cosmic-panel#d518c7d25ef96f1a9696aa8cce008656bf66ece4" +source = "git+https://github.com/pop-os/cosmic-panel#546a6c45210574caed9a3e51446aa8ad622d3627" dependencies = [ "anyhow", "cosmic-config", @@ -1426,7 +1435,7 @@ name = "cosmic-protocols" version = "0.1.0" source = "git+https://github.com/pop-os/cosmic-protocols//?rev=d0e95be#d0e95be25e423cfe523b11111a3666ed7aaf0dc4" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", "wayland-backend", "wayland-client", "wayland-protocols", @@ -1625,7 +1634,7 @@ dependencies = [ [[package]] name = "cosmic-settings-config" version = "0.1.0" -source = "git+https://github.com/pop-os/cosmic-settings-daemon#716da6d6af0b252e2f78aba2ad72ee19ae0241e0" +source = "git+https://github.com/pop-os/cosmic-settings-daemon#fa82bdf9fe7b5f5bd6008f32f393efd5e7a71c47" dependencies = [ "cosmic-config", "ron 0.11.0", @@ -1646,7 +1655,7 @@ dependencies = [ [[package]] name = "cosmic-settings-daemon-config" version = "0.1.0" -source = "git+https://github.com/pop-os/cosmic-settings-daemon#716da6d6af0b252e2f78aba2ad72ee19ae0241e0" +source = "git+https://github.com/pop-os/cosmic-settings-daemon#fa82bdf9fe7b5f5bd6008f32f393efd5e7a71c47" dependencies = [ "cosmic-config", "serde", @@ -1668,7 +1677,7 @@ dependencies = [ name = "cosmic-settings-network-manager-subscription" version = "1.0.7" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", "cosmic-dbus-networkmanager", "futures", "iced_futures", @@ -1744,7 +1753,7 @@ version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be17b688510d934ce13f48a2beba700e11583e281e0fda99c22bb256a14eda73" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", "fontdb", "harfrust", "linebender_resource_handle", @@ -1765,7 +1774,7 @@ dependencies = [ [[package]] name = "cosmic-theme" version = "1.0.0" -source = "git+https://github.com/pop-os/libcosmic#9c2a86a8f4d6faff2fe28627820c29c5430d95d5" +source = "git+https://github.com/pop-os/libcosmic#f0f68933f1552857e2165fc0fa953228107bddef" dependencies = [ "almost", "configparser", @@ -1879,10 +1888,13 @@ dependencies = [ ] [[package]] -name = "ctor-lite" -version = "0.1.2" +name = "ctor" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e162d0c2e2068eb736b71e5597eff0b9944e6b973cd9f37b6a288ab9bf20e300" +checksum = "83cf0d42651b16c6dfe68685716d18480d18a9c39c62d76e8cf3eb6ed5d8bcbf" +dependencies = [ + "dtor", +] [[package]] name = "cursor-icon" @@ -2111,7 +2123,7 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e0e367e4e7da84520dedcac1901e4da967309406d1e51017ae1abfb97adbd38" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", "block2 0.6.2", "libc", "objc2 0.6.4", @@ -2119,9 +2131,9 @@ dependencies = [ [[package]] name = "displaydoc" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +checksum = "1ac70aa55017e108007fbaf5aa0f54b021c98f92ff8af59d42eda9da96e3dd4f" dependencies = [ "proc-macro2", "quote", @@ -2142,7 +2154,7 @@ name = "dnd" version = "0.1.0" source = "git+https://github.com/pop-os/window_clipboard.git?tag=sctk-0.20#f68595ee0e62fbd6589f4709b5aaa5c3c7ea5f6c" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", "mime 0.1.0", "raw-window-handle", "smithay-client-toolkit", @@ -2173,7 +2185,7 @@ checksum = "117240f60069e65410b3ae1bb213295bd828f707b5bec6596a1afc8793ce0cbc" [[package]] name = "dpi" version = "0.1.2" -source = "git+https://github.com/pop-os/winit.git?tag=cosmic-0.14#261cda54017f98a12dc55569c864430fe6770366" +source = "git+https://github.com/pop-os/winit.git?tag=cosmic-0.14#f737f1f5900b3399b56951305b0f155afc9c9ee5" [[package]] name = "drm" @@ -2181,7 +2193,7 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0f8a69e60d75ae7dab4ef26a59ca99f2a89d4c142089b537775ae0c198bdcde" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", "bytemuck", "drm-ffi", "drm-fourcc", @@ -2214,6 +2226,12 @@ dependencies = [ "linux-raw-sys 0.6.5", ] +[[package]] +name = "dtor" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edf234dd1594d6dd434a8fb8cada51ddbbc593e40e4a01556a0b31c62da2775b" + [[package]] name = "dyn-clone" version = "1.0.20" @@ -2222,9 +2240,9 @@ checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" [[package]] name = "either" -version = "1.15.0" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" +checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e" [[package]] name = "endi" @@ -2388,23 +2406,9 @@ checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" [[package]] name = "fax" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f05de7d48f37cd6730705cbca900770cab77a89f413d23e100ad7fad7795a0ab" -dependencies = [ - "fax_derive", -] - -[[package]] -name = "fax_derive" -version = "0.2.0" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0aca10fb742cb43f9e7bb8467c91aa9bcb8e3ffbc6a6f7389bb93ffc920577d" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] +checksum = "caf1079563223d5d59d83c85886a56e586cfd5c1a26292e971a0fa266531ac5a" [[package]] name = "fdeflate" @@ -2787,8 +2791,21 @@ checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" dependencies = [ "cfg-if", "libc", - "r-efi", + "r-efi 5.3.0", + "wasip2", +] + +[[package]] +name = "getrandom" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" +dependencies = [ + "cfg-if", + "libc", + "r-efi 6.0.0", "wasip2", + "wasip3", ] [[package]] @@ -2895,7 +2912,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b89c83349105e3732062a895becfc71a8f921bb71ecbbdd8ff99263e3b53a0ca" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", "gpu-descriptor-types", "hashbrown 0.15.5", ] @@ -2906,14 +2923,14 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdf242682df893b86f33a73828fb09ca4b2d3bb6cc95249707fc684d27484b91" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", ] [[package]] name = "grid" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9e2d4c0a8296178d8802098410ca05d86b17a10bb5ab559b3fb404c1f948220" +checksum = "b40ca9252762c466af32d0b1002e91e4e1bc5398f77455e55474deb466355ff5" [[package]] name = "guillotiere" @@ -2943,7 +2960,7 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9da2e5ae821f6e96664977bf974d6d6a2d6682f9ccee23e62ec1d134246845f9" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", "bytemuck", "core_maths", "read-fonts", @@ -2978,9 +2995,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.17.0" +version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f467dd6dccf739c208452f8014c75c18bb8301b050ad1cfb27153803edb0f51" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" [[package]] name = "heck" @@ -3154,7 +3171,7 @@ dependencies = [ [[package]] name = "iced" version = "0.14.0" -source = "git+https://github.com/pop-os/libcosmic#9c2a86a8f4d6faff2fe28627820c29c5430d95d5" +source = "git+https://github.com/pop-os/libcosmic#f0f68933f1552857e2165fc0fa953228107bddef" dependencies = [ "dnd", "iced_accessibility", @@ -3175,7 +3192,7 @@ dependencies = [ [[package]] name = "iced_accessibility" version = "0.1.0" -source = "git+https://github.com/pop-os/libcosmic#9c2a86a8f4d6faff2fe28627820c29c5430d95d5" +source = "git+https://github.com/pop-os/libcosmic#f0f68933f1552857e2165fc0fa953228107bddef" dependencies = [ "accesskit", "accesskit_winit", @@ -3184,9 +3201,9 @@ dependencies = [ [[package]] name = "iced_core" version = "0.14.0" -source = "git+https://github.com/pop-os/libcosmic#9c2a86a8f4d6faff2fe28627820c29c5430d95d5" +source = "git+https://github.com/pop-os/libcosmic#f0f68933f1552857e2165fc0fa953228107bddef" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", "bytes", "cosmic-client-toolkit", "dnd", @@ -3202,6 +3219,7 @@ dependencies = [ "serde", "smol_str", "thiserror 2.0.18", + "unicode-segmentation", "web-time", "window_clipboard", ] @@ -3209,7 +3227,7 @@ dependencies = [ [[package]] name = "iced_debug" version = "0.14.0" -source = "git+https://github.com/pop-os/libcosmic#9c2a86a8f4d6faff2fe28627820c29c5430d95d5" +source = "git+https://github.com/pop-os/libcosmic#f0f68933f1552857e2165fc0fa953228107bddef" dependencies = [ "iced_core", "iced_futures", @@ -3219,7 +3237,7 @@ dependencies = [ [[package]] name = "iced_futures" version = "0.14.0" -source = "git+https://github.com/pop-os/libcosmic#9c2a86a8f4d6faff2fe28627820c29c5430d95d5" +source = "git+https://github.com/pop-os/libcosmic#f0f68933f1552857e2165fc0fa953228107bddef" dependencies = [ "futures", "iced_core", @@ -3233,9 +3251,9 @@ dependencies = [ [[package]] name = "iced_graphics" version = "0.14.0" -source = "git+https://github.com/pop-os/libcosmic#9c2a86a8f4d6faff2fe28627820c29c5430d95d5" +source = "git+https://github.com/pop-os/libcosmic#f0f68933f1552857e2165fc0fa953228107bddef" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", "bytemuck", "cosmic-text", "half", @@ -3254,7 +3272,7 @@ dependencies = [ [[package]] name = "iced_program" version = "0.14.0" -source = "git+https://github.com/pop-os/libcosmic#9c2a86a8f4d6faff2fe28627820c29c5430d95d5" +source = "git+https://github.com/pop-os/libcosmic#f0f68933f1552857e2165fc0fa953228107bddef" dependencies = [ "iced_graphics", "iced_runtime", @@ -3263,7 +3281,7 @@ dependencies = [ [[package]] name = "iced_renderer" version = "0.14.0" -source = "git+https://github.com/pop-os/libcosmic#9c2a86a8f4d6faff2fe28627820c29c5430d95d5" +source = "git+https://github.com/pop-os/libcosmic#f0f68933f1552857e2165fc0fa953228107bddef" dependencies = [ "iced_graphics", "iced_tiny_skia", @@ -3275,7 +3293,7 @@ dependencies = [ [[package]] name = "iced_runtime" version = "0.14.0" -source = "git+https://github.com/pop-os/libcosmic#9c2a86a8f4d6faff2fe28627820c29c5430d95d5" +source = "git+https://github.com/pop-os/libcosmic#f0f68933f1552857e2165fc0fa953228107bddef" dependencies = [ "bytes", "cosmic-client-toolkit", @@ -3291,7 +3309,7 @@ dependencies = [ [[package]] name = "iced_tiny_skia" version = "0.14.0" -source = "git+https://github.com/pop-os/libcosmic#9c2a86a8f4d6faff2fe28627820c29c5430d95d5" +source = "git+https://github.com/pop-os/libcosmic#f0f68933f1552857e2165fc0fa953228107bddef" dependencies = [ "bytemuck", "cosmic-text", @@ -3308,10 +3326,10 @@ dependencies = [ [[package]] name = "iced_wgpu" version = "0.14.0" -source = "git+https://github.com/pop-os/libcosmic#9c2a86a8f4d6faff2fe28627820c29c5430d95d5" +source = "git+https://github.com/pop-os/libcosmic#f0f68933f1552857e2165fc0fa953228107bddef" dependencies = [ "as-raw-xcb-connection", - "bitflags 2.11.1", + "bitflags 2.12.1", "bytemuck", "cosmic-client-toolkit", "cryoglyph", @@ -3339,7 +3357,7 @@ dependencies = [ [[package]] name = "iced_widget" version = "0.14.2" -source = "git+https://github.com/pop-os/libcosmic#9c2a86a8f4d6faff2fe28627820c29c5430d95d5" +source = "git+https://github.com/pop-os/libcosmic#f0f68933f1552857e2165fc0fa953228107bddef" dependencies = [ "cosmic-client-toolkit", "dnd", @@ -3359,7 +3377,7 @@ dependencies = [ [[package]] name = "iced_winit" version = "0.14.0" -source = "git+https://github.com/pop-os/libcosmic#9c2a86a8f4d6faff2fe28627820c29c5430d95d5" +source = "git+https://github.com/pop-os/libcosmic#f0f68933f1552857e2165fc0fa953228107bddef" dependencies = [ "cosmic-client-toolkit", "cursor-icon", @@ -3788,6 +3806,12 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f2f8aeca682d874a5247084aa4fb7d1cef9ba45d889c21209a8818dcaaa0ec9" +[[package]] +name = "id-arena" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" + [[package]] name = "ident_case" version = "1.0.1" @@ -3807,9 +3831,9 @@ dependencies = [ [[package]] name = "idna_adapter" -version = "1.2.1" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" +checksum = "cb68373c0d6620ef8105e855e7745e18b0d00d3bdb07fb532e434244cdb9a714" dependencies = [ "icu_normalizer", "icu_properties", @@ -3841,9 +3865,9 @@ dependencies = [ [[package]] name = "image-extras" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86d29ba92ef6970a2685cc758b455d190842b8b9e96c865ffd31cdb9954b7548" +checksum = "60d02eb2c9ccbbab470538fce34c7bc3be7b4e59268e65a3171367b296cdb842" dependencies = [ "image", ] @@ -3866,9 +3890,9 @@ checksum = "edcd27d72f2f071c64249075f42e205ff93c9a4c5f6c6da53e79ed9f9832c285" [[package]] name = "imgref" -version = "1.12.0" +version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7c5cedc30da3a610cac6b4ba17597bdf7152cf974e8aab3afb3d54455e371c8" +checksum = "40fac9d56ed6437b198fddba683305e8e2d651aa42647f00f5ae542e7f5c94a2" [[package]] name = "indenter" @@ -3894,7 +3918,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" dependencies = [ "equivalent", - "hashbrown 0.17.0", + "hashbrown 0.17.1", "serde", "serde_core", ] @@ -3910,11 +3934,11 @@ dependencies = [ [[package]] name = "inotify" -version = "0.11.1" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd5b3eaf1a28b758ac0faa5a4254e8ab2705605496f1b1f3fbbc3988ad73d199" +checksum = "533e68a5842e734946fe159fb03fc9bbbb254f590dd0d8ad321ae5ff7beca2c1" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", "inotify-sys", "libc", ] @@ -3940,11 +3964,11 @@ dependencies = [ [[package]] name = "input" -version = "0.9.1" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbdc09524a91f9cacd26f16734ff63d7dc650daffadd2b6f84d17a285bd875a9" +checksum = "f9793345a65d71317763a33066b5d8351f8760dde8d4930fe9e39b5f14a7959d" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", "input-sys", "libc", "log", @@ -4033,9 +4057,9 @@ checksum = "2ceaf4c6c48465bead8cb6a0b7c4ee0c86ecbb31239032b9c66ab9a08d2f3ee1" [[package]] name = "jiff" -version = "0.2.23" +version = "0.2.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a3546dc96b6d42c5f24902af9e2538e82e39ad350b0c766eb3fbf2d8f3d8359" +checksum = "4603d3033e49e2b0e31229fcab20a5d40089c607d975cd9c80551dc69eed9102" dependencies = [ "jiff-static", "jiff-tzdb-platform", @@ -4043,14 +4067,14 @@ dependencies = [ "portable-atomic", "portable-atomic-util", "serde_core", - "windows-sys 0.61.2", + "windows-link 0.2.1", ] [[package]] name = "jiff-static" -version = "0.2.23" +version = "0.2.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a8c8b344124222efd714b73bb41f8b5120b27a7cc1c75593a6ff768d9d05aa4" +checksum = "782d32378dddf207193ac91cefb848ad41abb58195c95168e1291227a0832b47" dependencies = [ "proc-macro2", "quote", @@ -4142,9 +4166,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.95" +version = "0.3.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2964e92d1d9dc3364cae4d718d93f227e3abb088e747d92e0395bfdedf1c12ca" +checksum = "142bc4740e452c1e57ade0cbc129f139c9093e354346f0872ef985f4f5cf5f11" dependencies = [ "cfg-if", "futures-util", @@ -4205,9 +4229,9 @@ dependencies = [ [[package]] name = "jxl-grid" -version = "0.6.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0e0ef92d5d60e76bf41098e57e985f523185e08fad54268da448637feca6989" +checksum = "01671307879a033bfa52e6e8784b941aca770b3f3a7d33830b455b6844f793fb" dependencies = [ "tracing", ] @@ -4244,9 +4268,9 @@ dependencies = [ [[package]] name = "jxl-modular" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da758b2f989aafd9eeb39489fe43d7be5a3a0d2ad61cf1bad705eb6990a6053c" +checksum = "2a2f045b24c738dd91d482be385512b512721ae08a671bd4b27bf1c47f215235" dependencies = [ "jxl-bitstream", "jxl-coding", @@ -4258,9 +4282,9 @@ dependencies = [ [[package]] name = "jxl-oxide" -version = "0.12.5" +version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee8ecd2678ed70c1eda42b811ccb2e25ab836edeb18e7f1178c1f917ed36b772" +checksum = "d36c662923f47586880211f3bc7c0d83fb3a9b410d278c7bde93450748abeef3" dependencies = [ "brotli-decompressor", "bytemuck", @@ -4288,9 +4312,9 @@ dependencies = [ [[package]] name = "jxl-render" -version = "0.12.3" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa0c3100918bd3c41bb0f8ce1f4f1664e48f3032ff8eeab0d6a2cfc3276f462d" +checksum = "d34386bfdb6a19b5a30cc9beb4d475d537422c31ae8c39bb69640fcce3fcaf19" dependencies = [ "bytemuck", "jxl-bitstream", @@ -4358,7 +4382,7 @@ version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fbe853b403ae61a04233030ae8a79d94975281ed9770a1f9e246732b534b28d" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", "serde", ] @@ -4390,9 +4414,9 @@ dependencies = [ [[package]] name = "kqueue" -version = "1.1.1" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eac30106d7dce88daf4a3fcb4879ea939476d5074a9b7ddd0fb97fa4bed5596a" +checksum = "273c0752728918e0ac4976f2b275b6fefb9ecd400585dec929419f3844cd87b5" dependencies = [ "kqueue-sys", "libc", @@ -4400,11 +4424,11 @@ dependencies = [ [[package]] name = "kqueue-sys" -version = "1.0.4" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed9625ffda8729b85e45cf04090035ac368927b8cebc34898e7c120f52e4838b" +checksum = "07293a4e297ac234359b510362495713f75ea345d5307140414f20c69ffeb087" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.12.1", "libc", ] @@ -4435,6 +4459,12 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +[[package]] +name = "leb128fmt" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" + [[package]] name = "lebe" version = "0.5.3" @@ -4450,7 +4480,7 @@ checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" [[package]] name = "libcosmic" version = "1.0.0" -source = "git+https://github.com/pop-os/libcosmic#9c2a86a8f4d6faff2fe28627820c29c5430d95d5" +source = "git+https://github.com/pop-os/libcosmic#f0f68933f1552857e2165fc0fa953228107bddef" dependencies = [ "apply", "ashpd 0.12.3", @@ -4491,7 +4521,7 @@ dependencies = [ "rust-embed", "rustix 1.1.4", "serde", - "shlex", + "shlex 1.3.0", "slotmap", "taffy", "thiserror 2.0.18", @@ -4504,9 +4534,9 @@ dependencies = [ [[package]] name = "libfuzzer-sys" -version = "0.4.12" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f12a681b7dd8ce12bff52488013ba614b869148d54dd79836ab85aafdd53f08d" +checksum = "a9fd2f41a1cba099f79a0b6b6c35656cf7c03351a7bae8ff0f28f25270f929d2" dependencies = [ "arbitrary", "cc", @@ -4530,14 +4560,14 @@ checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" [[package]] name = "libredox" -version = "0.1.16" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e02f3bb43d335493c96bf3fd3a321600bf6bd07ed34bc64118e9293bdffea46c" +checksum = "f02ab6bace2054fb888a3c16f990117b579d14a3088e472d63c6011fa185c9d3" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", "libc", "plain", - "redox_syscall 0.7.4", + "redox_syscall 0.8.1", ] [[package]] @@ -4638,9 +4668,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.29" +version = "0.4.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" +checksum = "953f07c43838f8e6f9758cab68bf5bed85465e7587ebe0b823f1bcd81978ad3a" [[package]] name = "loop9" @@ -4669,9 +4699,9 @@ dependencies = [ [[package]] name = "lyon_algorithms" -version = "1.0.19" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9815fac08e6fd96733a11dce4f9d15a3f338e96a2e2311ee21e1b738efc2bc0f" +checksum = "8575c0d003ae459399623c4def180c63b77f343b1a7fee64f249b349e7699a31" dependencies = [ "lyon_path", "num-traits", @@ -4750,9 +4780,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.8.0" +version = "2.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" +checksum = "6b947ae49db0d222b1dbc6b113ce7248a3fc3a6ca21b696717bfc000ba4484d8" [[package]] name = "memmap2" @@ -4787,7 +4817,7 @@ version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7047791b5bc903b8cd963014b355f71dc9864a9a0b727057676c1dcae5cbc15" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", "block", "core-graphics-types 0.2.0", "foreign-types", @@ -4832,9 +4862,9 @@ dependencies = [ [[package]] name = "mio" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50b7e5b27aa02a74bac8c3f23f448f8d87ff11f92d3aac1a6ed369ee08cc56c1" +checksum = "02bd0af71c67b473010cbbc60715ee815645a4dc942899111f494b4b737d6fda" dependencies = [ "libc", "log", @@ -4866,7 +4896,7 @@ checksum = "618f667225063219ddfc61251087db8a9aec3c3f0950c916b614e403486f1135" dependencies = [ "arrayvec", "bit-set", - "bitflags 2.11.1", + "bitflags 2.12.1", "cfg-if", "cfg_aliases 0.2.1", "codespan-reporting", @@ -4890,7 +4920,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", "jni-sys 0.3.1", "log", "ndk-sys", @@ -4930,9 +4960,9 @@ dependencies = [ [[package]] name = "no_std_io2" -version = "0.9.3" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b51ed7824b6e07d354605f4abb3d9d300350701299da96642ee084f5ce631550" +checksum = "418abd1b6d34fbf6cae440dc874771b0525a604428704c76e48b29a5e67b8003" dependencies = [ "memchr", ] @@ -4958,7 +4988,7 @@ version = "8.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4d3d07927151ff8575b7087f245456e549fea62edf0ec4e565a5ee50c8402bc3" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", "fsevent-sys", "inotify", "kqueue", @@ -4976,7 +5006,7 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42b8cfee0e339a0337359f3c88165702ac6e600dc01c0cc9579a92d62b08477a" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", ] [[package]] @@ -5032,9 +5062,9 @@ dependencies = [ [[package]] name = "num-conv" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6673768db2d862beb9b39a78fdcb1a69439615d5794a1be50caa9bc92c81967" +checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441" [[package]] name = "num-derive" @@ -5161,7 +5191,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e4e89ad9e3d7d297152b17d39ed92cd50ca8063a89a9fa569046d41568891eff" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", "block2 0.5.1", "libc", "objc2 0.5.2", @@ -5177,7 +5207,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d49e936b501e5c5bf01fda3a9452ff86dc3ea98ad5f283e1455153142d97518c" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", "block2 0.6.2", "objc2 0.6.4", "objc2-core-foundation", @@ -5190,7 +5220,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "617fbf49e071c178c0b24c080767db52958f716d9eabdf0890523aeae54773ef" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", "block2 0.5.1", "objc2 0.5.2", "objc2-foundation 0.2.2", @@ -5202,7 +5232,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", "block2 0.6.2", "dispatch2", "objc2 0.6.4", @@ -5214,7 +5244,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e022c9d066895efa1345f8e33e584b9f958da2fd4cd116792e15e07e4720a807" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", "libc", "objc2-core-foundation", ] @@ -5237,7 +5267,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d425caf1df73233f29fd8a5c3e5edbc30d2d4307870f802d18f00d83dc5141a6" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", "objc2-core-foundation", "objc2-core-graphics", ] @@ -5254,7 +5284,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", "block2 0.5.1", "libc", "objc2 0.5.2", @@ -5266,7 +5296,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", "block2 0.6.2", "objc2 0.6.4", "objc2-core-foundation", @@ -5288,7 +5318,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", "block2 0.5.1", "objc2 0.5.2", "objc2-foundation 0.2.2", @@ -5300,7 +5330,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", "block2 0.5.1", "objc2 0.5.2", "objc2-foundation 0.2.2", @@ -5313,7 +5343,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d87d638e33c06f577498cbcc50491496a3ed4246998a7fbba7ccb98b1e7eab22" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", "objc2 0.6.4", "objc2-core-foundation", "objc2-foundation 0.3.2", @@ -5363,9 +5393,9 @@ checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" [[package]] name = "orbclient" -version = "0.3.51" +version = "0.3.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59aed3b33578edcfa1bc96a321d590d31832b6ad55a26f0313362ce687e9abd6" +checksum = "5df339f526ea9a60e371768d50efc2f2508c7203290731565d1f7a6f71d21747" dependencies = [ "libc", "libredox", @@ -5529,7 +5559,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" dependencies = [ "phf_shared 0.11.3", - "rand 0.8.5", + "rand 0.8.6", ] [[package]] @@ -5596,18 +5626,18 @@ checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315" [[package]] name = "pin-project" -version = "1.1.11" +version = "1.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1749c7ed4bcaf4c3d0a3efc28538844fb29bcdd7d2b67b2be7e20ba861ff517" +checksum = "2466b2336ed02bcdca6b294417127b90ec92038d1d5c4fbeac971a922e0e0924" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.11" +version = "1.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b20ed30f105399776b9c883e68e536ef602a16ae6f596d2c473591d6ad64c6" +checksum = "c96395f0a926bc13b1c17622aaddda1ecb55d49c8f1bf9777e4d877800a43f8b" dependencies = [ "proc-macro2", "quote", @@ -5668,7 +5698,7 @@ version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60769b8b31b2a9f263dae2776c37b1b28ae246943cf719eb6946a1db05128a61" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", "crc32fast", "fdeflate", "flate2", @@ -5742,6 +5772,16 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8cf8e6a8aa66ce33f63993ffc4ea4271eb5b0530a9002db8455ea6050c77bfa" +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn 2.0.117", +] + [[package]] name = "proc-macro-crate" version = "3.5.0" @@ -5801,7 +5841,7 @@ version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25485360a54d6861439d60facef26de713b1e126bf015ec8f98239467a2b82f7" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", "procfs-core", "rustix 1.1.4", ] @@ -5812,24 +5852,24 @@ version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6401bf7b6af22f78b563665d15a22e9aef27775b79b149a66ca022468a4e405" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", "hex", ] [[package]] name = "profiling" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3eb8486b569e12e2c32ad3e204dbaba5e4b5b216e9367044f25f1dba42341773" +checksum = "3d595e54a326bc53c1c197b32d295e14b169e3cfeaa8dc82b529f947fba6bcf5" dependencies = [ "profiling-procmacros", ] [[package]] name = "profiling-procmacros" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52717f9a02b6965224f95ca2a81e2e0c5c43baacd28ca057577988930b6c3d5b" +checksum = "4488a4a36b9a4ba6b9334a32a39971f77c1436ec82c38707bce707699cc3bbcb" dependencies = [ "quote", "syn 2.0.117", @@ -5845,16 +5885,16 @@ dependencies = [ "byteorder", "hmac 0.10.1", "md-5", - "rand 0.8.5", + "rand 0.8.6", "sha-1", "sha2 0.9.9", ] [[package]] name = "pxfm" -version = "0.1.28" +version = "0.1.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5a041e753da8b807c9255f28de81879c78c876392ff2469cde94799b2896b9d" +checksum = "e0c5ccf5294c6ccd63a74f1565028353830a9c2f5eb0c682c355c471726a6e3f" [[package]] name = "qoi" @@ -5884,16 +5924,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b66c2058c55a409d601666cffe35f04333cf1013010882cec174a7467cd4e21c" dependencies = [ "memchr", - "serde", ] [[package]] name = "quick-xml" -version = "0.39.2" +version = "0.39.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "958f21e8e7ceb5a1aa7fa87fab28e7c75976e0bfe7e23ff069e0a260f894067d" +checksum = "cdcc8dd4e2f670d309a5f0e83fe36dfdc05af317008fea29144da1a2ac858e5e" dependencies = [ "memchr", + "serde", ] [[package]] @@ -5911,11 +5951,17 @@ version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" +[[package]] +name = "r-efi" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" + [[package]] name = "rand" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a" dependencies = [ "libc", "rand_chacha 0.3.1", @@ -6069,22 +6115,32 @@ dependencies = [ "font-types", ] +[[package]] +name = "redox_event" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c07d0d6d291e3a951bd847b1cd4af32fc6243d64116cf7702838c02797688b7" +dependencies = [ + "bitflags 2.12.1", + "libredox", +] + [[package]] name = "redox_syscall" version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", ] [[package]] name = "redox_syscall" -version = "0.7.4" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f450ad9c3b1da563fb6948a8e0fb0fb9269711c9c73d9ea1de5058c79c8d643a" +checksum = "5b44b894f2a6e36457d665d1e08c3866add6ed5e70050c1b4ba8a8ddedb02ce7" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", ] [[package]] @@ -6210,7 +6266,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db09040cc89e461f1a265139777a2bde7f8d8c67c4936f700c63ce3e2904d468" dependencies = [ "base64", - "bitflags 2.11.1", + "bitflags 2.12.1", "serde", "serde_derive", "unicode-ident", @@ -6222,7 +6278,7 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4147b952f3f819eca0e99527022f7d6a8d05f111aeb0a62960c74eb283bec8fc" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", "once_cell", "serde", "serde_derive", @@ -6303,7 +6359,7 @@ version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", "errno", "libc", "linux-raw-sys 0.4.15", @@ -6316,7 +6372,7 @@ version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", "errno", "libc", "linux-raw-sys 0.12.1", @@ -6335,7 +6391,7 @@ version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd3c7c96f8a08ee34eff8857b11b49b07d71d1c3f4e88f8a88d4c9e9f90b1702" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", "bytemuck", "core_maths", "log", @@ -6496,9 +6552,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.149" +version = "1.0.150" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" +checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" dependencies = [ "indexmap 2.14.0", "itoa", @@ -6521,11 +6577,12 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.18.0" +version = "3.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd5414fad8e6907dbdd5bc441a50ae8d6e26151a03b1de04d89a5576de61d01f" +checksum = "76a5c54c7310e7b8b9577c286d7e399ddd876c3e12b3ed917a8aabc4b96e9e8c" dependencies = [ "base64", + "bs58", "chrono", "hex", "indexmap 1.9.3", @@ -6540,9 +6597,9 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.18.0" +version = "3.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3db8978e608f1fe7357e211969fd9abdcae80bac1ba7a3369bb7eb6b404eb65" +checksum = "84d57bc0c8b9a17920c178daa6bb924850d54a9c97ab45194bb8c17ad66bb660" dependencies = [ "darling 0.23.0", "proc-macro2", @@ -6602,6 +6659,12 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +[[package]] +name = "shlex" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" + [[package]] name = "signal-hook-registry" version = "1.4.8" @@ -6654,9 +6717,9 @@ dependencies = [ [[package]] name = "siphasher" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2aa850e253778c88a04c3d7323b043aeda9d3e30d5971937c1855769763678e" +checksum = "8ee5873ec9cce0195efcb7a4e9507a04cd49aec9c83d0389df45b1ef7ba2e649" [[package]] name = "skrifa" @@ -6695,7 +6758,7 @@ version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0512da38f5e2b31201a93524adb8d3136276fa4fe4aafab4e1f727a82b534cc0" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", "bytemuck", "calloop", "calloop-wayland-source", @@ -6742,9 +6805,9 @@ dependencies = [ [[package]] name = "socket2" -version = "0.6.3" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" +checksum = "52d1cfed4120b4d927bf7c0f86d2087a4a7d6027c906d9f9d525a80573b9be51" dependencies = [ "libc", "windows-sys 0.61.2", @@ -6786,7 +6849,7 @@ version = "0.3.0+sdk-1.3.268.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eda41003dc44290527a59b13432d4a0379379fa074b70174882adfbdfd917844" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", ] [[package]] @@ -6967,7 +7030,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" dependencies = [ "fastrand", - "getrandom 0.3.4", + "getrandom 0.4.2", "once_cell", "rustix 1.1.4", "windows-sys 0.61.2", @@ -7112,12 +7175,12 @@ dependencies = [ [[package]] name = "tiny-xlib" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0324504befd01cab6e0c994f34b2ffa257849ee019d3fb3b64fb2c858887d89e" +checksum = "a90a0ca3ee6a69f2ad28fd11621a4c3f03b371f366be500b64df260c4ffbafb4" dependencies = [ "as-raw-xcb-connection", - "ctor-lite", + "ctor", "libloading", "pkg-config", "tracing", @@ -7151,9 +7214,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.52.0" +version = "1.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a91135f59b1cbf38c91e73cf3386fca9bb77915c45ce2771460c9d92f0f3d776" +checksum = "8fc7f01b389ac15039e4dc9531aa973a135d7a4135281b12d7c1bc79fd57fffe" dependencies = [ "bytes", "libc", @@ -7222,14 +7285,14 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.25.11+spec-1.1.0" +version = "0.25.12+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b59c4d22ed448339746c59b905d24568fcbb3ab65a500494f7b8c3e97739f2b" +checksum = "d2153edc6955a6c354fad8f5efd38b6a8769bdccf9fe50f8e1329f81b0baa5d7" dependencies = [ "indexmap 2.14.0", "toml_datetime", "toml_parser", - "winnow 1.0.1", + "winnow 1.0.3", ] [[package]] @@ -7238,7 +7301,7 @@ version = "1.1.2+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" dependencies = [ - "winnow 1.0.1", + "winnow 1.0.3", ] [[package]] @@ -7339,9 +7402,9 @@ checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c" [[package]] name = "typenum" -version = "1.19.0" +version = "1.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" +checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" [[package]] name = "udev" @@ -7444,9 +7507,9 @@ checksum = "383ad40bb927465ec0ce7720e033cb4ca06912855fc35db31b5755d0de75b1ee" [[package]] name = "unicode-segmentation" -version = "1.13.2" +version = "1.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9629274872b2bfaf8d66f5f15725007f635594914870f65218920345aa11aa8c" +checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8" [[package]] name = "unicode-vo" @@ -7466,6 +7529,12 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + [[package]] name = "upower_dbus" version = "0.3.2" @@ -7542,9 +7611,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.23.1" +version = "1.23.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddd74a9687298c6858e9b88ec8935ec45d22e8fd5e6394fa1bd4e99a87789c76" +checksum = "d258b83ceec21034727ecee8c382cfa6c3e133699b0742c64571814fb420c9f7" dependencies = [ "js-sys", "serde_core", @@ -7592,18 +7661,27 @@ checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasip2" -version = "1.0.2+wasi-0.2.9" +version = "1.0.3+wasi-0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20064672db26d7cdc89c7798c48a0fdfac8213434a1186e5ef29fd560ae223d6" +dependencies = [ + "wit-bindgen 0.57.1", +] + +[[package]] +name = "wasip3" +version = "0.4.0+wasi-0.3.0-rc-2026-01-06" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" +checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" dependencies = [ - "wit-bindgen", + "wit-bindgen 0.51.0", ] [[package]] name = "wasm-bindgen" -version = "0.2.118" +version = "0.2.122" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bf938a0bacb0469e83c1e148908bd7d5a6010354cf4fb73279b7447422e3a89" +checksum = "3ed04576f974d2b2fba0f38c51dbc5518011e38c36bf1143164be765528fd409" dependencies = [ "cfg-if", "once_cell", @@ -7614,9 +7692,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.68" +version = "0.4.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f371d383f2fb139252e0bfac3b81b265689bf45b6874af544ffa4c975ac1ebf8" +checksum = "9473dbd2991ae90b6291c3c32c30c6187ac49aa32f9905d1cce280ec1e110b0f" dependencies = [ "js-sys", "wasm-bindgen", @@ -7624,9 +7702,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.118" +version = "0.2.122" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eeff24f84126c0ec2db7a449f0c2ec963c6a49efe0698c4242929da037ca28ed" +checksum = "916151b09da36bd82f6615cbf3a419e2f0ba23a03c6160e8e92eb6bd4aa1dec6" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -7634,9 +7712,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.118" +version = "0.2.122" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d08065faf983b2b80a79fd87d8254c409281cf7de75fc4b773019824196c904" +checksum = "299047362ccbfce148b67ab7e73349f77748e00c8296f9542adfad2ad82c5c5e" dependencies = [ "bumpalo", "proc-macro2", @@ -7647,13 +7725,47 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.118" +version = "0.2.122" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fd04d9e306f1907bd13c6361b5c6bfc7b3b3c095ed3f8a9246390f8dbdee129" +checksum = "9a929b2c61f11ba3e9bc35b50c1f25cb38e0e892c0c231ae2b8cf78d5dad4437" dependencies = [ "unicode-ident", ] +[[package]] +name = "wasm-encoder" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" +dependencies = [ + "leb128fmt", + "wasmparser", +] + +[[package]] +name = "wasm-metadata" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" +dependencies = [ + "anyhow", + "indexmap 2.14.0", + "wasm-encoder", + "wasmparser", +] + +[[package]] +name = "wasmparser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" +dependencies = [ + "bitflags 2.12.1", + "hashbrown 0.15.5", + "indexmap 2.14.0", + "semver", +] + [[package]] name = "wasmtimer" version = "0.4.3" @@ -7688,7 +7800,7 @@ version = "0.31.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "645c7c96bb74690c3189b5c9cb4ca1627062bb23693a4fad9d8c3de958260144" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", "rustix 1.1.4", "wayland-backend", "wayland-scanner", @@ -7700,7 +7812,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "625c5029dbd43d25e6aa9615e88b829a5cad13b2819c4ae129fdbb7c31ab4c7e" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", "cursor-icon", "wayland-backend", ] @@ -7722,7 +7834,7 @@ version = "0.32.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "563a85523cade2429938e790815fd7319062103b9f4a2dc806e9b53b95982d8f" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", "wayland-backend", "wayland-client", "wayland-scanner", @@ -7735,7 +7847,7 @@ version = "20250721.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40a1f863128dcaaec790d7b4b396cc9b9a7a079e878e18c47e6c2d2c5a8dcbb1" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", "wayland-backend", "wayland-client", "wayland-protocols", @@ -7748,7 +7860,7 @@ version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e9567599ef23e09b8dad6e429e5738d4509dfc46b3b21f32841a304d16b29c8" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", "wayland-backend", "wayland-client", "wayland-protocols", @@ -7761,7 +7873,7 @@ version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b6d8cf1eb2c1c31ed1f5643c88a6e53538129d4af80030c8cabd1f9fa884d91" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", "wayland-backend", "wayland-client", "wayland-protocols", @@ -7774,7 +7886,7 @@ version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eb04e52f7836d7c7976c78ca0250d61e33873c34156a2a1fc9474828ec268234" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", "wayland-backend", "wayland-client", "wayland-protocols", @@ -7789,7 +7901,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c324a910fd86ebdc364a3e61ec1f11737d3b1d6c273c0239ee8ff4bc0d24b4a" dependencies = [ "proc-macro2", - "quick-xml 0.39.2", + "quick-xml 0.39.4", "quote", ] @@ -7799,7 +7911,7 @@ version = "0.31.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cc1846eb04c49182e04f4a099e2a830a2b745610bbc1d61246e206f29c7000a0" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", "downcast-rs 1.2.1", "rustix 1.1.4", "wayland-backend", @@ -7820,9 +7932,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.95" +version = "0.3.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f2dfbb17949fa2088e5d39408c48368947b86f7834484e87b73de55bc14d97d" +checksum = "6d621441cfc37b84979402712047321980c178f299193a3589d05b99e8763436" dependencies = [ "js-sys", "wasm-bindgen", @@ -7851,7 +7963,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f9cb534d5ffd109c7d1135f34cdae29e60eab94855a625dcfe1705f8bc7ad79f" dependencies = [ "arrayvec", - "bitflags 2.11.1", + "bitflags 2.12.1", "bytemuck", "cfg-if", "cfg_aliases 0.2.1", @@ -7883,7 +7995,7 @@ dependencies = [ "arrayvec", "bit-set", "bit-vec", - "bitflags 2.11.1", + "bitflags 2.12.1", "bytemuck", "cfg_aliases 0.2.1", "document-features", @@ -7943,7 +8055,7 @@ dependencies = [ "arrayvec", "ash", "bit-set", - "bitflags 2.11.1", + "bitflags 2.12.1", "block", "bytemuck", "cfg-if", @@ -7987,7 +8099,7 @@ version = "28.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e18308757e594ed2cd27dddbb16a139c42a683819d32a2e0b1b0167552f5840c" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", "bytemuck", "js-sys", "log", @@ -8482,9 +8594,9 @@ checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" [[package]] name = "winit" version = "0.31.0-beta.2" -source = "git+https://github.com/pop-os/winit.git?tag=cosmic-0.14#261cda54017f98a12dc55569c864430fe6770366" +source = "git+https://github.com/pop-os/winit.git?tag=cosmic-0.14#f737f1f5900b3399b56951305b0f155afc9c9ee5" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", "cfg_aliases 0.2.1", "cursor-icon", "dpi", @@ -8508,10 +8620,10 @@ dependencies = [ [[package]] name = "winit-android" version = "0.31.0-beta.2" -source = "git+https://github.com/pop-os/winit.git?tag=cosmic-0.14#261cda54017f98a12dc55569c864430fe6770366" +source = "git+https://github.com/pop-os/winit.git?tag=cosmic-0.14#f737f1f5900b3399b56951305b0f155afc9c9ee5" dependencies = [ "android-activity", - "bitflags 2.11.1", + "bitflags 2.12.1", "dpi", "ndk", "raw-window-handle", @@ -8523,9 +8635,9 @@ dependencies = [ [[package]] name = "winit-appkit" version = "0.31.0-beta.2" -source = "git+https://github.com/pop-os/winit.git?tag=cosmic-0.14#261cda54017f98a12dc55569c864430fe6770366" +source = "git+https://github.com/pop-os/winit.git?tag=cosmic-0.14#f737f1f5900b3399b56951305b0f155afc9c9ee5" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", "block2 0.6.2", "dispatch2", "dpi", @@ -8545,7 +8657,7 @@ dependencies = [ [[package]] name = "winit-common" version = "0.31.0-beta.2" -source = "git+https://github.com/pop-os/winit.git?tag=cosmic-0.14#261cda54017f98a12dc55569c864430fe6770366" +source = "git+https://github.com/pop-os/winit.git?tag=cosmic-0.14#f737f1f5900b3399b56951305b0f155afc9c9ee5" dependencies = [ "memmap2 0.9.10", "objc2 0.6.4", @@ -8560,9 +8672,9 @@ dependencies = [ [[package]] name = "winit-core" version = "0.31.0-beta.2" -source = "git+https://github.com/pop-os/winit.git?tag=cosmic-0.14#261cda54017f98a12dc55569c864430fe6770366" +source = "git+https://github.com/pop-os/winit.git?tag=cosmic-0.14#f737f1f5900b3399b56951305b0f155afc9c9ee5" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", "cursor-icon", "dpi", "keyboard-types", @@ -8574,14 +8686,14 @@ dependencies = [ [[package]] name = "winit-orbital" version = "0.31.0-beta.2" -source = "git+https://github.com/pop-os/winit.git?tag=cosmic-0.14#261cda54017f98a12dc55569c864430fe6770366" +source = "git+https://github.com/pop-os/winit.git?tag=cosmic-0.14#f737f1f5900b3399b56951305b0f155afc9c9ee5" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", "dpi", "libredox", "orbclient", "raw-window-handle", - "redox_syscall 0.7.4", + "redox_event", "smol_str", "tracing", "winit-core", @@ -8590,9 +8702,9 @@ dependencies = [ [[package]] name = "winit-uikit" version = "0.31.0-beta.2" -source = "git+https://github.com/pop-os/winit.git?tag=cosmic-0.14#261cda54017f98a12dc55569c864430fe6770366" +source = "git+https://github.com/pop-os/winit.git?tag=cosmic-0.14#f737f1f5900b3399b56951305b0f155afc9c9ee5" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", "block2 0.6.2", "dispatch2", "dpi", @@ -8610,10 +8722,10 @@ dependencies = [ [[package]] name = "winit-wayland" version = "0.31.0-beta.2" -source = "git+https://github.com/pop-os/winit.git?tag=cosmic-0.14#261cda54017f98a12dc55569c864430fe6770366" +source = "git+https://github.com/pop-os/winit.git?tag=cosmic-0.14#f737f1f5900b3399b56951305b0f155afc9c9ee5" dependencies = [ "ahash", - "bitflags 2.11.1", + "bitflags 2.12.1", "calloop", "cursor-icon", "dpi", @@ -8636,10 +8748,10 @@ dependencies = [ [[package]] name = "winit-web" version = "0.31.0-beta.2" -source = "git+https://github.com/pop-os/winit.git?tag=cosmic-0.14#261cda54017f98a12dc55569c864430fe6770366" +source = "git+https://github.com/pop-os/winit.git?tag=cosmic-0.14#f737f1f5900b3399b56951305b0f155afc9c9ee5" dependencies = [ "atomic-waker", - "bitflags 2.11.1", + "bitflags 2.12.1", "concurrent-queue", "cursor-icon", "dpi", @@ -8658,9 +8770,9 @@ dependencies = [ [[package]] name = "winit-win32" version = "0.31.0-beta.2" -source = "git+https://github.com/pop-os/winit.git?tag=cosmic-0.14#261cda54017f98a12dc55569c864430fe6770366" +source = "git+https://github.com/pop-os/winit.git?tag=cosmic-0.14#f737f1f5900b3399b56951305b0f155afc9c9ee5" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", "cursor-icon", "dpi", "raw-window-handle", @@ -8674,9 +8786,9 @@ dependencies = [ [[package]] name = "winit-x11" version = "0.31.0-beta.2" -source = "git+https://github.com/pop-os/winit.git?tag=cosmic-0.14#261cda54017f98a12dc55569c864430fe6770366" +source = "git+https://github.com/pop-os/winit.git?tag=cosmic-0.14#f737f1f5900b3399b56951305b0f155afc9c9ee5" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", "bytemuck", "calloop", "cursor-icon", @@ -8705,9 +8817,9 @@ dependencies = [ [[package]] name = "winnow" -version = "1.0.1" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09dac053f1cd375980747450bfc7250c264eaae0583872e845c0c7cd578872b5" +checksum = "0592e1c9d151f854e6fd382574c3a0855250e1d9b2f99d9281c6e6391af352f1" dependencies = [ "memchr", ] @@ -8717,6 +8829,94 @@ name = "wit-bindgen" version = "0.51.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" +dependencies = [ + "wit-bindgen-rust-macro", +] + +[[package]] +name = "wit-bindgen" +version = "0.57.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" + +[[package]] +name = "wit-bindgen-core" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" +dependencies = [ + "anyhow", + "heck 0.5.0", + "wit-parser", +] + +[[package]] +name = "wit-bindgen-rust" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" +dependencies = [ + "anyhow", + "heck 0.5.0", + "indexmap 2.14.0", + "prettyplease", + "syn 2.0.117", + "wasm-metadata", + "wit-bindgen-core", + "wit-component", +] + +[[package]] +name = "wit-bindgen-rust-macro" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" +dependencies = [ + "anyhow", + "prettyplease", + "proc-macro2", + "quote", + "syn 2.0.117", + "wit-bindgen-core", + "wit-bindgen-rust", +] + +[[package]] +name = "wit-component" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" +dependencies = [ + "anyhow", + "bitflags 2.12.1", + "indexmap 2.14.0", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder", + "wasm-metadata", + "wasmparser", + "wit-parser", +] + +[[package]] +name = "wit-parser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" +dependencies = [ + "anyhow", + "id-arena", + "indexmap 2.14.0", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser", +] [[package]] name = "write16" @@ -8781,7 +8981,7 @@ checksum = "2fb433233f2df9344722454bc7e96465c9d03bff9d77c248f9e7523fe79585b5" [[package]] name = "xdg-shell-wrapper-config" version = "0.1.0" -source = "git+https://github.com/pop-os/cosmic-panel#d518c7d25ef96f1a9696aa8cce008656bf66ece4" +source = "git+https://github.com/pop-os/cosmic-panel#546a6c45210574caed9a3e51446aa8ad622d3627" dependencies = [ "serde", "wayland-protocols-wlr", @@ -8848,7 +9048,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d039de8032a9a8856a6be89cea3e5d12fdd82306ab7c94d74e6deab2460651c5" dependencies = [ - "bitflags 2.11.1", + "bitflags 2.12.1", "dlib", "log", "once_cell", @@ -8866,9 +9066,9 @@ dependencies = [ [[package]] name = "xml" -version = "1.2.1" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8aa498d22c9bbaf482329839bc5620c46be275a19a812e9a22a2b07529a642a" +checksum = "636f85e5ca6488e96401b61eb7de54f4e44755c988af0f52cf90230c312a1a89" [[package]] name = "xml-rs" @@ -8911,9 +9111,9 @@ checksum = "e01738255b5a16e78bbb83e7fbba0a1e7dd506905cfc53f4622d89015a03fbb5" [[package]] name = "yoke" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abe8c5fda708d9ca3df187cae8bfb9ceda00dd96231bed36e445a1a48e66f9ca" +checksum = "709fe23a0424b6a435d82152b1bd3fdfb0833487d5fa90d05d42762a9891fef5" dependencies = [ "stable_deref_trait", "yoke-derive", @@ -8934,9 +9134,9 @@ dependencies = [ [[package]] name = "zbus" -version = "5.15.0" +version = "5.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3bcbf15c8708d7fc1be0c993622e0a5cbd5e8b52bfa40afa4c3e0cd8d724ac1" +checksum = "eee682d202a77e4a9f3b2c2bdf48a7b28af5c08c34ddf66f98c93e5e39464285" dependencies = [ "async-broadcast", "async-executor", @@ -8962,7 +9162,7 @@ dependencies = [ "uds_windows", "uuid", "windows-sys 0.61.2", - "winnow 1.0.1", + "winnow 1.0.3", "zbus_macros", "zbus_names", "zvariant", @@ -8994,9 +9194,9 @@ dependencies = [ [[package]] name = "zbus_macros" -version = "5.15.0" +version = "5.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51fa5406ad9175a8c825a931f8cf347116b531b3634fcb0b627c290f1f2516ff" +checksum = "adf1bd45a81a103745b1757754762a26e8cd01e4532e4d6c8ec431624b80d1d6" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -9014,7 +9214,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7074f3e50b894eac91750142016d30d0a89be8e67dbfd9704fb875825760e52d" dependencies = [ "serde", - "winnow 1.0.1", + "winnow 1.0.3", "zvariant", ] @@ -9033,11 +9233,11 @@ dependencies = [ [[package]] name = "zbus_xml" -version = "5.1.0" +version = "5.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "441a0064125265655bccc3a6af6bef56814d9277ac83fce48b1cd7e160b80eac" +checksum = "a8067892e940ed1727dea64690378601603b31d62dfde019a5335fbb7c0e0ed9" dependencies = [ - "quick-xml 0.38.4", + "quick-xml 0.39.4", "serde", "zbus_names", "zvariant", @@ -9051,18 +9251,18 @@ checksum = "6df3dc4292935e51816d896edcd52aa30bc297907c26167fec31e2b0c6a32524" [[package]] name = "zerocopy" -version = "0.8.48" +version = "0.8.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eed437bf9d6692032087e337407a86f04cd8d6a16a37199ed57949d415bd68e9" +checksum = "3b065d4f0e55f82fae73202e189638116a87c55ab6b8e6c2721e13dd9d854ad1" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.48" +version = "0.8.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70e3cd084b1788766f53af483dd21f93881ff30d7320490ec3ef7526d203bad4" +checksum = "0b631b19d36a892ab55420c92dbc83ccd79274f25be714855d3074aa71cab639" dependencies = [ "proc-macro2", "quote", @@ -9071,9 +9271,9 @@ dependencies = [ [[package]] name = "zerofrom" -version = "0.1.7" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69faa1f2a1ea75661980b013019ed6687ed0e83d069bc1114e2cc74c6c04c4df" +checksum = "0ec05a11813ea801ff6d75110ad09cd0824ddba17dfe17128ea0d5f68e6c5272" dependencies = [ "zerofrom-derive", ] @@ -9245,24 +9445,24 @@ dependencies = [ [[package]] name = "zvariant" -version = "5.11.0" +version = "5.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c1567a6ec68df868cbbfde844cfc6d81649fe5109a62b116b19fabd53e618ee" +checksum = "a192a0bde63360d77a7523c833d4b4ce6070a927e2c53246e4c540b1a3e27be0" dependencies = [ "endi", "enumflags2", "serde", "url", - "winnow 1.0.1", + "winnow 1.0.3", "zvariant_derive", "zvariant_utils", ] [[package]] name = "zvariant_derive" -version = "5.11.0" +version = "5.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7d5b780599bbde114e39d9a0799577fad1ced5105d38515745f7b3099d8ceda" +checksum = "90bc6cde9c01c511074be97f7ccb6c19d0da89e3f8662e812e999dcfd4638737" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -9273,13 +9473,13 @@ dependencies = [ [[package]] name = "zvariant_utils" -version = "3.3.1" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d464f5733ffa07a3164d656f18533caace9d0638596721355d73256a410d691" +checksum = "1e8535915cfa75547e559d8c68e8139909a4aeee076831e4ef7fc59d8172c4d6" dependencies = [ "proc-macro2", "quote", "serde", "syn 2.0.117", - "winnow 1.0.1", + "winnow 1.0.3", ]