From a3fb55a2b89e2c41c8943573acfabba69542bc1f Mon Sep 17 00:00:00 2001 From: Michael Aaron Murphy Date: Tue, 11 Nov 2025 16:56:53 +0100 Subject: [PATCH] feat(audio): share cosmic-settings' new sound library --- Cargo.lock | 634 ++++++++----- Cargo.toml | 2 - cosmic-applet-a11y/Cargo.toml | 11 +- cosmic-applet-a11y/src/app.rs | 7 +- cosmic-applet-a11y/src/backend/wayland/mod.rs | 2 +- cosmic-applet-audio/Cargo.toml | 4 +- cosmic-applet-audio/src/lib.rs | 529 +++-------- cosmic-applet-audio/src/pulse.rs | 840 ------------------ cosmic-applet-battery/Cargo.toml | 10 +- cosmic-applet-battery/src/app.rs | 13 +- 10 files changed, 574 insertions(+), 1478 deletions(-) delete mode 100644 cosmic-applet-audio/src/pulse.rs diff --git a/Cargo.lock b/Cargo.lock index 9b160617..aeb698c6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -176,14 +176,20 @@ dependencies = [ [[package]] name = "annotate-snippets" -version = "0.9.2" +version = "0.11.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccaf7e9dfbb6ab22c82e473cd1a8a7bd313c19a5b7e40970f3d89ef5a5c9e81e" +checksum = "710e8eae58854cdc1790fcb56cca04d712a17be849eeb81da2a724bf4bae2bc4" dependencies = [ - "unicode-width", - "yansi-term", + "anstyle", + "unicode-width 0.2.2", ] +[[package]] +name = "anstyle" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" + [[package]] name = "anyhow" version = "1.0.100" @@ -327,16 +333,6 @@ dependencies = [ "slab", ] -[[package]] -name = "async-fn-stream" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e71711442f1016c768c259bec59300a10efe753bc3e686ec19e2c6a54a97c29b" -dependencies = [ - "futures-util", - "pin-project-lite", -] - [[package]] name = "async-io" version = "1.13.0" @@ -438,7 +434,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ "proc-macro2", "quote", - "syn 2.0.109", + "syn 2.0.111", ] [[package]] @@ -473,7 +469,7 @@ checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.109", + "syn 2.0.111", ] [[package]] @@ -549,7 +545,7 @@ dependencies = [ "derive_utils", "proc-macro2", "quote", - "syn 2.0.109", + "syn 2.0.111", ] [[package]] @@ -575,23 +571,21 @@ dependencies = [ [[package]] name = "bindgen" -version = "0.69.5" +version = "0.72.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088" +checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895" dependencies = [ "annotate-snippets", "bitflags 2.10.0", "cexpr", "clang-sys", - "itertools 0.12.1", - "lazy_static", - "lazycell", + "itertools 0.13.0", "proc-macro2", "quote", "regex", - "rustc-hash 1.1.0", + "rustc-hash 2.1.1", "shlex", - "syn 2.0.109", + "syn 2.0.111", ] [[package]] @@ -739,7 +733,7 @@ checksum = "f9abbd1bc6865053c427f7198e6af43bfdedc55ab791faed4fbd361d789575ff" dependencies = [ "proc-macro2", "quote", - "syn 2.0.109", + "syn 2.0.111", ] [[package]] @@ -756,9 +750,9 @@ checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" [[package]] name = "bytes" -version = "1.10.1" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" +checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3" [[package]] name = "calendrical_calculations" @@ -823,9 +817,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.45" +version = "1.2.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35900b6c8d709fb1d854671ae27aeaa9eec2f8b01b364e1619a40da3e6fe2afe" +checksum = "cd405d82c84ff7f35739f175f67d8b9fb7687a0e84ccdc78bd3568839827cf07" dependencies = [ "find-msvc-tools", "jobserver", @@ -845,14 +839,14 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" dependencies = [ - "nom", + "nom 7.1.3", ] [[package]] name = "cfg-expr" -version = "0.15.8" +version = "0.20.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d067ad48b8650848b989a59a86c6c36a995d02d2bf778d45c3c5d57bc2718f02" +checksum = "9acd0bdbbf4b2612d09f52ba61da432140cb10930354079d0d53fafc12968726" dependencies = [ "smallvec", "target-lexicon", @@ -986,7 +980,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" dependencies = [ "termcolor", - "unicode-width", + "unicode-width 0.1.14", ] [[package]] @@ -1047,9 +1041,9 @@ dependencies = [ [[package]] name = "convert_case" -version = "0.6.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" +checksum = "baaaa0ecca5b51987b9423ccdc971514dd8b0bb7b4060b983d3664dad3f1f89f" dependencies = [ "unicode-segmentation", ] @@ -1059,9 +1053,6 @@ name = "cookie-factory" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9885fa71e26b8ab7855e2ec7cae6e9b380edff76cd052e07c683a0319d51b3a2" -dependencies = [ - "futures", -] [[package]] name = "core-foundation" @@ -1155,7 +1146,8 @@ dependencies = [ "anyhow", "cosmic-client-toolkit", "cosmic-protocols", - "cosmic-settings-subscriptions", + "cosmic-settings-a11y-manager-subscription", + "cosmic-settings-accessibility-subscription", "cosmic-time", "i18n-embed", "i18n-embed-fl", @@ -1171,7 +1163,7 @@ dependencies = [ name = "cosmic-applet-audio" version = "0.1.1" dependencies = [ - "cosmic-settings-subscriptions", + "cosmic-settings-sound-subscription", "cosmic-time", "i18n-embed", "i18n-embed-fl", @@ -1194,7 +1186,8 @@ name = "cosmic-applet-battery" version = "0.1.0" dependencies = [ "anyhow", - "cosmic-settings-subscriptions", + "cosmic-settings-daemon-subscription", + "cosmic-settings-upower-subscription", "cosmic-time", "drm 0.14.1", "futures", @@ -1457,7 +1450,7 @@ dependencies = [ [[package]] name = "cosmic-config" version = "0.1.0" -source = "git+https://github.com/pop-os/libcosmic#2296e8e94dee2b8b2c2d658c72b96eccf37d566e" +source = "git+https://github.com/pop-os/libcosmic#639326fcc31a95a0c7a9a5bb56f1ed4d53530f26" dependencies = [ "atomicwrites", "cosmic-config-derive", @@ -1478,10 +1471,10 @@ dependencies = [ [[package]] name = "cosmic-config-derive" version = "0.1.0" -source = "git+https://github.com/pop-os/libcosmic#2296e8e94dee2b8b2c2d658c72b96eccf37d566e" +source = "git+https://github.com/pop-os/libcosmic#639326fcc31a95a0c7a9a5bb56f1ed4d53530f26" dependencies = [ "quote", - "syn 2.0.109", + "syn 2.0.111", ] [[package]] @@ -1508,7 +1501,7 @@ dependencies = [ [[package]] name = "cosmic-freedesktop-icons" version = "0.4.0" -source = "git+https://github.com/pop-os/freedesktop-icons#689c60d428f46dc59316eafa22297e196afa4b15" +source = "git+https://github.com/pop-os/freedesktop-icons#44edef967337dfacdb1b997a6b1b4c641c1b6e18" dependencies = [ "dirs", "ini_core", @@ -1521,7 +1514,7 @@ dependencies = [ [[package]] name = "cosmic-notifications-config" version = "0.1.0" -source = "git+https://github.com/pop-os/cosmic-notifications#c5e561e26461ca95c95dd2e44e55c67d5d532a91" +source = "git+https://github.com/pop-os/cosmic-notifications#592c6ace2a49857fa6610bd1a4ad4cfd899e6d68" dependencies = [ "cosmic-config", "serde", @@ -1530,7 +1523,7 @@ dependencies = [ [[package]] name = "cosmic-notifications-util" version = "0.1.0" -source = "git+https://github.com/pop-os/cosmic-notifications#c5e561e26461ca95c95dd2e44e55c67d5d532a91" +source = "git+https://github.com/pop-os/cosmic-notifications#592c6ace2a49857fa6610bd1a4ad4cfd899e6d68" dependencies = [ "fast_image_resize", "libcosmic", @@ -1556,7 +1549,7 @@ dependencies = [ [[package]] name = "cosmic-panel-config" version = "0.1.0" -source = "git+https://github.com/pop-os/cosmic-panel#231180bff8491f3628d4dd3e249173bd17c21d19" +source = "git+https://github.com/pop-os/cosmic-panel#bf62b3820529af678c260aca64463884ed45403c" dependencies = [ "anyhow", "cosmic-config", @@ -1567,6 +1560,20 @@ dependencies = [ "xdg-shell-wrapper-config", ] +[[package]] +name = "cosmic-pipewire" +version = "1.0.0-beta6" +source = "git+https://github.com/pop-os/cosmic-settings#2c9f60cd5f9525ed46f1c5f587b5c00295c8e1e4" +dependencies = [ + "intmap", + "libspa", + "libspa-sys", + "pipewire", + "serde", + "serde_json", + "tracing", +] + [[package]] name = "cosmic-protocols" version = "0.1.0" @@ -1581,10 +1588,37 @@ dependencies = [ "wayland-server", ] +[[package]] +name = "cosmic-settings-a11y-manager-subscription" +version = "1.0.0-beta6" +source = "git+https://github.com/pop-os/cosmic-settings#2c9f60cd5f9525ed46f1c5f587b5c00295c8e1e4" +dependencies = [ + "cosmic-protocols", + "iced_futures", + "num-derive", + "num-traits", + "smithay-client-toolkit 0.20.0", + "tokio", + "tracing", +] + +[[package]] +name = "cosmic-settings-accessibility-subscription" +version = "1.0.0-beta6" +source = "git+https://github.com/pop-os/cosmic-settings#2c9f60cd5f9525ed46f1c5f587b5c00295c8e1e4" +dependencies = [ + "cosmic-dbus-a11y", + "futures", + "iced_futures", + "tokio", + "tracing", + "zbus 5.12.0", +] + [[package]] name = "cosmic-settings-config" version = "0.1.0" -source = "git+https://github.com/pop-os/cosmic-settings-daemon#fbd4adede269681c07cd273f417f9296feabc26e" +source = "git+https://github.com/pop-os/cosmic-settings-daemon#d67de203ced04c5b8ec3486d93e0e61876a8ee91" dependencies = [ "cosmic-config", "ron", @@ -1603,28 +1637,45 @@ dependencies = [ ] [[package]] -name = "cosmic-settings-subscriptions" -version = "0.1.0" -source = "git+https://github.com/pop-os/cosmic-settings-subscriptions#f858ca0b6416a2b75d5f7fa513bc6fc43647d3f8" +name = "cosmic-settings-daemon-subscription" +version = "1.0.0-beta6" +source = "git+https://github.com/pop-os/cosmic-settings#2c9f60cd5f9525ed46f1c5f587b5c00295c8e1e4" dependencies = [ - "async-fn-stream", - "cosmic-dbus-a11y", - "cosmic-protocols", "futures", "iced_futures", - "itertools 0.14.0", - "libcosmic", - "libpulse-binding", "log", - "num-derive", - "num-traits", - "pipewire", - "rustix 1.1.2", - "smithay-client-toolkit 0.20.0", - "thiserror 2.0.17", "tokio", "tokio-stream", + "zbus 5.12.0", +] + +[[package]] +name = "cosmic-settings-sound-subscription" +version = "1.0.0-beta6" +source = "git+https://github.com/pop-os/cosmic-settings#2c9f60cd5f9525ed46f1c5f587b5c00295c8e1e4" +dependencies = [ + "cosmic-pipewire", + "crossbeam-queue", + "futures", + "intmap", + "libcosmic", + "log", + "numtoa", + "rustix 1.1.2", + "tokio", "tracing", +] + +[[package]] +name = "cosmic-settings-upower-subscription" +version = "1.0.0-beta6" +source = "git+https://github.com/pop-os/cosmic-settings#2c9f60cd5f9525ed46f1c5f587b5c00295c8e1e4" +dependencies = [ + "futures", + "iced_futures", + "log", + "tokio", + "tokio-stream", "upower_dbus", "zbus 5.12.0", ] @@ -1655,7 +1706,7 @@ dependencies = [ [[package]] name = "cosmic-theme" version = "0.1.0" -source = "git+https://github.com/pop-os/libcosmic#2296e8e94dee2b8b2c2d658c72b96eccf37d566e" +source = "git+https://github.com/pop-os/libcosmic#639326fcc31a95a0c7a9a5bb56f1ed4d53530f26" dependencies = [ "almost", "cosmic-config", @@ -1686,6 +1737,28 @@ dependencies = [ "libc", ] +[[package]] +name = "crabtime" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81605e11aed454fb3838bc408f091d17f2f6d31613fb897f561a45bc8fb98378" +dependencies = [ + "crabtime-internal", +] + +[[package]] +name = "crabtime-internal" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e49bfb43e6dfb9026c045fc1fd5cde79f4927ea6d1d89a2d346c55879e85900c" +dependencies = [ + "proc-macro2", + "quote", + "rustc_version", + "syn 2.0.111", + "toml 0.8.23", +] + [[package]] name = "crc32fast" version = "1.5.0" @@ -1695,6 +1768,15 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "crossbeam-queue" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f58bbc28f91df819d0aa2a2c00cd19754769c2fad90579b3592b1c9ba7a3115" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "crossbeam-utils" version = "0.8.21" @@ -1709,9 +1791,9 @@ checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" [[package]] name = "crypto-common" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" dependencies = [ "generic-array", "typenum", @@ -1763,7 +1845,7 @@ dependencies = [ "darling 0.20.11", "proc-macro2", "quote", - "syn 2.0.109", + "syn 2.0.111", "synstructure", ] @@ -1809,7 +1891,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.109", + "syn 2.0.111", ] [[package]] @@ -1823,7 +1905,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.109", + "syn 2.0.111", ] [[package]] @@ -1834,7 +1916,7 @@ checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" dependencies = [ "darling_core 0.20.11", "quote", - "syn 2.0.109", + "syn 2.0.111", ] [[package]] @@ -1845,7 +1927,7 @@ checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81" dependencies = [ "darling_core 0.21.3", "quote", - "syn 2.0.109", + "syn 2.0.111", ] [[package]] @@ -1869,9 +1951,9 @@ dependencies = [ [[package]] name = "dbus-crossroads" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a4c83437187544ba5142427746835061b330446ca8902eabd70e4afb8f76de0" +checksum = "64bff0bd181fba667660276c6b7ebdc50cff37ce593e7adf9e734f89c8f444e8" dependencies = [ "dbus", ] @@ -1926,7 +2008,7 @@ dependencies = [ "darling 0.20.11", "proc-macro2", "quote", - "syn 2.0.109", + "syn 2.0.111", ] [[package]] @@ -1936,7 +2018,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" dependencies = [ "derive_builder_core", - "syn 2.0.109", + "syn 2.0.111", ] [[package]] @@ -1948,7 +2030,7 @@ dependencies = [ "darling 0.20.11", "proc-macro2", "quote", - "syn 2.0.109", + "syn 2.0.111", ] [[package]] @@ -1959,7 +2041,7 @@ checksum = "ccfae181bab5ab6c5478b2ccb69e4c68a02f8c3ec72f6616bfec9dbc599d2ee0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.109", + "syn 2.0.111", ] [[package]] @@ -2019,7 +2101,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.109", + "syn 2.0.111", ] [[package]] @@ -2172,7 +2254,7 @@ checksum = "67c78a4d8fdf9953a5c9d458f9efe940fd97a0cab0941c075a813ac594733827" dependencies = [ "proc-macro2", "quote", - "syn 2.0.109", + "syn 2.0.111", ] [[package]] @@ -2307,9 +2389,9 @@ dependencies = [ [[package]] name = "find-msvc-tools" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52051878f80a721bb68ebfbc930e07b65ba72f2da88968ea5c06fd6ca3d3a127" +checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844" [[package]] name = "fixed_decimal" @@ -2406,9 +2488,9 @@ checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" [[package]] name = "font-types" -version = "0.10.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "511e2c18a516c666d27867d2f9821f76e7d591f762e9fc41dd6cc5c90fe54b0b" +checksum = "39a654f404bbcbd48ea58c617c2993ee91d1cb63727a37bf2323a4edeed1b8c5" dependencies = [ "bytemuck", ] @@ -2468,7 +2550,7 @@ checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" dependencies = [ "proc-macro2", "quote", - "syn 2.0.109", + "syn 2.0.111", ] [[package]] @@ -2595,7 +2677,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.109", + "syn 2.0.111", ] [[package]] @@ -2630,9 +2712,9 @@ dependencies = [ [[package]] name = "generic-array" -version = "0.14.9" +version = "0.14.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bb6743198531e02858aeaea5398fcc883e71851fcbcb5a2f773e2fb6cb1edf2" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", @@ -2799,9 +2881,9 @@ dependencies = [ [[package]] name = "grid" -version = "0.18.0" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12101ecc8225ea6d675bc70263074eab6169079621c2186fe0c66590b2df9681" +checksum = "f9e2d4c0a8296178d8802098410ca05d86b17a10bb5ab559b3fb404c1f948220" [[package]] name = "guillotiere" @@ -2854,9 +2936,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.16.0" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" [[package]] name = "hassle-rs" @@ -2959,7 +3041,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.109", + "syn 2.0.111", "unic-langid", ] @@ -2973,7 +3055,7 @@ dependencies = [ "i18n-config", "proc-macro2", "quote", - "syn 2.0.109", + "syn 2.0.111", ] [[package]] @@ -3003,7 +3085,7 @@ dependencies = [ [[package]] name = "iced" version = "0.14.0-dev" -source = "git+https://github.com/pop-os/libcosmic#2296e8e94dee2b8b2c2d658c72b96eccf37d566e" +source = "git+https://github.com/pop-os/libcosmic#639326fcc31a95a0c7a9a5bb56f1ed4d53530f26" dependencies = [ "dnd", "iced_accessibility", @@ -3021,7 +3103,7 @@ dependencies = [ [[package]] name = "iced_accessibility" version = "0.1.0" -source = "git+https://github.com/pop-os/libcosmic#2296e8e94dee2b8b2c2d658c72b96eccf37d566e" +source = "git+https://github.com/pop-os/libcosmic#639326fcc31a95a0c7a9a5bb56f1ed4d53530f26" dependencies = [ "accesskit", "accesskit_winit", @@ -3030,13 +3112,14 @@ dependencies = [ [[package]] name = "iced_core" version = "0.14.0-dev" -source = "git+https://github.com/pop-os/libcosmic#2296e8e94dee2b8b2c2d658c72b96eccf37d566e" +source = "git+https://github.com/pop-os/libcosmic#639326fcc31a95a0c7a9a5bb56f1ed4d53530f26" dependencies = [ "bitflags 2.10.0", "bytes", "cosmic-client-toolkit", "dnd", "glam", + "iced_accessibility", "log", "mime 0.1.0", "num-traits", @@ -3054,7 +3137,7 @@ dependencies = [ [[package]] name = "iced_futures" version = "0.14.0-dev" -source = "git+https://github.com/pop-os/libcosmic#2296e8e94dee2b8b2c2d658c72b96eccf37d566e" +source = "git+https://github.com/pop-os/libcosmic#639326fcc31a95a0c7a9a5bb56f1ed4d53530f26" dependencies = [ "futures", "iced_core", @@ -3080,7 +3163,7 @@ dependencies = [ [[package]] name = "iced_graphics" version = "0.14.0-dev" -source = "git+https://github.com/pop-os/libcosmic#2296e8e94dee2b8b2c2d658c72b96eccf37d566e" +source = "git+https://github.com/pop-os/libcosmic#639326fcc31a95a0c7a9a5bb56f1ed4d53530f26" dependencies = [ "bitflags 2.10.0", "bytemuck", @@ -3102,7 +3185,7 @@ dependencies = [ [[package]] name = "iced_renderer" version = "0.14.0-dev" -source = "git+https://github.com/pop-os/libcosmic#2296e8e94dee2b8b2c2d658c72b96eccf37d566e" +source = "git+https://github.com/pop-os/libcosmic#639326fcc31a95a0c7a9a5bb56f1ed4d53530f26" dependencies = [ "iced_graphics", "iced_tiny_skia", @@ -3114,11 +3197,12 @@ dependencies = [ [[package]] name = "iced_runtime" version = "0.14.0-dev" -source = "git+https://github.com/pop-os/libcosmic#2296e8e94dee2b8b2c2d658c72b96eccf37d566e" +source = "git+https://github.com/pop-os/libcosmic#639326fcc31a95a0c7a9a5bb56f1ed4d53530f26" dependencies = [ "bytes", "cosmic-client-toolkit", "dnd", + "iced_accessibility", "iced_core", "iced_futures", "raw-window-handle", @@ -3129,7 +3213,7 @@ dependencies = [ [[package]] name = "iced_tiny_skia" version = "0.14.0-dev" -source = "git+https://github.com/pop-os/libcosmic#2296e8e94dee2b8b2c2d658c72b96eccf37d566e" +source = "git+https://github.com/pop-os/libcosmic#639326fcc31a95a0c7a9a5bb56f1ed4d53530f26" dependencies = [ "bytemuck", "cosmic-text", @@ -3145,7 +3229,7 @@ dependencies = [ [[package]] name = "iced_wgpu" version = "0.14.0-dev" -source = "git+https://github.com/pop-os/libcosmic#2296e8e94dee2b8b2c2d658c72b96eccf37d566e" +source = "git+https://github.com/pop-os/libcosmic#639326fcc31a95a0c7a9a5bb56f1ed4d53530f26" dependencies = [ "as-raw-xcb-connection", "bitflags 2.10.0", @@ -3176,10 +3260,11 @@ dependencies = [ [[package]] name = "iced_widget" version = "0.14.0-dev" -source = "git+https://github.com/pop-os/libcosmic#2296e8e94dee2b8b2c2d658c72b96eccf37d566e" +source = "git+https://github.com/pop-os/libcosmic#639326fcc31a95a0c7a9a5bb56f1ed4d53530f26" dependencies = [ "cosmic-client-toolkit", "dnd", + "iced_accessibility", "iced_renderer", "iced_runtime", "log", @@ -3195,10 +3280,11 @@ dependencies = [ [[package]] name = "iced_winit" version = "0.14.0-dev" -source = "git+https://github.com/pop-os/libcosmic#2296e8e94dee2b8b2c2d658c72b96eccf37d566e" +source = "git+https://github.com/pop-os/libcosmic#639326fcc31a95a0c7a9a5bb56f1ed4d53530f26" dependencies = [ "cosmic-client-toolkit", "dnd", + "iced_accessibility", "iced_futures", "iced_graphics", "iced_runtime", @@ -3645,9 +3731,9 @@ dependencies = [ [[package]] name = "image" -version = "0.25.8" +version = "0.25.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "529feb3e6769d234375c4cf1ee2ce713682b8e76538cb13f9fc23e1400a591e7" +checksum = "e6506c6c10786659413faa717ceebcb8f70731c0a60cbae39795fdf114519c1a" dependencies = [ "bytemuck", "byteorder-lite", @@ -3686,12 +3772,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.12.0" +version = "2.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6717a8d2a5a929a1a2eb43a12812498ed141a0bcfb7e8f7844fbdbe4303bba9f" +checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2" dependencies = [ "equivalent", - "hashbrown 0.16.0", + "hashbrown 0.16.1", "serde", "serde_core", ] @@ -3772,6 +3858,12 @@ dependencies = [ "unic-langid", ] +[[package]] +name = "intmap" +version = "3.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2e611826a1868311677fdcdfbec9e8621d104c732d080f546a854530232f0ee" + [[package]] name = "io-lifetimes" version = "1.0.11" @@ -3785,9 +3877,9 @@ dependencies = [ [[package]] name = "itertools" -version = "0.12.1" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" dependencies = [ "either", ] @@ -3943,12 +4035,6 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" -[[package]] -name = "lazycell" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" - [[package]] name = "libc" version = "0.2.177" @@ -3958,7 +4044,7 @@ checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" [[package]] name = "libcosmic" version = "0.1.0" -source = "git+https://github.com/pop-os/libcosmic#2296e8e94dee2b8b2c2d658c72b96eccf37d566e" +source = "git+https://github.com/pop-os/libcosmic#639326fcc31a95a0c7a9a5bb56f1ed4d53530f26" dependencies = [ "apply", "ashpd 0.12.0", @@ -3971,6 +4057,7 @@ dependencies = [ "cosmic-settings-config", "cosmic-settings-daemon", "cosmic-theme", + "crabtime", "css-color", "derive_setters", "freedesktop-desktop-entry", @@ -3978,6 +4065,7 @@ dependencies = [ "i18n-embed", "i18n-embed-fl", "iced", + "iced_accessibility", "iced_core", "iced_futures", "iced_renderer", @@ -3987,8 +4075,10 @@ dependencies = [ "iced_winit", "image", "libc", + "log", "mime 0.3.17", "palette", + "phf 0.13.1", "raw-window-handle", "rfd", "ron", @@ -4071,9 +4161,9 @@ dependencies = [ [[package]] name = "libspa" -version = "0.8.0" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65f3a4b81b2a2d8c7f300643676202debd1b7c929dbf5c9bb89402ea11d19810" +checksum = "b6b8cfa2a7656627b4c92c6b9ef929433acd673d5ab3708cda1b18478ac00df4" dependencies = [ "bitflags 2.10.0", "cc", @@ -4081,16 +4171,16 @@ dependencies = [ "cookie-factory", "libc", "libspa-sys", - "nix 0.27.1", - "nom", + "nix 0.30.1", + "nom 8.0.0", "system-deps", ] [[package]] name = "libspa-sys" -version = "0.8.0" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf0d9716420364790e85cbb9d3ac2c950bde16a7dd36f3209b7dfdfc4a24d01f" +checksum = "901049455d2eb6decf9058235d745237952f4804bc584c5fcb41412e6adcc6e0" dependencies = [ "bindgen", "cc", @@ -4215,9 +4305,9 @@ dependencies = [ [[package]] name = "lyon_geom" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e16770d760c7848b0c1c2d209101e408207a65168109509f8483837a36cf2e7" +checksum = "e260b6de923e6e47adfedf6243013a7a874684165a6a277594ee3906021b2343" dependencies = [ "arrayvec", "euclid", @@ -4409,7 +4499,7 @@ dependencies = [ "cfg_aliases 0.1.1", "codespan-reporting", "hexf-parse", - "indexmap 2.12.0", + "indexmap 2.12.1", "log", "rustc-hash 1.1.0", "spirv", @@ -4469,17 +4559,6 @@ dependencies = [ "memoffset 0.7.1", ] -[[package]] -name = "nix" -version = "0.27.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" -dependencies = [ - "bitflags 2.10.0", - "cfg-if", - "libc", -] - [[package]] name = "nix" version = "0.29.0" @@ -4515,6 +4594,15 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "nom" +version = "8.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df9761775871bdef83bee530e60050f7e54b1105350d6884eb0fb4f46c2f9405" +dependencies = [ + "memchr", +] + [[package]] name = "notify" version = "8.2.0" @@ -4572,7 +4660,7 @@ checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" dependencies = [ "proc-macro2", "quote", - "syn 2.0.109", + "syn 2.0.111", ] [[package]] @@ -4634,9 +4722,15 @@ dependencies = [ "proc-macro-crate 3.4.0", "proc-macro2", "quote", - "syn 2.0.109", + "syn 2.0.111", ] +[[package]] +name = "numtoa" +version = "1.0.0-alpha1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3f98606e662e333dada0fa9fb6723a3c363fb4a66b51e47ce964cfaf58833d2" + [[package]] name = "objc" version = "0.2.7" @@ -4964,7 +5058,7 @@ dependencies = [ "proc-macro2", "proc-macro2-diagnostics", "quote", - "syn 2.0.109", + "syn 2.0.111", ] [[package]] @@ -4998,7 +5092,7 @@ dependencies = [ "by_address", "proc-macro2", "quote", - "syn 2.0.109", + "syn 2.0.111", ] [[package]] @@ -5073,7 +5167,7 @@ version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" dependencies = [ - "phf_macros", + "phf_macros 0.11.3", "phf_shared 0.11.3", ] @@ -5086,6 +5180,17 @@ dependencies = [ "phf_shared 0.12.1", ] +[[package]] +name = "phf" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1562dc717473dbaa4c1f85a36410e03c047b2e7df7f45ee938fbef64ae7fadf" +dependencies = [ + "phf_macros 0.13.1", + "phf_shared 0.13.1", + "serde", +] + [[package]] name = "phf_generator" version = "0.11.3" @@ -5096,17 +5201,40 @@ dependencies = [ "rand 0.8.5", ] +[[package]] +name = "phf_generator" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "135ace3a761e564ec88c03a77317a7c6b80bb7f7135ef2544dbe054243b89737" +dependencies = [ + "fastrand 2.3.0", + "phf_shared 0.13.1", +] + [[package]] name = "phf_macros" version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f84ac04429c13a7ff43785d75ad27569f2951ce0ffd30a3321230db2fc727216" dependencies = [ - "phf_generator", + "phf_generator 0.11.3", "phf_shared 0.11.3", "proc-macro2", "quote", - "syn 2.0.109", + "syn 2.0.111", +] + +[[package]] +name = "phf_macros" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "812f032b54b1e759ccd5f8b6677695d5268c588701effba24601f6932f8269ef" +dependencies = [ + "phf_generator 0.13.1", + "phf_shared 0.13.1", + "proc-macro2", + "quote", + "syn 2.0.111", ] [[package]] @@ -5127,6 +5255,15 @@ dependencies = [ "siphasher", ] +[[package]] +name = "phf_shared" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e57fef6bc5981e38c2ce2d63bfa546861309f875b8a75f092d1d54ae2d64f266" +dependencies = [ + "siphasher", +] + [[package]] name = "pico-args" version = "0.5.0" @@ -5150,7 +5287,7 @@ checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" dependencies = [ "proc-macro2", "quote", - "syn 2.0.109", + "syn 2.0.111", ] [[package]] @@ -5178,26 +5315,26 @@ dependencies = [ [[package]] name = "pipewire" -version = "0.8.0" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08e645ba5c45109106d56610b3ee60eb13a6f2beb8b74f8dc8186cf261788dda" +checksum = "9688b89abf11d756499f7c6190711d6dbe5a3acdb30c8fbf001d6596d06a8d44" dependencies = [ "anyhow", "bitflags 2.10.0", "libc", "libspa", "libspa-sys", - "nix 0.27.1", + "nix 0.30.1", "once_cell", "pipewire-sys", - "thiserror 1.0.69", + "thiserror 2.0.17", ] [[package]] name = "pipewire-sys" -version = "0.8.0" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "849e188f90b1dda88fe2bfe1ad31fe5f158af2c98f80fb5d13726c44f3f01112" +checksum = "cb028afee0d6ca17020b090e3b8fa2d7de23305aef975c7e5192a5050246ea36" dependencies = [ "bindgen", "libspa-sys", @@ -5342,7 +5479,7 @@ dependencies = [ "proc-macro-error-attr2", "proc-macro2", "quote", - "syn 2.0.109", + "syn 2.0.111", ] [[package]] @@ -5362,7 +5499,7 @@ checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.109", + "syn 2.0.111", "version_check", "yansi", ] @@ -5494,9 +5631,9 @@ checksum = "c3d6831663a5098ea164f89cff59c6284e95f4e3c76ce9848d4529f5ccca9bde" [[package]] name = "rangemap" -version = "1.6.0" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f93e7e49bb0bf967717f7bd674458b3d6b0c5f48ec7e3038166026a69fc22223" +checksum = "acbbbbea733ec66275512d0b9694f34102e7d5406fdbe2ad8d21b28dce92887c" [[package]] name = "raw-window-handle" @@ -5561,7 +5698,7 @@ checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.109", + "syn 2.0.111", ] [[package]] @@ -5687,7 +5824,7 @@ dependencies = [ "proc-macro2", "quote", "rust-embed-utils", - "syn 2.0.109", + "syn 2.0.111", "walkdir", ] @@ -5713,6 +5850,15 @@ version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + [[package]] name = "rustix" version = "0.37.28" @@ -5845,6 +5991,12 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "16c2f82143577edb4921b71ede051dac62ca3c16084e918bf7b40c96ae10eb33" +[[package]] +name = "semver" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" + [[package]] name = "serde" version = "1.0.228" @@ -5884,7 +6036,7 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn 2.0.109", + "syn 2.0.111", ] [[package]] @@ -5893,7 +6045,7 @@ version = "1.0.145" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" dependencies = [ - "indexmap 2.12.0", + "indexmap 2.12.1", "itoa", "memchr", "ryu", @@ -5909,7 +6061,7 @@ checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.109", + "syn 2.0.111", ] [[package]] @@ -5922,16 +6074,25 @@ dependencies = [ ] [[package]] -name = "serde_with" -version = "3.15.1" +name = "serde_spanned" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa66c845eee442168b2c8134fec70ac50dc20e760769c8ba0ad1319ca1959b04" +checksum = "e24345aa0fe688594e73770a5f6d1b216508b4f93484c0026d521acd30134392" +dependencies = [ + "serde_core", +] + +[[package]] +name = "serde_with" +version = "3.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10574371d41b0d9b2cff89418eda27da52bcaff2cc8741db26382a77c29131f1" dependencies = [ "base64", "chrono", "hex", "indexmap 1.9.3", - "indexmap 2.12.0", + "indexmap 2.12.1", "schemars 0.9.0", "schemars 1.1.0", "serde_core", @@ -5942,14 +6103,14 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.15.1" +version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b91a903660542fced4e99881aa481bdbaec1634568ee02e0b8bd57c64cb38955" +checksum = "08a72d8216842fdd57820dc78d840bef99248e35fb2554ff923319e60f2d686b" dependencies = [ "darling 0.21.3", "proc-macro2", "quote", - "syn 2.0.109", + "syn 2.0.111", ] [[package]] @@ -5991,9 +6152,9 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "signal-hook-registry" -version = "1.4.6" +version = "1.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b" +checksum = "7664a098b8e616bdfcc2dc0e9ac44eb231eedf41db4e9fe95d8d32ec728dedad" dependencies = [ "libc", ] @@ -6230,7 +6391,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.109", + "syn 2.0.111", ] [[package]] @@ -6281,9 +6442,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.109" +version = "2.0.111" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f17c7e013e88258aa9543dcbe81aca68a667a9ac37cd69c9fbc07858bfe0e2f" +checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87" dependencies = [ "proc-macro2", "quote", @@ -6298,7 +6459,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.109", + "syn 2.0.111", ] [[package]] @@ -6312,22 +6473,22 @@ dependencies = [ [[package]] name = "system-deps" -version = "6.2.2" +version = "7.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3e535eb8dded36d55ec13eddacd30dec501792ff23a0b1682c38601b8cf2349" +checksum = "48c8f33736f986f16d69b6cb8b03f55ddcad5c41acc4ccc39dd88e84aa805e7f" dependencies = [ "cfg-expr", "heck 0.5.0", "pkg-config", - "toml 0.8.23", + "toml 0.9.8", "version-compare", ] [[package]] name = "taffy" -version = "0.9.1" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b25026fb8cc9ab51ab9fdabe5d11706796966f6d1c78e19871ef63be2b8f0644" +checksum = "41ba83ebaf2954d31d05d67340fd46cebe99da2b7133b0dd68d70c65473a437b" dependencies = [ "arrayvec", "grid", @@ -6337,9 +6498,9 @@ dependencies = [ [[package]] name = "target-lexicon" -version = "0.12.16" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" +checksum = "df7f62577c25e07834649fc3b39fafdc597c0a3527dc1c60129201ccfcbaa50c" [[package]] name = "temp-dir" @@ -6395,7 +6556,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.109", + "syn 2.0.111", ] [[package]] @@ -6406,7 +6567,7 @@ checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" dependencies = [ "proc-macro2", "quote", - "syn 2.0.109", + "syn 2.0.111", ] [[package]] @@ -6548,7 +6709,7 @@ checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.109", + "syn 2.0.111", ] [[package]] @@ -6578,11 +6739,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" dependencies = [ "serde", - "serde_spanned", + "serde_spanned 0.6.9", "toml_datetime 0.6.11", "toml_edit 0.22.27", ] +[[package]] +name = "toml" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0dc8b1fb61449e27716ec0e1bdf0f6b8f3e8f6b05391e8497b8b6d7804ea6d8" +dependencies = [ + "indexmap 2.12.1", + "serde_core", + "serde_spanned 1.0.3", + "toml_datetime 0.7.3", + "toml_parser", + "toml_writer", + "winnow 0.7.13", +] + [[package]] name = "toml_datetime" version = "0.6.11" @@ -6607,7 +6783,7 @@ version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ - "indexmap 2.12.0", + "indexmap 2.12.1", "toml_datetime 0.6.11", "winnow 0.5.40", ] @@ -6618,10 +6794,11 @@ version = "0.22.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" dependencies = [ - "indexmap 2.12.0", + "indexmap 2.12.1", "serde", - "serde_spanned", + "serde_spanned 0.6.9", "toml_datetime 0.6.11", + "toml_write", "winnow 0.7.13", ] @@ -6631,7 +6808,7 @@ version = "0.23.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6485ef6d0d9b5d0ec17244ff7eb05310113c3f316f2d14200d4de56b3cb98f8d" dependencies = [ - "indexmap 2.12.0", + "indexmap 2.12.1", "toml_datetime 0.7.3", "toml_parser", "winnow 0.7.13", @@ -6646,6 +6823,18 @@ dependencies = [ "winnow 0.7.13", ] +[[package]] +name = "toml_write" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" + +[[package]] +name = "toml_writer" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df8b2b54733674ad286d16267dcfc7a71ed5c776e4ac7aa3c3e2561f7c637bf2" + [[package]] name = "tracing" version = "0.1.41" @@ -6666,7 +6855,7 @@ checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" dependencies = [ "proc-macro2", "quote", - "syn 2.0.109", + "syn 2.0.111", ] [[package]] @@ -6846,6 +7035,12 @@ version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" +[[package]] +name = "unicode-width" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" + [[package]] name = "unicode-xid" version = "0.2.6" @@ -7025,7 +7220,7 @@ dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn 2.0.109", + "syn 2.0.111", "wasm-bindgen-shared", ] @@ -7225,9 +7420,9 @@ dependencies = [ [[package]] name = "weezl" -version = "0.1.10" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a751b3277700db47d3e574514de2eced5e54dc8a5436a3bf7a0b248b2cee16f3" +checksum = "a28ac98ddc8b9274cb41bb4d9d4d5c425b6020c50c46f25559911905610b4a88" [[package]] name = "wgpu" @@ -7265,7 +7460,7 @@ dependencies = [ "bitflags 2.10.0", "cfg_aliases 0.1.1", "document-features", - "indexmap 2.12.0", + "indexmap 2.12.1", "log", "naga", "once_cell", @@ -7449,7 +7644,7 @@ checksum = "942ac266be9249c84ca862f0a164a39533dc2f6f33dc98ec89c8da99b82ea0bd" dependencies = [ "proc-macro2", "quote", - "syn 2.0.109", + "syn 2.0.111", ] [[package]] @@ -7460,7 +7655,7 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", - "syn 2.0.109", + "syn 2.0.111", ] [[package]] @@ -7471,7 +7666,7 @@ checksum = "da33557140a288fae4e1d5f8873aaf9eb6613a9cf82c3e070223ff177f598b60" dependencies = [ "proc-macro2", "quote", - "syn 2.0.109", + "syn 2.0.111", ] [[package]] @@ -7482,7 +7677,7 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", - "syn 2.0.109", + "syn 2.0.111", ] [[package]] @@ -7963,7 +8158,7 @@ dependencies = [ [[package]] name = "xdg-shell-wrapper-config" version = "0.1.0" -source = "git+https://github.com/pop-os/cosmic-panel#231180bff8491f3628d4dd3e249173bd17c21d19" +source = "git+https://github.com/pop-os/cosmic-panel#bf62b3820529af678c260aca64463884ed45403c" dependencies = [ "serde", "wayland-protocols-wlr", @@ -8052,15 +8247,6 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" -[[package]] -name = "yansi-term" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe5c30ade05e61656247b2e334a031dfd0cc466fadef865bdcdea8d537951bf1" -dependencies = [ - "winapi", -] - [[package]] name = "yazi" version = "0.2.1" @@ -8086,7 +8272,7 @@ checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.109", + "syn 2.0.111", "synstructure", ] @@ -8184,7 +8370,7 @@ dependencies = [ "proc-macro-crate 3.4.0", "proc-macro2", "quote", - "syn 2.0.109", + "syn 2.0.111", "zbus_names 4.2.0", "zvariant 5.8.0", "zvariant_utils 3.2.1", @@ -8221,22 +8407,22 @@ checksum = "6df3dc4292935e51816d896edcd52aa30bc297907c26167fec31e2b0c6a32524" [[package]] name = "zerocopy" -version = "0.8.27" +version = "0.8.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c" +checksum = "4ea879c944afe8a2b25fef16bb4ba234f47c694565e97383b36f3a878219065c" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.27" +version = "0.8.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831" +checksum = "cf955aa904d6040f70dc8e9384444cb1030aed272ba3cb09bbc4ab9e7c1f34f5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.109", + "syn 2.0.111", ] [[package]] @@ -8256,7 +8442,7 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn 2.0.109", + "syn 2.0.111", "synstructure", ] @@ -8291,20 +8477,20 @@ checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.109", + "syn 2.0.111", ] [[package]] name = "zune-core" -version = "0.4.12" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a" +checksum = "111f7d9820f05fd715df3144e254d6fc02ee4088b0644c0ffd0efc9e6d9d2773" [[package]] name = "zune-jpeg" -version = "0.4.21" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29ce2c8a9384ad323cf564b67da86e21d3cfdff87908bc1223ed5c99bc792713" +checksum = "dc6fb7703e32e9a07fb3f757360338b3a567a5054f21b5f52a666752e333d58e" dependencies = [ "zune-core", ] @@ -8360,7 +8546,7 @@ dependencies = [ "proc-macro-crate 3.4.0", "proc-macro2", "quote", - "syn 2.0.109", + "syn 2.0.111", "zvariant_utils 3.2.1", ] @@ -8384,6 +8570,6 @@ dependencies = [ "proc-macro2", "quote", "serde", - "syn 2.0.109", + "syn 2.0.111", "winnow 0.7.13", ] diff --git a/Cargo.toml b/Cargo.toml index 6aaf267b..23df9191 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,8 +30,6 @@ cosmic-applets-config = { path = "cosmic-applets-config" } cosmic-protocols = { git = "https://github.com/pop-os/cosmic-protocols", default-features = false, features = [ "client", ], rev = "d0e95be" } -cosmic-settings-subscriptions = { git = "https://github.com/pop-os/cosmic-settings-subscriptions" } - cosmic-time = { git = "https://github.com/pop-os/cosmic-time", default-features = false } # cosmic-time = { path = "../cosmic-time", default-features = false ] } diff --git a/cosmic-applet-a11y/Cargo.toml b/cosmic-applet-a11y/Cargo.toml index 48945b5b..b5ff2c2c 100644 --- a/cosmic-applet-a11y/Cargo.toml +++ b/cosmic-applet-a11y/Cargo.toml @@ -4,11 +4,6 @@ version = "0.1.0" edition = "2024" [dependencies] -# cosmic-dbus-a11y = { git = "https://github.com/pop-os/dbus-settings-bindings" } -cosmic-settings-subscriptions = { workspace = true, features = [ - "accessibility", - "cosmic_a11y_manager", -] } anyhow.workspace = true cctk.workspace = true cosmic-protocols.workspace = true @@ -21,3 +16,9 @@ tokio.workspace = true tracing-log.workspace = true tracing-subscriber.workspace = true tracing.workspace = true + +[dependencies.cosmic-settings-a11y-manager-subscription] +git = "https://github.com/pop-os/cosmic-settings" + +[dependencies.cosmic-settings-accessibility-subscription] +git = "https://github.com/pop-os/cosmic-settings" diff --git a/cosmic-applet-a11y/src/app.rs b/cosmic-applet-a11y/src/app.rs index e79f96fc..8e0ccc5c 100644 --- a/cosmic-applet-a11y/src/app.rs +++ b/cosmic-applet-a11y/src/app.rs @@ -24,10 +24,11 @@ use cosmic::{ theme::{self, CosmicTheme}, widget::{Column, divider, text}, }; -use cosmic_settings_subscriptions::{ - accessibility::{self, DBusRequest, DBusUpdate}, - cosmic_a11y_manager::{AccessibilityEvent, AccessibilityRequest, ColorFilter}, + +use cosmic_settings_a11y_manager_subscription::{ + self as cosmic_a11y_manager, AccessibilityEvent, AccessibilityRequest, ColorFilter, }; +use cosmic_settings_accessibility_subscription::{self as accessibility, DBusRequest, DBusUpdate}; use cosmic_time::{Instant, Timeline, anim, chain, id}; use std::sync::LazyLock; use tokio::sync::mpsc::UnboundedSender; diff --git a/cosmic-applet-a11y/src/backend/wayland/mod.rs b/cosmic-applet-a11y/src/backend/wayland/mod.rs index 67b48a96..f66cbb2d 100644 --- a/cosmic-applet-a11y/src/backend/wayland/mod.rs +++ b/cosmic-applet-a11y/src/backend/wayland/mod.rs @@ -9,7 +9,7 @@ use cosmic::iced::{ stream, }; use cosmic_protocols::a11y::v1::client::cosmic_a11y_manager_v1::Filter; -use cosmic_settings_subscriptions::cosmic_a11y_manager::{ +use cosmic_settings_a11y_manager_subscription::{ self as thread, AccessibilityEvent, AccessibilityRequest, }; use std::sync::LazyLock; diff --git a/cosmic-applet-audio/Cargo.toml b/cosmic-applet-audio/Cargo.toml index 91c5a900..bcd97cfb 100644 --- a/cosmic-applet-audio/Cargo.toml +++ b/cosmic-applet-audio/Cargo.toml @@ -5,7 +5,6 @@ edition = "2024" license = "GPL-3.0-only" [dependencies] -cosmic-settings-subscriptions.workspace = true cosmic-time.workspace = true i18n-embed-fl.workspace = true i18n-embed.workspace = true @@ -22,3 +21,6 @@ tracing.workspace = true url = "2" urlencoding = "2.1.3" zbus.workspace = true + +[dependencies.cosmic-settings-sound-subscription] +git = "https://github.com/pop-os/cosmic-settings" diff --git a/cosmic-applet-audio/src/lib.rs b/cosmic-applet-audio/src/lib.rs index ab9374b4..3adc7479 100644 --- a/cosmic-applet-audio/src/lib.rs +++ b/cosmic-applet-audio/src/lib.rs @@ -4,10 +4,7 @@ mod localize; mod mouse_area; -use std::sync::LazyLock; -use std::time::Duration; - -use crate::{localize::localize, pulse::DeviceInfo}; +use crate::localize::localize; use config::{AudioAppletConfig, amplification_sink, amplification_source}; use cosmic::{ Element, Renderer, Task, Theme, app, @@ -22,28 +19,22 @@ use cosmic::{ cosmic_theme::Spacing, iced::{ self, Alignment, Length, Subscription, + futures::StreamExt, widget::{self, column, row, slider}, window, }, surface, theme, - widget::{Column, Row, button, container, divider, horizontal_space, icon, text}, + widget::{Row, button, container, divider, horizontal_space, icon, text}, }; -use cosmic_settings_subscriptions::pulse as sub_pulse; +use cosmic_settings_sound_subscription as css; use cosmic_time::{Instant, Timeline, anim, chain, id}; use iced::platform_specific::shell::wayland::commands::popup::{destroy_popup, get_popup}; -use libpulse_binding::volume::Volume; use mpris_subscription::{MprisRequest, MprisUpdate}; use mpris2_zbus::player::PlaybackStatus; +use std::sync::LazyLock; mod config; mod mpris_subscription; -mod pulse; - -// Full, in this case, means 100%. -static FULL_VOLUME: f64 = Volume::NORMAL.0 as f64; - -// Max volume is 150% volume. -static MAX_VOLUME: f64 = FULL_VOLUME + (FULL_VOLUME * 0.5); static SHOW_MEDIA_CONTROLS: LazyLock = LazyLock::new(id::Toggler::unique); @@ -59,72 +50,57 @@ pub fn run() -> cosmic::iced::Result { #[derive(Default)] pub struct Audio { + /// For interfacing with libcosmic. core: cosmic::app::Core, - is_open: IsOpen, - output_volume: f64, - output_volume_debounce: bool, - output_volume_text: String, - output_amplification: bool, - input_volume: f64, - input_volume_debounce: bool, - input_volume_text: String, - input_amplification: bool, - current_output: Option, - current_input: Option, - outputs: Vec, - inputs: Vec, - pulse_state: PulseState, + /// Track the applet's popup window. popup: Option, + /// The model from cosmic-settings for managing pipewire devices. + model: css::Model, + /// Whether to expand the revealer of a source or sink device. + is_open: IsOpen, + /// Max slider volume for the sink device, as determined by the amplification property. + max_sink_volume: u32, + /// Max slider volume for the source device, as determined by the amplification property. + max_source_volume: u32, + /// Breakpoints for the sink volume slider. + sink_breakpoints: &'static [u32], + /// Breakpoitns for the source volume slider. + source_breakpoints: &'static [u32], + /// Track animations used by the revealers. timeline: Timeline, + /// Config file specific to this applet. config: AudioAppletConfig, + /// mpris player status player_status: Option, + /// Used to request an activation token for opening cosmic-settings. token_tx: Option>, - channels: Option, } impl Audio { - fn update_output(&mut self, output: Option) { - self.current_output = output; - - if let Some(device) = self.current_output.as_ref() { - self.output_volume = volume_to_percent(device.volume.avg()); - self.output_volume_text = format!("{}%", self.output_volume.round()); - } - } - fn output_icon_name(&self) -> &'static str { - let volume = self.output_volume; - let mute = self.current_output_mute(); - if mute || volume == 0. { + let volume = self.model.sink_volume; + let mute = self.model.sink_mute; + if mute || volume == 0 { "audio-volume-muted-symbolic" - } else if volume < 33. { + } else if volume < 33 { "audio-volume-low-symbolic" - } else if volume < 66. { + } else if volume < 66 { "audio-volume-medium-symbolic" - } else if volume <= 100. { + } else if volume <= 100 { "audio-volume-high-symbolic" } else { "audio-volume-overamplified-symbolic" } } - fn update_input(&mut self, input: Option) { - self.current_input = input; - - if let Some(device) = self.current_input.as_ref() { - self.input_volume = volume_to_percent(device.volume.avg()); - self.input_volume_text = format!("{}%", self.input_volume.round()); - } - } - fn input_icon_name(&self) -> &'static str { - let volume = self.input_volume; - let mute = self.current_input_mute(); - if mute || volume == 0. { + let volume = self.model.source_volume; + let mute = self.model.source_mute; + if mute || volume == 0 { "microphone-sensitivity-muted-symbolic" - } else if volume < 33. { + } else if volume < 33 { "microphone-sensitivity-low-symbolic" - } else if volume < 66. { + } else if volume < 66 { "microphone-sensitivity-medium-symbolic" } else { "microphone-sensitivity-high-symbolic" @@ -143,17 +119,14 @@ enum IsOpen { #[derive(Debug, Clone)] pub enum Message { Ignore, - ApplyOutputVolume, - ApplyInputVolume, - SetOutputVolume(f64), - SetInputVolume(f64), - SetOutputMute(bool), - SetInputMute(bool), + SetSinkVolume(u32), + SetSourceVolume(u32), + ToggleSinkMute, + ToggleSourceMute, + SetDefaultSink(usize), + SetDefaultSource(usize), OutputToggle, InputToggle, - OutputChanged(String), - InputChanged(String), - Pulse(pulse::Event), TogglePopup, CloseRequested(window::Id), ToggleMediaControlsInTopPanel(chain::Toggler, bool), @@ -163,7 +136,7 @@ pub enum Message { MprisRequest(MprisRequest), Token(TokenUpdate), OpenSettings, - PulseSub(sub_pulse::Event), + Subscription(css::Message), Surface(surface::Action), } @@ -267,14 +240,6 @@ impl Audio { } }) } - - fn current_output_mute(&self) -> bool { - self.current_output.as_ref().is_some_and(|o| o.mute) - } - - fn current_input_mute(&self) -> bool { - self.current_input.as_ref().is_some_and(|o| o.mute) - } } impl cosmic::Application for Audio { @@ -284,9 +249,15 @@ impl cosmic::Application for Audio { const APP_ID: &'static str = "com.system76.CosmicAppletAudio"; fn init(core: cosmic::app::Core, _flags: ()) -> (Self, app::Task) { + let mut model = css::Model::default(); + model.unplugged_text = "Unplugged".into(); + model.hd_audio_text = "HD Audio".into(); + model.usb_audio_text = "USB Audio".into(); + ( Self { core, + model, ..Default::default() }, Task::none(), @@ -313,15 +284,21 @@ impl cosmic::Application for Audio { if let Some(p) = self.popup.take() { return destroy_popup(p); } else { - if let Some(conn) = self.pulse_state.connection() { - conn.send(pulse::Message::UpdateConnection); - } let new_id = window::Id::unique(); self.popup.replace(new_id); self.timeline = Timeline::new(); - self.output_amplification = amplification_sink(); - self.input_amplification = amplification_source(); + (self.max_sink_volume, self.sink_breakpoints) = if amplification_sink() { + (150, &[100][..]) + } else { + (100, &[][..]) + }; + + (self.max_source_volume, self.source_breakpoints) = if amplification_source() { + (150, &[100][..]) + } else { + (100, &[][..]) + }; let popup_settings = self.core.applet.get_popup_settings( self.core.main_window_id().unwrap(), @@ -331,130 +308,14 @@ impl cosmic::Application for Audio { None, ); - if let Some(conn) = self.pulse_state.connection() { - conn.send(pulse::Message::GetDefaultSink); - conn.send(pulse::Message::GetDefaultSource); - conn.send(pulse::Message::GetSinks); - conn.send(pulse::Message::GetSources); - } - return get_popup(popup_settings); } } - Message::SetOutputVolume(vol) => { - if self.output_volume == vol { - return Task::none(); - } - self.output_volume = vol; - self.output_volume_text = format!("{}%", self.output_volume.round()); - - if self.output_volume_debounce { - return Task::none(); - } - - self.output_volume_debounce = true; - - return cosmic::task::future(async move { - tokio::time::sleep(Duration::from_millis(64)).await; - Message::ApplyOutputVolume - }); - } - Message::SetInputVolume(vol) => { - if self.input_volume == vol { - return Task::none(); - } - - self.input_volume = vol; - self.input_volume_text = format!("{}%", self.input_volume.round()); - - if self.input_volume_debounce { - return Task::none(); - } - - self.input_volume_debounce = true; - - return cosmic::task::future(async move { - tokio::time::sleep(Duration::from_millis(64)).await; - Message::ApplyInputVolume - }); - } - Message::ApplyOutputVolume => { - self.output_volume_debounce = false; - - if let Some(channel) = self.channels.as_mut() { - channel.set_volume(self.output_volume as f32 / 100.); - } - } - Message::ApplyInputVolume => { - self.input_volume_debounce = false; - - self.current_input.as_mut().map(|i| { - i.volume - .set(i.volume.len(), percent_to_volume(self.input_volume)) - }); - - if let PulseState::Connected(connection) = &mut self.pulse_state { - if let Some(device) = &self.current_input { - if let Some(name) = &device.name { - tracing::info!("increasing volume of {}", name); - connection.send(pulse::Message::SetSourceVolumeByName( - name.clone(), - device.volume, - )); - } - } - } - } - Message::SetOutputMute(mute) => { - if let Some(output) = self.current_output.as_mut() { - output.mute = mute; - } - if let PulseState::Connected(connection) = &mut self.pulse_state { - if let Some(device) = &self.current_output { - if let Some(name) = &device.name { - connection - .send(pulse::Message::SetSinkMuteByName(name.clone(), device.mute)); - } - } - } - } - Message::SetInputMute(mute) => { - if let Some(input) = self.current_input.as_mut() { - input.mute = mute; - } - if let PulseState::Connected(connection) = &mut self.pulse_state { - if let Some(device) = &self.current_input { - if let Some(name) = &device.name { - connection.send(pulse::Message::SetSourceMuteByName( - name.clone(), - device.mute, - )) - } - } - } - } - Message::OutputChanged(val) => { - if let Some(conn) = self.pulse_state.connection() { - if let Some(val) = self.outputs.iter().find(|o| o.name.as_ref() == Some(&val)) { - conn.send(pulse::Message::SetDefaultSink(val.clone())); - } - } - } - Message::InputChanged(val) => { - if let Some(conn) = self.pulse_state.connection() { - if let Some(val) = self.inputs.iter().find(|i| i.name.as_ref() == Some(&val)) { - conn.send(pulse::Message::SetDefaultSource(val.clone())); - } - } - } Message::OutputToggle => { self.is_open = if self.is_open == IsOpen::Output { IsOpen::None } else { - if let Some(conn) = self.pulse_state.connection() { - conn.send(pulse::Message::GetSinks); - } IsOpen::Output } } @@ -462,61 +323,48 @@ impl cosmic::Application for Audio { self.is_open = if self.is_open == IsOpen::Input { IsOpen::None } else { - if let Some(conn) = self.pulse_state.connection() { - conn.send(pulse::Message::GetSources); - } IsOpen::Input } } - Message::Pulse(event) => match event { - pulse::Event::Init(mut conn) => { - conn.send(pulse::Message::UpdateConnection); - self.pulse_state = PulseState::Disconnected(conn); - } - pulse::Event::Connected => { - self.pulse_state.connected(); + Message::Subscription(message) => { + return self + .model + .update(message) + .map(|message| Message::Subscription(message).into()); + } + + Message::SetDefaultSink(pos) => { + return self + .model + .set_default_sink(pos) + .map(|message| Message::Subscription(message).into()); + } + + Message::SetDefaultSource(pos) => { + return self + .model + .set_default_source(pos) + .map(|message| Message::Subscription(message).into()); + } + + Message::ToggleSinkMute => self.model.toggle_sink_mute(), + + Message::ToggleSourceMute => self.model.toggle_source_mute(), + + Message::SetSinkVolume(volume) => { + return self + .model + .set_sink_volume(volume) + .map(|message| Message::Subscription(message).into()); + } + + Message::SetSourceVolume(volume) => { + return self + .model + .set_source_volume(volume) + .map(|message| Message::Subscription(message).into()); + } - if let Some(conn) = self.pulse_state.connection() { - conn.send(pulse::Message::GetSinks); - conn.send(pulse::Message::GetSources); - conn.send(pulse::Message::GetDefaultSink); - conn.send(pulse::Message::GetDefaultSource); - } - } - pulse::Event::MessageReceived(msg) => { - match msg { - // This is where we match messages from the subscription to app state - pulse::Message::SetSinks(sinks) => self.outputs = sinks, - pulse::Message::SetSources(mut sources) => { - sources.retain(|source| { - !source.name.as_ref().is_some_and(|n| n.contains("monitor")) - }); - self.inputs = sources; - } - pulse::Message::SetDefaultSink(sink) => { - self.update_output(Some(sink)); - } - pulse::Message::SetDefaultSource(source) => { - self.update_input(Some(source)); - } - pulse::Message::Disconnected => { - panic!("Subscription error handling is bad. This should never happen.") - } - _ => { - tracing::trace!("Received misc message") - } - } - } - pulse::Event::Disconnected => { - self.pulse_state.disconnected(); - if let Some(mut conn) = self.pulse_state.connection().cloned() { - _ = tokio::spawn(async move { - tokio::time::sleep(tokio::time::Duration::from_secs(30)).await; - conn.send(pulse::Message::UpdateConnection); - }); - } - } - }, Message::ToggleMediaControlsInTopPanel(chain, enabled) => { self.timeline.set_chain(chain).start(); self.config.show_media_controls_in_top_panel = enabled; @@ -618,45 +466,6 @@ impl cosmic::Application for Audio { tokio::spawn(cosmic::process::spawn(cmd)); } }, - Message::PulseSub(event) => match event { - sub_pulse::Event::SinkVolume(value) => { - self.current_output.as_mut().map(|output| { - output - .volume - .set(output.volume.len(), percent_to_volume(value as f64)) - }); - - self.output_volume = value as f64; - self.output_volume_text = format!("{}%", self.output_volume.round()); - } - sub_pulse::Event::SinkMute(value) => { - if let Some(output) = self.current_output.as_mut() { - output.mute = value; - } - } - sub_pulse::Event::SourceVolume(value) => { - self.current_input.as_mut().map(|input| { - input - .volume - .set(input.volume.len(), percent_to_volume(value as f64)) - }); - - self.input_volume = value as f64; - self.input_volume_text = format!("{}%", self.input_volume.round()); - } - sub_pulse::Event::SourceMute(value) => { - if let Some(input) = self.current_input.as_mut() { - input.mute = value; - } - } - sub_pulse::Event::Channels(c) => { - self.channels = Some(c); - } - sub_pulse::Event::DefaultSink(_) => {} - sub_pulse::Event::DefaultSource(_) => {} - sub_pulse::Event::CardInfo(_) => {} - sub_pulse::Event::Balance(_) => {} - }, Message::Surface(a) => { return cosmic::task::message(cosmic::Action::Cosmic( cosmic::app::Action::Surface(a), @@ -669,7 +478,6 @@ impl cosmic::Application for Audio { fn subscription(&self) -> Subscription { Subscription::batch([ - pulse::connect().map(Message::Pulse), self.timeline .as_subscription() .map(|(_, now)| Message::Frame(now)), @@ -681,7 +489,7 @@ impl cosmic::Application for Audio { }), mpris_subscription::mpris_subscription(0).map(Message::Mpris), activation_token_subscription(0).map(Message::Token), - sub_pulse::subscription().map(Message::PulseSub), + Subscription::run(|| css::watch().map(Message::Subscription)), ]) } @@ -702,15 +510,9 @@ impl cosmic::Application for Audio { return Message::Ignore; } - let new_volume = (self.output_volume + (scroll_vector as f64)).clamp( - 0.0, - if self.output_amplification { - 150.0 - } else { - 100.0 - }, - ); - Message::SetOutputVolume(new_volume) + let new_volume = (self.model.sink_volume as f64 + (scroll_vector as f64)) + .clamp(0.0, self.max_sink_volume as f64); + Message::SetSinkVolume(new_volume as u32) }); let playback_buttons = (!self.core.applet.suggested_bounds.as_ref().is_some_and(|c| { @@ -760,33 +562,31 @@ impl cosmic::Application for Audio { space_xxs, space_s, .. } = theme::active().cosmic().spacing; - let audio_disabled = matches!(self.pulse_state, PulseState::Disconnected(_)); - let out_mute = self.current_output_mute(); - let in_mute = self.current_input_mute(); + let sink = self + .model + .active_sink() + .and_then(|pos| self.model.sinks().get(pos)); + let source = self + .model + .active_source() + .and_then(|pos| self.model.sources().get(pos)); - let mut audio_content = if audio_disabled { - column![padded_control( - text::title3(fl!("disconnected")) - .width(Length::Fill) - .align_x(Alignment::Center) - )] - } else { - let output_slider = if self.output_amplification { - slider(0.0..=150.0, self.output_volume, Message::SetOutputVolume) - .width(Length::FillPortion(5)) - .breakpoints(&[100.]) - } else { - slider(0.0..=100.0, self.output_volume, Message::SetOutputVolume) - .width(Length::FillPortion(5)) - }; - let input_slider = if self.input_amplification { - slider(0.0..=150.0, self.input_volume, Message::SetInputVolume) - .width(Length::FillPortion(5)) - .breakpoints(&[100.]) - } else { - slider(0.0..=100.0, self.input_volume, Message::SetInputVolume) - .width(Length::FillPortion(5)) - }; + let mut audio_content = { + let output_slider = slider( + 0..=self.max_sink_volume, + self.model.sink_volume, + Message::SetSinkVolume, + ) + .width(Length::FillPortion(5)) + .breakpoints(self.sink_breakpoints); + + let input_slider = slider( + 0..=self.max_source_volume, + self.model.source_volume, + Message::SetSourceVolume, + ) + .width(Length::FillPortion(5)) + .breakpoints(self.source_breakpoints); column![ padded_control( @@ -799,9 +599,9 @@ impl cosmic::Application for Audio { .class(cosmic::theme::Button::Icon) .icon_size(24) .line_height(24) - .on_press(Message::SetOutputMute(!out_mute)), + .on_press(Message::ToggleSinkMute), output_slider, - container(text(&self.output_volume_text).size(16)) + container(text(&self.model.sink_volume_text).size(16)) .width(Length::FillPortion(1)) .align_x(Alignment::End) ] @@ -818,9 +618,9 @@ impl cosmic::Application for Audio { .class(cosmic::theme::Button::Icon) .icon_size(24) .line_height(24) - .on_press(Message::SetInputMute(!in_mute)), + .on_press(Message::ToggleSourceMute), input_slider, - container(text(&self.input_volume_text).size(16)) + container(text(&self.model.source_volume_text).size(16)) .width(Length::FillPortion(1)) .align_x(Alignment::End) ] @@ -831,24 +631,24 @@ impl cosmic::Application for Audio { revealer( self.is_open == IsOpen::Output, fl!("output"), - match &self.current_output { - Some(output) => pretty_name(output.description.clone()), + match sink { + Some(sink) => sink.to_owned(), None => String::from("No device selected"), }, - self.outputs.as_slice(), + self.model.sinks(), Message::OutputToggle, - Message::OutputChanged, + Message::SetDefaultSink, ), revealer( self.is_open == IsOpen::Input, fl!("input"), - match &self.current_input { - Some(input) => pretty_name(input.description.clone()), + match source { + Some(source) => source.to_owned(), None => fl!("no-device"), }, - self.inputs.as_slice(), + self.model.sources(), Message::InputToggle, - Message::InputChanged, + Message::SetDefaultSource, ) ] .align_x(Alignment::Start) @@ -974,18 +774,12 @@ fn revealer( open: bool, title: String, selected: String, - device_info: &[DeviceInfo], + devices: &[String], toggle: Message, - mut change: impl FnMut(String) -> Message + 'static, + mut change: impl FnMut(usize) -> Message + 'static, ) -> widget::Column<'static, Message, crate::Theme, Renderer> { if open { - let options = device_info.iter().map(|device| { - ( - device.name.clone().unwrap_or_default(), - pretty_name(device.description.clone()), - ) - }); - options.fold( + devices.iter().cloned().enumerate().fold( column![revealer_head(open, title, selected, toggle)].width(Length::Fill), |col, (id, name)| { col.push( @@ -1013,48 +807,3 @@ fn revealer_head( ]) .on_press(toggle) } - -fn pretty_name(name: Option) -> String { - match name { - Some(n) => n, - None => String::from("Generic"), - } -} - -#[derive(Default)] -enum PulseState { - #[default] - Init, - Disconnected(pulse::Connection), - Connected(pulse::Connection), -} - -impl PulseState { - fn connection(&mut self) -> Option<&mut pulse::Connection> { - match self { - Self::Disconnected(c) => Some(c), - Self::Connected(c) => Some(c), - Self::Init => None, - } - } - - fn connected(&mut self) { - if let Self::Disconnected(c) = self { - *self = Self::Connected(c.clone()); - } - } - - fn disconnected(&mut self) { - if let Self::Connected(c) = self { - *self = Self::Disconnected(c.clone()); - } - } -} - -fn volume_to_percent(volume: Volume) -> f64 { - volume.0 as f64 * 100. / FULL_VOLUME -} - -fn percent_to_volume(percent: f64) -> Volume { - Volume((percent / 100. * FULL_VOLUME).clamp(0., MAX_VOLUME).round() as u32) -} diff --git a/cosmic-applet-audio/src/pulse.rs b/cosmic-applet-audio/src/pulse.rs deleted file mode 100644 index 97d45666..00000000 --- a/cosmic-applet-audio/src/pulse.rs +++ /dev/null @@ -1,840 +0,0 @@ -// Copyright 2023 System76 -// SPDX-License-Identifier: GPL-3.0-only - -use std::{cell::RefCell, mem, rc::Rc, sync::LazyLock, thread, time::Duration}; - -extern crate libpulse_binding as pulse; - -use cosmic::{ - iced::{self, Subscription, stream}, - iced_futures::futures::{self, SinkExt}, -}; - -use libpulse_binding::{ - callbacks::ListResult, - context::{ - Context, - introspect::{Introspector, SinkInfo, SourceInfo}, - }, - error::PAErr, - mainloop::standard::{IterateResult, Mainloop}, - proplist::Proplist, - volume::ChannelVolumes, -}; - -use tokio::sync::{Mutex, mpsc}; - -pub static FROM_PULSE: LazyLock, mpsc::Sender)>>> = - LazyLock::new(|| Mutex::new(None)); - -pub fn connect() -> iced::Subscription { - struct SomeWorker; - - Subscription::run_with_id( - std::any::TypeId::of::(), - stream::channel(50, move |mut output| async move { - let mut state = State::Connecting(0); - - loop { - state = start_listening(state, &mut output).await; - } - }), - ) -} - -async fn start_listening( - state: State, - output: &mut futures::channel::mpsc::Sender, -) -> State { - match state { - // Waiting for Connection to succeed - State::Connecting(mut disconnect_count) => { - let mut guard = FROM_PULSE.lock().await; - let (from_pulse, to_pulse) = { - if guard.is_none() { - let PulseHandle { - to_pulse, - from_pulse, - } = PulseHandle::new(); - _ = output.send(Event::Init(Connection(to_pulse.clone()))).await; - - *guard = Some((from_pulse, to_pulse)); - } - guard.as_mut().unwrap() - }; - to_pulse - .send(Message::UpdateConnection) - .await - .expect("Failed to request connection update"); - - match from_pulse.recv().await { - Some(Message::Connected) => { - disconnect_count = 0; - _ = output.send(Event::Connected).await; - State::Connected - } - Some(Message::Disconnected) => { - disconnect_count += 1; - _ = output.send(Event::Disconnected).await; - tokio::time::sleep(Duration::from_millis( - 2_usize - .saturating_pow(disconnect_count.try_into().unwrap_or(u32::MAX)) - .try_into() - .unwrap_or(u64::MAX), - )) - .await; - State::Connecting(1) - } - Some(m) => { - tracing::error!("Unexpected message: {:?}", m); - State::Connecting(1) - } - None => { - panic!("Pulse Sender dropped, something has gone wrong!"); - } - } - } - State::Connected => { - let mut guard = FROM_PULSE.lock().await; - let Some((from_pulse, _)) = guard.as_mut() else { - return State::Connecting(1); - }; - // This is where we match messages from the pulse server to pass to the gui - match from_pulse.recv().await { - Some(Message::SetSinks(sinks)) => { - _ = output - .send(Event::MessageReceived(Message::SetSinks(sinks))) - .await; - - State::Connected - } - Some(Message::SetSources(sources)) => { - _ = output - .send(Event::MessageReceived(Message::SetSources(sources))) - .await; - State::Connected - } - Some(Message::SetDefaultSink(sink)) => { - _ = output - .send(Event::MessageReceived(Message::SetDefaultSink(sink))) - .await; - State::Connected - } - Some(Message::SetDefaultSource(source)) => { - _ = output - .send(Event::MessageReceived(Message::SetDefaultSource(source))) - .await; - State::Connected - } - Some(Message::Disconnected) => { - _ = output.send(Event::Disconnected).await; - State::Connecting(1) - } - None => { - _ = output.send(Event::Disconnected).await; - State::Connecting(1) - } - _ => State::Connected, - } - } - } -} - -// #[derive(Debug)] -enum State { - Connecting(usize), - Connected, -} - -#[derive(Debug, Clone)] -pub enum Event { - Init(Connection), - Connected, - Disconnected, - MessageReceived(Message), -} - -#[derive(Debug, Clone)] -pub struct Connection(mpsc::Sender); - -impl Connection { - pub fn send(&mut self, message: Message) { - if let Err(e) = self.0.try_send(message) { - match e { - mpsc::error::TrySendError::Closed(_) => { - tracing::error!( - "Failed to send message: PulseAudio server communication closed" - ); - panic!(); - } - mpsc::error::TrySendError::Full(_) => { - tracing::warn!("Failed to send message to PulseAudio server: channel is full"); - } - } - } - } -} - -#[derive(Debug, Clone, PartialEq)] -pub enum Message { - Connected, - Disconnected, - GetSinks, - GetSources, - UpdateConnection, - SetSinks(Vec), - SetSources(Vec), - GetDefaultSink, - GetDefaultSource, - SetDefaultSink(DeviceInfo), - SetDefaultSource(DeviceInfo), - SetSinkVolumeByName(String, ChannelVolumes), - SetSourceVolumeByName(String, ChannelVolumes), - SetSinkMuteByName(String, bool), - SetSourceMuteByName(String, bool), -} - -struct PulseHandle { - to_pulse: tokio::sync::mpsc::Sender, - from_pulse: tokio::sync::mpsc::Receiver, -} - -impl PulseHandle { - // Create pulse server thread, and bidirectional comms - pub fn new() -> Self { - let (to_pulse, mut to_pulse_recv) = tokio::sync::mpsc::channel(50); - let (from_pulse_send, from_pulse) = tokio::sync::mpsc::channel(50); - - // this thread should complete by pushing a completed message, - // or fail message. This should never complete/fail without pushing - // a message. This lets the iced subscription go to sleep while init - // finishes. TLDR: be very careful with error handling - thread::spawn(move || { - let rt = tokio::runtime::Builder::new_current_thread() - .enable_all() - .build() - .unwrap(); - - // take `PulseServer` and handle reciver into async context - // to listen for messages that need to be passed to the pulseserver - // this lets us put the thread to sleep, but keep hold a single - // thread, because pulse audio's API is not multithreaded... at all - rt.block_on(async { - let mut server: Option = None; - - let mut msgs = Vec::new(); - - loop { - if let Some(msg) = to_pulse_recv.recv().await { - msgs.push(msg); - } - - // Consume any additional messages in the channel. - while let Ok(msg) = to_pulse_recv.try_recv() { - // Deduplicate volume change messages. - if matches!( - msg, - Message::SetSinkVolumeByName(..) | Message::SetSourceVolumeByName(..) - ) { - let last_msg = msgs.last_mut().unwrap(); // - if mem::discriminant(last_msg) == mem::discriminant(&msg) { - *last_msg = msg; - continue; - } - } - - msgs.push(msg); - } - - for msg in msgs.drain(..) { - match msg { - Message::GetDefaultSink => { - let Some(server) = server.as_mut() else { - continue; - }; - match server.get_default_sink() { - Ok(sink) => { - if let Err(err) = from_pulse_send - .send(Message::SetDefaultSink(sink)) - .await - { - tracing::error!("ERROR! {}", err); - } - } - Err(_) => Self::send_disconnected(&from_pulse_send).await, - } - } - Message::GetDefaultSource => { - let Some(server) = server.as_mut() else { - continue; - }; - match server.get_default_source() { - Ok(source) => { - if let Err(err) = from_pulse_send - .send(Message::SetDefaultSource(source)) - .await - { - tracing::error!("ERROR! {}", err); - } - } - Err(e) => { - tracing::error!("ERROR! {:?}", e); - Self::send_disconnected(&from_pulse_send).await; - } - } - } - Message::GetSinks => { - let Some(server) = server.as_mut() else { - continue; - }; - match server.get_sinks() { - Ok(sinks) => { - if let Err(err) = - from_pulse_send.send(Message::SetSinks(sinks)).await - { - tracing::error!("ERROR! {}", err); - } - } - Err(_) => Self::send_disconnected(&from_pulse_send).await, - } - } - Message::GetSources => { - let Some(server) = server.as_mut() else { - continue; - }; - match server.get_sources() { - Ok(sinks) => { - if let Err(err) = - from_pulse_send.send(Message::SetSources(sinks)).await - { - tracing::error!("ERROR! {}", err); - } - } - Err(_) => Self::send_disconnected(&from_pulse_send).await, - } - } - Message::SetSinkVolumeByName(name, channel_volumes) => { - let Some(server) = server.as_mut() else { - continue; - }; - server.set_sink_volume_by_name(&name, &channel_volumes); - } - Message::SetSourceVolumeByName(name, channel_volumes) => { - let Some(server) = server.as_mut() else { - continue; - }; - server.set_source_volume_by_name(&name, &channel_volumes); - } - Message::SetSinkMuteByName(name, mute) => { - let Some(server) = server.as_mut() else { - continue; - }; - - let op = - server.introspector.set_sink_mute_by_name(&name, mute, None); - server.wait_for_result(op).ok(); - } - Message::SetSourceMuteByName(name, mute) => { - let Some(server) = server.as_mut() else { - continue; - }; - - let op = server - .introspector - .set_source_mute_by_name(&name, mute, None); - server.wait_for_result(op).ok(); - } - Message::UpdateConnection => { - tracing::info!( - "Updating Connection, server exists: {:?}", - server.is_some() - ); - if let Some(mut cur_server) = server.take() { - if cur_server.get_server_info().is_err() { - tracing::warn!("got error, server must be disconnected..."); - Self::send_disconnected(&from_pulse_send).await; - } else { - tracing::info!("got server info, still connected..."); - server = Some(cur_server); - Self::send_connected(&from_pulse_send).await; - } - } else { - match PulseServer::connect().and_then(PulseServer::init) { - Ok(new_server) => { - tracing::info!("Connected to server"); - Self::send_connected(&from_pulse_send).await; - server = Some(new_server); - } - Err(err) => { - tracing::error!( - "Failed to connect to server: {:?}", - err - ); - Self::send_disconnected(&from_pulse_send).await; - } - } - } - } - Message::SetDefaultSink(device) => { - let Some(server) = server.as_mut() else { - continue; - }; - let Ok(default_sink) = server.get_default_sink() else { - continue; - }; - let to_move = server.get_sink_inputs(default_sink.index); - if let Some(name) = device.name.as_ref() { - if server.set_default_sink(name, to_move) { - if let Err(err) = from_pulse_send - .send(Message::SetDefaultSink(device)) - .await - { - tracing::error!("ERROR! {:?}", err); - }; - } - } - } - Message::SetDefaultSource(device) => { - let Some(server) = server.as_mut() else { - continue; - }; - let Ok(default_source) = server.get_default_source() else { - continue; - }; - let to_move = server.get_source_outputs(default_source.index); - if let Some(name) = device.name.as_ref() { - if server.set_default_source(name, to_move) { - if let Err(err) = from_pulse_send - .send(Message::SetDefaultSource(device)) - .await - { - tracing::error!("ERROR! {:?}", err); - } - } - } - } - _ => { - tracing::warn!("message doesn't match"); - } - } - } - } - }); - }); - Self { - to_pulse, - from_pulse, - } - } - - async fn send_disconnected(sender: &tokio::sync::mpsc::Sender) { - sender.send(Message::Disconnected).await.unwrap(); - } - - #[allow(dead_code)] - async fn send_connected(sender: &tokio::sync::mpsc::Sender) { - sender.send(Message::Connected).await.unwrap(); - } -} - -struct PulseServer { - mainloop: Rc>, - context: Rc>, - introspector: Introspector, -} - -#[derive(Clone, Debug)] -enum PulseServerError<'a> { - IterateErr(IterateResult), - ContextErr(pulse::context::State), - OperationErr(pulse::operation::State), - PAErr(PAErr), - Connect, - Misc(&'a str), -} - -// `PulseServer` code is heavily inspired by Dave Patrick Caberto's pulsectl-rs (SeaDve) -// https://crates.io/crates/pulsectl-rs -impl PulseServer { - // connect() requires init() to be run after - pub fn connect() -> Result> { - // TODO: fix app name, should be variable - let mut proplist = Proplist::new().unwrap(); - proplist - .set_str( - pulse::proplist::properties::APPLICATION_NAME, - "com.system76", - ) - .or(Err(PulseServerError::Connect))?; - - let mainloop = Rc::new(RefCell::new( - pulse::mainloop::standard::Mainloop::new().ok_or(PulseServerError::Connect)?, - )); - - let context = Rc::new(RefCell::new( - Context::new_with_proplist(&*mainloop.borrow(), "MainConn", &proplist) - .ok_or(PulseServerError::Connect)?, - )); - - let introspector = context.borrow_mut().introspect(); - - context - .borrow_mut() - .connect(None, pulse::context::FlagSet::NOFLAGS, None) - .map_err(PulseServerError::PAErr)?; - - Ok(Self { - mainloop, - context, - introspector, - }) - } - - // Wait for pulse audio connection to complete - pub fn init(self) -> Result> { - loop { - match self.mainloop.borrow_mut().iterate(false) { - IterateResult::Success(_) => {} - IterateResult::Err(e) => { - return Err(PulseServerError::IterateErr(IterateResult::Err(e))); - } - IterateResult::Quit(e) => { - return Err(PulseServerError::IterateErr(IterateResult::Quit(e))); - } - } - - match self.context.borrow().get_state() { - pulse::context::State::Ready => break, - pulse::context::State::Failed => { - return Err(PulseServerError::ContextErr(pulse::context::State::Failed)); - } - pulse::context::State::Terminated => { - return Err(PulseServerError::ContextErr( - pulse::context::State::Terminated, - )); - } - _ => {} - } - } - Ok(self) - } - - // Get a list of output devices - pub fn get_sinks(&self) -> Result, PulseServerError<'_>> { - let list: Rc>>> = Rc::new(RefCell::new(Some(Vec::new()))); - let list_ref = list.clone(); - - let operation = self.introspector.get_sink_info_list( - move |sink_list: ListResult<&pulse::context::introspect::SinkInfo>| { - if let ListResult::Item(item) = sink_list { - list_ref.borrow_mut().as_mut().unwrap().push(item.into()); - } - }, - ); - self.wait_for_result(operation).and_then(|()| { - list.borrow_mut().take().ok_or(PulseServerError::Misc( - "get_sinks(): failed to wait for operation", - )) - }) - } - - // Get a list of input devices - pub fn get_sources(&self) -> Result, PulseServerError<'_>> { - let list: Rc>>> = Rc::new(RefCell::new(Some(Vec::new()))); - let list_ref = list.clone(); - - let operation = self.introspector.get_source_info_list( - move |sink_list: ListResult<&pulse::context::introspect::SourceInfo>| { - if let ListResult::Item(item) = sink_list { - list_ref.borrow_mut().as_mut().unwrap().push(item.into()); - } - }, - ); - self.wait_for_result(operation).and_then(|()| { - list.borrow_mut().take().ok_or(PulseServerError::Misc( - "get_sources(): Failed to wait for operation", - )) - }) - } - - pub fn get_server_info(&mut self) -> Result> { - let info = Rc::new(RefCell::new(Some(None))); - let info_ref = info.clone(); - - let op = self.introspector.get_server_info(move |res| { - info_ref.borrow_mut().as_mut().unwrap().replace(res.into()); - }); - self.wait_for_result(op)?; - info.take() - .flatten() - .ok_or(PulseServerError::Misc("get_server_info(): failed")) - } - - fn set_default_sink(&mut self, sink: &str, to_move: Vec) -> bool { - let set_default_success = Rc::new(RefCell::new(false)); - let set_default_success_ref = set_default_success.clone(); - let op = self - .context - .borrow_mut() - .set_default_sink(sink, move |ret| { - *set_default_success.borrow_mut() = ret; - }); - self.wait_for_result(op).ok(); - if !set_default_success_ref.replace(true) { - return false; - } - - for index in to_move { - let move_success = Rc::new(RefCell::new(false)); - let op = self.introspector.move_sink_input_by_name( - index, - sink, - Some(Box::new(move |ret| { - *move_success.borrow_mut() = ret; - })), - ); - - self.wait_for_result(op).ok(); - } - // TODO handle errors - true - } - - fn set_default_source(&mut self, sink: &str, to_move: Vec) -> bool { - let set_default_success = Rc::new(RefCell::new(false)); - let set_default_success_ref = set_default_success.clone(); - let op = self - .context - .borrow_mut() - .set_default_source(sink, move |ret| { - *set_default_success.borrow_mut() = ret; - }); - self.wait_for_result(op).ok(); - - if !set_default_success_ref.replace(true) { - return false; - } - - for index in to_move { - let move_success = Rc::new(RefCell::new(false)); - let op = self.introspector.move_source_output_by_name( - index, - sink, - Some(Box::new(move |ret| { - *move_success.borrow_mut() = ret; - })), - ); - - self.wait_for_result(op).ok(); - } - - true - } - - fn get_default_sink(&mut self) -> Result> { - let server_info = self.get_server_info(); - match server_info { - Ok(info) => { - let name = &info.default_sink_name.unwrap_or_default(); - let device = Rc::new(RefCell::new(Some(None))); - let dev_ref = device.clone(); - let op = self.introspector.get_sink_info_by_name( - name, - move |sink_list: ListResult<&SinkInfo>| { - if let ListResult::Item(item) = sink_list { - dev_ref.borrow_mut().as_mut().unwrap().replace(item.into()); - } - }, - ); - self.wait_for_result(op)?; - let mut result = device.borrow_mut(); - result.take().unwrap().ok_or({ - PulseServerError::Misc("get_default_sink(): Error getting requested device") - }) - } - Err(_) => Err(PulseServerError::Misc("get_default_sink() failed")), - } - } - - fn get_default_source(&mut self) -> Result> { - let server_info = self.get_server_info(); - match server_info { - Ok(info) => { - let name = &info.default_source_name.unwrap_or_default(); - let device = Rc::new(RefCell::new(Some(None))); - let dev_ref = device.clone(); - let op = self.introspector.get_source_info_by_name( - name, - move |sink_list: ListResult<&SourceInfo>| { - if let ListResult::Item(item) = sink_list { - dev_ref.borrow_mut().as_mut().unwrap().replace(item.into()); - } - }, - ); - self.wait_for_result(op)?; - let mut result = device.borrow_mut(); - result.take().unwrap().ok_or({ - PulseServerError::Misc("get_default_source(): Error getting requested device") - }) - } - Err(_) => Err(PulseServerError::Misc("get_default_source() failed")), - } - } - - fn set_sink_volume_by_name(&mut self, name: &str, volume: &ChannelVolumes) { - let op = self - .introspector - .set_sink_mute_by_name(name, volume.is_muted(), None); - self.wait_for_result(op).ok(); - - let op = self - .introspector - .set_sink_volume_by_name(name, volume, None); - self.wait_for_result(op).ok(); - } - - fn set_source_volume_by_name(&mut self, name: &str, volume: &ChannelVolumes) { - let op = self - .introspector - .set_source_mute_by_name(name, volume.is_muted(), None); - let _ = self.wait_for_result(op); - - let op = self - .introspector - .set_source_volume_by_name(name, volume, None); - let _ = self.wait_for_result(op); - } - - fn get_source_outputs(&mut self, source: u32) -> Vec { - let result = Rc::new(RefCell::new(Vec::new())); - let result_ref = Rc::new(RefCell::new(Vec::new())); - let op = self.introspector.get_source_output_info_list(move |list| { - if let ListResult::Item(item) = list { - if source == item.source { - result.borrow_mut().push(item.index); - } - } - }); - let _ = self.wait_for_result(op); - result_ref.replace(Vec::new()) - } - - fn get_sink_inputs(&mut self, sink: u32) -> Vec { - let result = Rc::new(RefCell::new(Vec::new())); - let result_ref = Rc::new(RefCell::new(Vec::new())); - let op = self.introspector.get_sink_input_info_list(move |list| { - if let ListResult::Item(item) = list { - if sink == item.sink { - result.borrow_mut().push(item.index); - } - } - }); - let _ = self.wait_for_result(op); - result_ref.replace(Vec::new()) - } - - // after building an operation such as get_devices() we need to keep polling - // the pulse audio server to "wait" for the operation to complete - fn wait_for_result( - &self, - operation: pulse::operation::Operation, - ) -> Result<(), PulseServerError<'_>> { - // TODO: make this loop async. It is already in an async context, so - // we could make this thread sleep while waiting for the pulse server's - // response. - loop { - match self.mainloop.borrow_mut().iterate(false) { - IterateResult::Err(e) => { - return Err(PulseServerError::IterateErr(IterateResult::Err(e))); - } - IterateResult::Quit(e) => { - return Err(PulseServerError::IterateErr(IterateResult::Quit(e))); - } - IterateResult::Success(_) => {} - } - match operation.get_state() { - pulse::operation::State::Done => return Ok(()), - pulse::operation::State::Running => {} - pulse::operation::State::Cancelled => { - return Err(PulseServerError::OperationErr( - pulse::operation::State::Cancelled, - )); - } - } - } - } -} - -#[derive(Debug, Clone, PartialEq)] -pub struct DeviceInfo { - pub name: Option, - pub description: Option, - pub volume: ChannelVolumes, - pub mute: bool, - pub index: u32, -} - -impl<'a> From<&SinkInfo<'a>> for DeviceInfo { - fn from(info: &SinkInfo<'a>) -> Self { - Self { - name: info.name.as_deref().map(str::to_string), - description: info.description.as_deref().map(str::to_string), - volume: info.volume, - mute: info.mute, - index: info.index, - } - } -} - -impl<'a> From<&SourceInfo<'a>> for DeviceInfo { - fn from(info: &SourceInfo<'a>) -> Self { - Self { - name: info.name.as_deref().map(str::to_string), - description: info.description.as_deref().map(str::to_string), - volume: info.volume, - mute: info.mute, - index: info.index, - } - } -} - -impl Eq for DeviceInfo {} - -#[derive(Debug)] -pub struct ServerInfo { - /// User name of the daemon process. - pub user_name: Option, - /// Host name the daemon is running on. - pub host_name: Option, - /// Version string of the daemon. - pub server_version: Option, - /// Server package name (usually “pulseaudio”). - pub server_name: Option, - // Default sample specification. - //pub sample_spec: sample::Spec, - /// Name of default sink. - pub default_sink_name: Option, - /// Name of default source. - pub default_source_name: Option, - /// A random cookie for identifying this instance of PulseAudio. - pub cookie: u32, - // Default channel map. - //pub channel_map: channelmap::Map, -} - -impl<'a> From<&'a pulse::context::introspect::ServerInfo<'a>> for ServerInfo { - fn from(info: &'a pulse::context::introspect::ServerInfo<'a>) -> Self { - use std::borrow::Cow; - Self { - user_name: info.user_name.as_ref().map(Cow::to_string), - host_name: info.host_name.as_ref().map(Cow::to_string), - server_version: info.server_version.as_ref().map(Cow::to_string), - server_name: info.server_name.as_ref().map(Cow::to_string), - //sample_spec: info.sample_spec, - default_sink_name: info.default_sink_name.as_ref().map(Cow::to_string), - default_source_name: info.default_source_name.as_ref().map(Cow::to_string), - cookie: info.cookie, - //channel_map: info.channel_map, - } - } -} diff --git a/cosmic-applet-battery/Cargo.toml b/cosmic-applet-battery/Cargo.toml index 7692e53a..20d6bd4f 100644 --- a/cosmic-applet-battery/Cargo.toml +++ b/cosmic-applet-battery/Cargo.toml @@ -6,10 +6,6 @@ license = "GPL-3.0-only" [dependencies] anyhow.workspace = true -cosmic-settings-subscriptions = { workspace = true, features = [ - "upower", - "settings_daemon", -] } cosmic-time.workspace = true drm = "0.14.1" futures.workspace = true @@ -24,3 +20,9 @@ tracing-subscriber.workspace = true tracing.workspace = true udev = "0.9" zbus.workspace = true + +[dependencies.cosmic-settings-upower-subscription] +git = "https://github.com/pop-os/cosmic-settings" + +[dependencies.cosmic-settings-daemon-subscription] +git = "https://github.com/pop-os/cosmic-settings" diff --git a/cosmic-applet-battery/src/app.rs b/cosmic-applet-battery/src/app.rs index aa36eddb..eb053000 100644 --- a/cosmic-applet-battery/src/app.rs +++ b/cosmic-applet-battery/src/app.rs @@ -29,15 +29,12 @@ use cosmic::{ surface, theme, widget::{divider, horizontal_space, icon, scrollable, slider, text, vertical_space}, }; -use cosmic_settings_subscriptions::{ - settings_daemon, - upower::{ - device::{DeviceDbusEvent, device_subscription}, - kbdbacklight::{ - KeyboardBacklightRequest, KeyboardBacklightUpdate, kbd_backlight_subscription, - }, - }, +use cosmic_settings_daemon_subscription as settings_daemon; +use cosmic_settings_upower_subscription::{ + device::{DeviceDbusEvent, device_subscription}, + kbdbacklight::{KeyboardBacklightRequest, KeyboardBacklightUpdate, kbd_backlight_subscription}, }; + use cosmic_time::{Instant, Timeline, anim, chain, id}; use rustc_hash::FxHashMap;