diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f4f19141..754b859e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,11 +19,11 @@ jobs: - uses: actions/checkout@v5 - uses: dtolnay/rust-toolchain@master with: - toolchain: 1.93.1 + toolchain: 1.90.0 components: clippy - name: install dependencies run: sudo apt update && sudo apt install -y libxkbcommon-dev libwayland-dev libdbus-1-dev libpulse-dev libpipewire-0.3-dev libinput-dev - uses: actions-rs-plus/clippy-check@v2 with: - toolchain: 1.93.1 + toolchain: 1.90.0 args: --all --all-targets --all-features diff --git a/.zed/settings.json b/.zed/settings.json deleted file mode 100644 index 2cc7b98d..00000000 --- a/.zed/settings.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "format_on_save": "on", - "lsp": { - "rust-analyzer": { - "initialization_options": { - "check": { - "command": "clippy", - }, - "rustfmt": { - "extraArgs": ["+nightly"], - }, - }, - }, - }, -} diff --git a/Cargo.lock b/Cargo.lock index dc5deede..1a265c94 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -107,6 +107,17 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" +[[package]] +name = "aes" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + [[package]] name = "ahash" version = "0.8.12" @@ -135,12 +146,6 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "250f629c0161ad8107cf89319e990051fae62832fd343083bea452d93e2205fd" -[[package]] -name = "allocator-api2" -version = "0.2.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" - [[package]] name = "almost" version = "0.2.0" @@ -154,7 +159,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0f2a1bb052857d5dd49572219344a7332b31b76405648eabac5bc68978251bcd" dependencies = [ "android-properties", - "bitflags 2.13.1", + "bitflags 2.11.0", "cc", "jni", "libc", @@ -181,6 +186,22 @@ 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", +] + +[[package]] +name = "anstyle" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" + [[package]] name = "anyhow" version = "1.0.102" @@ -223,6 +244,12 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" +[[package]] +name = "as-raw-xcb-connection" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "175571dd1d178ced59193a6fc02dde1b972eb0bc56c892cde9beeceac5bf0f6b" + [[package]] name = "ash" version = "0.38.0+1.3.281" @@ -241,7 +268,7 @@ dependencies = [ "enumflags2", "futures-channel", "futures-util", - "rand 0.9.4", + "rand 0.9.2", "raw-window-handle", "serde", "serde_repr", @@ -259,7 +286,7 @@ dependencies = [ "enumflags2", "futures-channel", "futures-util", - "rand 0.9.4", + "rand 0.9.2", "serde", "serde_repr", "tokio", @@ -498,6 +525,25 @@ 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.0", + "cexpr", + "clang-sys", + "itertools 0.13.0", + "proc-macro2", + "quote", + "regex", + "rustc-hash 2.1.2", + "shlex", + "syn", +] + [[package]] name = "bit-set" version = "0.8.0" @@ -521,9 +567,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.13.1" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b588b76d00fde79687d7646a9b5bdf3cc0f655e0bbd080335a95d7e96f3587da" +checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" dependencies = [ "serde_core", ] @@ -543,6 +589,15 @@ dependencies = [ "generic-array", ] +[[package]] +name = "block-padding" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93" +dependencies = [ + "generic-array", +] + [[package]] name = "block2" version = "0.5.1" @@ -591,7 +646,7 @@ dependencies = [ "libc", "log", "macaddr", - "nix", + "nix 0.29.0", "num-derive", "num-traits", "pin-project", @@ -613,15 +668,6 @@ dependencies = [ "cfg_aliases", ] -[[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" @@ -702,7 +748,7 @@ version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4dbf9978365bac10f54d1d4b04f7ce4427e51f71d61f2fe15e3fed5166474df7" dependencies = [ - "bitflags 2.13.1", + "bitflags 2.11.0", "polling", "rustix 1.1.4", "slab", @@ -722,10 +768,19 @@ dependencies = [ ] [[package]] -name = "cc" -version = "1.2.62" +name = "cbc" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1dce859f0832a7d088c4f1119888ab94ef4b5d6795d1ce05afb7fe159d79f98" +checksum = "26b52a9543ae338f279b96b0b9fed9c8093744685043739079ce85cd58f289a6" +dependencies = [ + "cipher", +] + +[[package]] +name = "cc" +version = "1.2.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43c5703da9466b66a946814e1adf53ea2c90f10063b86290cc9eb67ce3478a20" dependencies = [ "find-msvc-tools", "jobserver", @@ -733,6 +788,25 @@ 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 = "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" @@ -757,6 +831,27 @@ dependencies = [ "windows-link 0.2.1", ] +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "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 = "clipboard-win" version = "5.4.1" @@ -769,6 +864,7 @@ dependencies = [ [[package]] name = "clipboard_macos" version = "0.1.0" +source = "git+https://github.com/pop-os/window_clipboard.git?tag=sctk-0.20#f68595ee0e62fbd6589f4709b5aaa5c3c7ea5f6c" dependencies = [ "objc", "objc-foundation", @@ -778,12 +874,22 @@ dependencies = [ [[package]] name = "clipboard_wayland" version = "0.2.2" +source = "git+https://github.com/pop-os/window_clipboard.git?tag=sctk-0.20#f68595ee0e62fbd6589f4709b5aaa5c3c7ea5f6c" dependencies = [ "dnd", "mime 0.1.0", "smithay-clipboard", ] +[[package]] +name = "clipboard_x11" +version = "0.4.2" +source = "git+https://github.com/pop-os/window_clipboard.git?tag=sctk-0.20#f68595ee0e62fbd6589f4709b5aaa5c3c7ea5f6c" +dependencies = [ + "thiserror 1.0.69", + "x11rb", +] + [[package]] name = "cocoa" version = "0.25.0" @@ -856,6 +962,21 @@ 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" @@ -912,7 +1033,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb" dependencies = [ - "bitflags 2.13.1", + "bitflags 2.11.0", "core-foundation 0.10.1", "libc", ] @@ -928,7 +1049,7 @@ dependencies = [ [[package]] name = "cosmic-app-list" -version = "1.3.0" +version = "1.0.2" dependencies = [ "anyhow", "cosmic-app-list-config", @@ -939,7 +1060,7 @@ dependencies = [ "i18n-embed", "i18n-embed-fl", "image", - "libcosmic-yoda", + "libcosmic", "memmap2 0.9.10", "rust-embed", "rustc-hash 2.1.2", @@ -955,15 +1076,15 @@ dependencies = [ [[package]] name = "cosmic-app-list-config" -version = "1.3.0" +version = "1.0.2" dependencies = [ - "libcosmic-yoda", + "libcosmic", "serde", ] [[package]] name = "cosmic-applet-a11y" -version = "1.3.0" +version = "1.0.2" dependencies = [ "anyhow", "cosmic-client-toolkit", @@ -972,7 +1093,7 @@ dependencies = [ "cosmic-settings-accessibility-subscription", "i18n-embed", "i18n-embed-fl", - "libcosmic-yoda", + "libcosmic", "rust-embed", "tokio", "tracing", @@ -982,14 +1103,12 @@ dependencies = [ [[package]] name = "cosmic-applet-audio" -version = "1.3.0" +version = "1.0.2" dependencies = [ - "cosmic-settings-audio-client", - "futures", + "cosmic-settings-sound-subscription", "i18n-embed", "i18n-embed-fl", - "intmap", - "libcosmic-yoda", + "libcosmic", "mpris2-zbus", "rust-embed", "serde", @@ -1000,23 +1119,22 @@ dependencies = [ "url", "urlencoding", "zbus", - "zlink", ] [[package]] name = "cosmic-applet-battery" -version = "1.3.0" +version = "1.0.2" dependencies = [ "anyhow", "cosmic-applets-config", "cosmic-config", "cosmic-settings-daemon-subscription", "cosmic-settings-upower-subscription", - "drm", + "drm 0.14.1", "futures", "i18n-embed", "i18n-embed-fl", - "libcosmic-yoda", + "libcosmic", "rust-embed", "rustc-hash 2.1.2", "serde", @@ -1030,7 +1148,7 @@ dependencies = [ [[package]] name = "cosmic-applet-bluetooth" -version = "1.3.0" +version = "1.0.2" dependencies = [ "anyhow", "bluer", @@ -1038,7 +1156,7 @@ dependencies = [ "futures", "i18n-embed", "i18n-embed-fl", - "libcosmic-yoda", + "libcosmic", "rust-embed", "rustc-hash 2.1.2", "tokio", @@ -1049,12 +1167,12 @@ dependencies = [ [[package]] name = "cosmic-applet-input-sources" -version = "1.3.0" +version = "1.0.2" dependencies = [ "cosmic-comp-config", "i18n-embed", "i18n-embed-fl", - "libcosmic-yoda", + "libcosmic", "rust-embed", "tokio", "tracing", @@ -1065,13 +1183,13 @@ dependencies = [ [[package]] name = "cosmic-applet-minimize" -version = "1.3.0" +version = "1.0.2" dependencies = [ "anyhow", "i18n-embed", "i18n-embed-fl", "image", - "libcosmic-yoda", + "libcosmic", "memmap2 0.9.10", "rust-embed", "rustix 1.1.4", @@ -1083,16 +1201,20 @@ dependencies = [ [[package]] name = "cosmic-applet-network" -version = "1.3.0" +version = "1.0.2" dependencies = [ + "anyhow", "async-fn-stream", + "cosmic-dbus-networkmanager", + "cosmic-settings-airplane-mode-subscription", + "cosmic-settings-network-manager-subscription", "futures", "futures-util", "i18n-embed", "i18n-embed-fl", "indexmap 2.14.0", - "libcosmic-yoda", - "nmrs", + "libcosmic", + "nm-secret-agent-manager", "rust-embed", "rustc-hash 2.1.2", "secure-string", @@ -1101,11 +1223,12 @@ dependencies = [ "tracing-log", "tracing-subscriber", "uuid", + "zbus", ] [[package]] name = "cosmic-applet-notifications" -version = "1.3.0" +version = "1.0.2" dependencies = [ "anyhow", "cosmic-notifications-config", @@ -1113,7 +1236,7 @@ dependencies = [ "futures-util", "i18n-embed", "i18n-embed-fl", - "libcosmic-yoda", + "libcosmic", "rust-embed", "tokio", "tracing", @@ -1125,11 +1248,11 @@ dependencies = [ [[package]] name = "cosmic-applet-power" -version = "1.3.0" +version = "1.0.2" dependencies = [ "i18n-embed", "i18n-embed-fl", - "libcosmic-yoda", + "libcosmic", "logind-zbus", "rust-embed", "rustix 1.1.4", @@ -1142,10 +1265,10 @@ dependencies = [ [[package]] name = "cosmic-applet-status-area" -version = "1.3.0" +version = "1.0.2" dependencies = [ "futures", - "libcosmic-yoda", + "libcosmic", "rustc-hash 2.1.2", "serde", "tokio", @@ -1157,7 +1280,7 @@ dependencies = [ [[package]] name = "cosmic-applet-tiling" -version = "1.3.0" +version = "1.0.2" dependencies = [ "anyhow", "cosmic-client-toolkit", @@ -1165,7 +1288,7 @@ dependencies = [ "cosmic-protocols", "i18n-embed", "i18n-embed-fl", - "libcosmic-yoda", + "libcosmic", "rust-embed", "rustc-hash 2.1.2", "tokio", @@ -1176,14 +1299,14 @@ dependencies = [ [[package]] name = "cosmic-applet-time" -version = "1.3.0" +version = "1.0.2" dependencies = [ "cosmic-applets-config", "i18n-embed", "i18n-embed-fl", "icu", "jiff", - "libcosmic-yoda", + "libcosmic", "logind-zbus", "rust-embed", "timedate-zbus", @@ -1196,14 +1319,14 @@ dependencies = [ [[package]] name = "cosmic-applet-workspaces" -version = "1.3.0" +version = "1.0.2" dependencies = [ "anyhow", "cosmic-client-toolkit", "futures", "i18n-embed", "i18n-embed-fl", - "libcosmic-yoda", + "libcosmic", "rust-embed", "tokio", "tracing", @@ -1213,7 +1336,7 @@ dependencies = [ [[package]] name = "cosmic-applets" -version = "1.3.0" +version = "1.0.2" dependencies = [ "cosmic-app-list", "cosmic-applet-a11y", @@ -1230,7 +1353,7 @@ dependencies = [ "cosmic-applet-time", "cosmic-applet-workspaces", "cosmic-panel-button", - "libcosmic-yoda", + "libcosmic", "tracing", "tracing-log", "tracing-subscriber", @@ -1239,7 +1362,7 @@ dependencies = [ [[package]] name = "cosmic-applets-config" -version = "1.3.0" +version = "1.0.2" dependencies = [ "cosmic-config", "serde", @@ -1248,8 +1371,9 @@ dependencies = [ [[package]] name = "cosmic-client-toolkit" version = "0.2.0" +source = "git+https://github.com/pop-os/cosmic-protocols//?branch=main#160b086abe03cd34a8a375d7fbe47b24308d1f38" dependencies = [ - "bitflags 2.13.1", + "bitflags 2.11.0", "cosmic-protocols", "libc", "smithay-client-toolkit", @@ -1259,7 +1383,8 @@ dependencies = [ [[package]] name = "cosmic-comp-config" -version = "1.0.0" +version = "0.1.0" +source = "git+https://github.com/pop-os/cosmic-comp.git?rev=5eb5af4#5eb5af46756f2f2bc5e812cf68fe4c4b79efd924" dependencies = [ "cosmic-config", "input", @@ -1269,6 +1394,7 @@ dependencies = [ [[package]] name = "cosmic-config" version = "1.0.0" +source = "git+https://github.com/pop-os/libcosmic#6caccaba337ed9bab21c5fe3c2aa7392e322e89c" dependencies = [ "atomicwrites", "cosmic-config-derive", @@ -1289,6 +1415,7 @@ dependencies = [ [[package]] name = "cosmic-config-derive" version = "1.0.0" +source = "git+https://github.com/pop-os/libcosmic#6caccaba337ed9bab21c5fe3c2aa7392e322e89c" dependencies = [ "quote", "syn", @@ -1297,14 +1424,28 @@ dependencies = [ [[package]] name = "cosmic-dbus-a11y" version = "0.1.0" -source = "git+https://github.com/pop-os/dbus-settings-bindings#eed01dd3609e90e3c8cd043656734c500956c793" +source = "git+https://github.com/pop-os/dbus-settings-bindings#507e342c21d3ce6ae41b1d4f3fa2f0ad5ee23e75" dependencies = [ "zbus", ] +[[package]] +name = "cosmic-dbus-networkmanager" +version = "0.1.0" +source = "git+https://github.com/pop-os/dbus-settings-bindings#507e342c21d3ce6ae41b1d4f3fa2f0ad5ee23e75" +dependencies = [ + "bitflags 2.11.0", + "derive_builder", + "jiff", + "procfs", + "zbus", + "zvariant", +] + [[package]] name = "cosmic-freedesktop-icons" version = "0.4.0" +source = "git+https://github.com/pop-os/freedesktop-icons#7a61a704f6d1ec41f71cbe766e3cc484858523fa" dependencies = [ "bstr", "btoi", @@ -1318,6 +1459,7 @@ dependencies = [ [[package]] name = "cosmic-notifications-config" version = "0.1.0" +source = "git+https://github.com/pop-os/cosmic-notifications#6046bb29d570161196ab98d60806f6d6489df7a6" dependencies = [ "cosmic-config", "serde", @@ -1326,9 +1468,10 @@ dependencies = [ [[package]] name = "cosmic-notifications-util" version = "0.1.0" +source = "git+https://github.com/pop-os/cosmic-notifications#6046bb29d570161196ab98d60806f6d6489df7a6" dependencies = [ "fast_image_resize", - "libcosmic-yoda", + "libcosmic", "serde", "tl", "tracing", @@ -1338,10 +1481,10 @@ dependencies = [ [[package]] name = "cosmic-panel-button" -version = "1.3.0" +version = "1.0.2" dependencies = [ "cosmic-config", - "libcosmic-yoda", + "libcosmic", "rustc-hash 2.1.2", "serde", "tracing", @@ -1351,7 +1494,8 @@ dependencies = [ [[package]] name = "cosmic-panel-config" -version = "1.3.0" +version = "0.1.0" +source = "git+https://github.com/pop-os/cosmic-panel#d518c7d25ef96f1a9696aa8cce008656bf66ece4" dependencies = [ "anyhow", "cosmic-config", @@ -1362,11 +1506,26 @@ dependencies = [ "xdg-shell-wrapper-config", ] +[[package]] +name = "cosmic-pipewire" +version = "1.0.7" +source = "git+https://github.com/pop-os/cosmic-settings#3639bafcfa3b27fd6a8a9bca7e77589b903975e8" +dependencies = [ + "intmap", + "libspa", + "libspa-sys", + "pipewire", + "serde", + "serde_json", + "tracing", +] + [[package]] name = "cosmic-protocols" version = "0.2.0" +source = "git+https://github.com/pop-os/cosmic-protocols//?branch=main#160b086abe03cd34a8a375d7fbe47b24308d1f38" dependencies = [ - "bitflags 2.13.1", + "bitflags 2.11.0", "wayland-backend", "wayland-client", "wayland-protocols", @@ -1378,6 +1537,7 @@ dependencies = [ [[package]] name = "cosmic-settings-a11y-manager-subscription" version = "1.0.7" +source = "git+https://github.com/pop-os/cosmic-settings#3639bafcfa3b27fd6a8a9bca7e77589b903975e8" dependencies = [ "cosmic-protocols", "iced_futures", @@ -1391,6 +1551,7 @@ dependencies = [ [[package]] name = "cosmic-settings-accessibility-subscription" version = "1.0.7" +source = "git+https://github.com/pop-os/cosmic-settings#3639bafcfa3b27fd6a8a9bca7e77589b903975e8" dependencies = [ "cosmic-dbus-a11y", "futures", @@ -1401,33 +1562,21 @@ dependencies = [ ] [[package]] -name = "cosmic-settings-audio-client" -version = "1.0.0" +name = "cosmic-settings-airplane-mode-subscription" +version = "1.0.7" +source = "git+https://github.com/pop-os/cosmic-settings#3639bafcfa3b27fd6a8a9bca7e77589b903975e8" dependencies = [ - "cosmic-settings-audio-core", - "dirs", - "futures-util", - "ron 0.12.1", - "serde", + "futures", + "iced_futures", + "log", + "rustix 1.1.4", "tokio", - "tokio-util", - "tracing", - "zlink", -] - -[[package]] -name = "cosmic-settings-audio-core" -version = "1.0.0" -dependencies = [ - "dirs", - "ron 0.12.1", - "serde", - "zlink", ] [[package]] name = "cosmic-settings-config" version = "0.1.0" +source = "git+https://github.com/pop-os/cosmic-settings-daemon#716da6d6af0b252e2f78aba2ad72ee19ae0241e0" dependencies = [ "cosmic-config", "ron 0.11.0", @@ -1439,7 +1588,8 @@ dependencies = [ [[package]] name = "cosmic-settings-daemon" -version = "0.1.1-yoda.1" +version = "0.1.0" +source = "git+https://github.com/pop-os/dbus-settings-bindings#507e342c21d3ce6ae41b1d4f3fa2f0ad5ee23e75" dependencies = [ "zbus", ] @@ -1447,6 +1597,7 @@ dependencies = [ [[package]] name = "cosmic-settings-daemon-subscription" version = "1.0.7" +source = "git+https://github.com/pop-os/cosmic-settings#3639bafcfa3b27fd6a8a9bca7e77589b903975e8" dependencies = [ "futures", "iced_futures", @@ -1456,9 +1607,44 @@ dependencies = [ "zbus", ] +[[package]] +name = "cosmic-settings-network-manager-subscription" +version = "1.0.7" +source = "git+https://github.com/pop-os/cosmic-settings#3639bafcfa3b27fd6a8a9bca7e77589b903975e8" +dependencies = [ + "bitflags 2.11.0", + "cosmic-dbus-networkmanager", + "futures", + "iced_futures", + "itertools 0.14.0", + "nm-secret-agent-manager", + "secret-service", + "secure-string", + "thiserror 2.0.18", + "tokio", + "tracing", + "zbus", +] + +[[package]] +name = "cosmic-settings-sound-subscription" +version = "1.0.7" +source = "git+https://github.com/pop-os/cosmic-settings#3639bafcfa3b27fd6a8a9bca7e77589b903975e8" +dependencies = [ + "cosmic-pipewire", + "futures", + "intmap", + "libcosmic", + "numtoa", + "rustix 1.1.4", + "tokio", + "tracing", +] + [[package]] name = "cosmic-settings-upower-subscription" version = "1.0.7" +source = "git+https://github.com/pop-os/cosmic-settings#3639bafcfa3b27fd6a8a9bca7e77589b903975e8" dependencies = [ "futures", "iced_futures", @@ -1471,9 +1657,10 @@ dependencies = [ [[package]] name = "cosmic-text" -version = "0.19.0" +version = "0.18.2" +source = "git+https://github.com/pop-os/cosmic-text.git#4d74f795cc771fdcc7ea0f9cacba63fcf036fad6" dependencies = [ - "bitflags 2.13.1", + "bitflags 2.11.0", "fontdb", "harfrust", "linebender_resource_handle", @@ -1489,19 +1676,18 @@ dependencies = [ "unicode-linebreak", "unicode-script", "unicode-segmentation", - "unicode-width", ] [[package]] name = "cosmic-theme" version = "1.0.0" +source = "git+https://github.com/pop-os/libcosmic#6caccaba337ed9bab21c5fe3c2aa7392e322e89c" dependencies = [ "almost", "configparser", "cosmic-config", "csscolorparser", "dirs", - "hex_color", "palette", "ron 0.12.1", "serde", @@ -1542,6 +1728,7 @@ checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" [[package]] name = "cryoglyph" version = "0.1.0" +source = "git+https://github.com/pop-os/glyphon.git?tag=cosmic-0.14#c49de15bce4d8254ac136d1be9911960cc85ce12" dependencies = [ "cosmic-text", "etagere", @@ -1578,6 +1765,12 @@ dependencies = [ "uncased", ] +[[package]] +name = "ctor-lite" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e162d0c2e2068eb736b71e5597eff0b9944e6b973cd9f37b6a288ab9bf20e300" + [[package]] name = "cursor-icon" version = "1.2.0" @@ -1718,15 +1911,15 @@ checksum = "be1e0bca6c3637f992fc1cc7cbc52a78c1ef6db076dbf1059c4323d6a2048376" [[package]] name = "dbus" -version = "0.9.11" +version = "0.9.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b942602992bb7acfd1f51c49811c58a610ef9181b6e66f3e519d79b540a3bf73" +checksum = "21b3aa68d7e7abee336255bd7248ea965cc393f3e70411135a6f6a4b651345d4" dependencies = [ "futures-channel", "futures-util", "libc", "libdbus-sys", - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -1759,6 +1952,37 @@ dependencies = [ "serde_core", ] +[[package]] +name = "derive_builder" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "507dfb09ea8b7fa618fcf76e953f4f5e192547945816d5358edffe39f6f94947" +dependencies = [ + "derive_builder_macro", +] + +[[package]] +name = "derive_builder_core" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d5bcf7b024d6835cfb3d473887cd966994907effbe9227e8c8219824d06c4e8" +dependencies = [ + "darling 0.20.11", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "derive_builder_macro" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" +dependencies = [ + "derive_builder_core", + "syn", +] + [[package]] name = "derive_setters" version = "0.1.9" @@ -1790,6 +2014,7 @@ checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer", "crypto-common", + "subtle", ] [[package]] @@ -1819,7 +2044,7 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e0e367e4e7da84520dedcac1901e4da967309406d1e51017ae1abfb97adbd38" dependencies = [ - "bitflags 2.13.1", + "bitflags 2.11.0", "block2 0.6.2", "libc", "objc2 0.6.4", @@ -1848,8 +2073,9 @@ dependencies = [ [[package]] name = "dnd" version = "0.1.0" +source = "git+https://github.com/pop-os/window_clipboard.git?tag=sctk-0.20#f68595ee0e62fbd6589f4709b5aaa5c3c7ea5f6c" dependencies = [ - "bitflags 2.13.1", + "bitflags 2.11.0", "mime 0.1.0", "raw-window-handle", "smithay-client-toolkit", @@ -1874,6 +2100,20 @@ checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" [[package]] name = "dpi" version = "0.1.2" +source = "git+https://github.com/pop-os/winit.git?tag=cosmic-0.14#a610ac9c7a72b39ff102ed4d946291618dc725b6" + +[[package]] +name = "drm" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0f8a69e60d75ae7dab4ef26a59ca99f2a89d4c142089b537775ae0c198bdcde" +dependencies = [ + "bitflags 2.11.0", + "bytemuck", + "drm-ffi 0.7.1", + "drm-fourcc", + "rustix 0.38.44", +] [[package]] name = "drm" @@ -1881,21 +2121,31 @@ version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "80bc8c5c6c2941f70a55c15f8d9f00f9710ebda3ffda98075f996a0e6c92756f" dependencies = [ - "bitflags 2.13.1", + "bitflags 2.11.0", "bytemuck", - "drm-ffi", + "drm-ffi 0.9.1", "drm-fourcc", "libc", "rustix 0.38.44", ] +[[package]] +name = "drm-ffi" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41334f8405792483e32ad05fbb9c5680ff4e84491883d2947a4757dc54cb2ac6" +dependencies = [ + "drm-sys 0.6.1", + "rustix 0.38.44", +] + [[package]] name = "drm-ffi" version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51a91c9b32ac4e8105dec255e849e0d66e27d7c34d184364fb93e469db08f690" dependencies = [ - "drm-sys", + "drm-sys 0.8.1", "rustix 1.1.4", ] @@ -1905,6 +2155,16 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0aafbcdb8afc29c1a7ee5fbe53b5d62f4565b35a042a662ca9fecd0b54dae6f4" +[[package]] +name = "drm-sys" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d09ff881f92f118b11105ba5e34ff8f4adf27b30dae8f12e28c193af1c83176" +dependencies = [ + "libc", + "linux-raw-sys 0.6.5", +] + [[package]] name = "drm-sys" version = "0.8.1" @@ -2024,9 +2284,9 @@ checksum = "dd2e7510819d6fbf51a5545c8f922716ecfb14df168a3242f7d33e0239efe6a1" [[package]] name = "fast_image_resize" -version = "6.0.0" +version = "5.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12dd43e5011e8d8411a3215a0d57a2ec5c68282fb90eb5d7221fab0113442174" +checksum = "fbc7fe45cf92b43817ff62a3723e862b85bd1d06288f63007f7645d1d2f7a060" dependencies = [ "cfg-if", "document-features", @@ -2055,7 +2315,7 @@ version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "59a98bbaacea1c0eb6a0876280051b892eb73594fd90cf3b20e9c817029c57d2" dependencies = [ - "toml", + "toml 0.5.11", ] [[package]] @@ -2171,9 +2431,9 @@ checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" [[package]] name = "font-types" -version = "0.11.3" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b38ad915f6dadd993ced50848a8291a543bd41ca62bc10740d5e64e2ab4cfd7" +checksum = "2d9237c6d82152100c691fb77ea18037b402bcc7257d2c876a4ffac81bc22a1c" dependencies = [ "bytemuck", ] @@ -2259,7 +2519,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc6d3a3635983a889f065aa9ce760384713f23a9b4a04f696f86c39a5d7a6a5a" dependencies = [ "indexmap 2.14.0", - "nom", + "nom 8.0.0", ] [[package]] @@ -2355,12 +2615,6 @@ version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" -[[package]] -name = "futures-timer" -version = "3.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af43fadb8a98512d547e37b4e92e0ced13e205c061b87b4623eff01d918d6968" - [[package]] name = "futures-util" version = "0.3.32" @@ -2388,6 +2642,16 @@ dependencies = [ "version_check", ] +[[package]] +name = "gethostname" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bd49230192a3797a9a4d6abe9b3eed6f7fa4c8a8a4947977c6f80025f92cbd8" +dependencies = [ + "rustix 1.1.4", + "windows-link 0.2.1", +] + [[package]] name = "getrandom" version = "0.2.17" @@ -2471,6 +2735,12 @@ 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" @@ -2493,17 +2763,34 @@ dependencies = [ ] [[package]] -name = "gpu-allocator" -version = "0.28.0" +name = "gpu-alloc" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51255ea7cfaadb6c5f1528d43e92a82acb2b96c43365989a28b2d44ee38f8795" +checksum = "fbcd2dba93594b227a1f57ee09b8b9da8892c34d55aa332e034a228d0fe6a171" +dependencies = [ + "bitflags 2.11.0", + "gpu-alloc-types", +] + +[[package]] +name = "gpu-alloc-types" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98ff03b468aa837d70984d55f5d3f846f6ec31fe34bbb97c4f85219caeee1ca4" +dependencies = [ + "bitflags 2.11.0", +] + +[[package]] +name = "gpu-allocator" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c151a2a5ef800297b4e79efa4f4bec035c5f51d5ae587287c9b952bdf734cacd" dependencies = [ - "ash", - "hashbrown 0.16.1", "log", "presser", - "thiserror 2.0.18", - "windows 0.62.2", + "thiserror 1.0.69", + "windows 0.58.0", ] [[package]] @@ -2512,7 +2799,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b89c83349105e3732062a895becfc71a8f921bb71ecbbdd8ff99263e3b53a0ca" dependencies = [ - "bitflags 2.13.1", + "bitflags 2.11.0", "gpu-descriptor-types", "hashbrown 0.15.5", ] @@ -2523,14 +2810,14 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdf242682df893b86f33a73828fb09ca4b2d3bb6cc95249707fc684d27484b91" dependencies = [ - "bitflags 2.13.1", + "bitflags 2.11.0", ] [[package]] name = "grid" -version = "1.0.1" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b40ca9252762c466af32d0b1002e91e4e1bc5398f77455e55474deb466355ff5" +checksum = "f9e2d4c0a8296178d8802098410ca05d86b17a10bb5ab559b3fb404c1f948220" [[package]] name = "guillotiere" @@ -2560,7 +2847,7 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9da2e5ae821f6e96664977bf974d6d6a2d6682f9ccee23e62ec1d134246845f9" dependencies = [ - "bitflags 2.13.1", + "bitflags 2.11.0", "bytemuck", "core_maths", "read-fonts", @@ -2588,16 +2875,14 @@ version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" dependencies = [ - "allocator-api2", - "equivalent", "foldhash 0.2.0", ] [[package]] name = "hashbrown" -version = "0.17.1" +version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" +checksum = "4f467dd6dccf739c208452f8014c75c18bb8301b050ad1cfb27153803edb0f51" [[package]] name = "heck" @@ -2629,23 +2914,30 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" -[[package]] -name = "hex_color" -version = "3.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d37f101bf4c633f7ca2e4b5e136050314503dd198e78e325ea602c327c484ef0" -dependencies = [ - "arrayvec", - "rand 0.8.6", - "serde", -] - [[package]] name = "hexf-parse" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df" +[[package]] +name = "hkdf" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" +dependencies = [ + "hmac", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + [[package]] name = "i18n-config" version = "0.4.8" @@ -2740,6 +3032,7 @@ dependencies = [ [[package]] name = "iced" version = "0.14.0" +source = "git+https://github.com/pop-os/libcosmic#6caccaba337ed9bab21c5fe3c2aa7392e322e89c" dependencies = [ "dnd", "iced_accessibility", @@ -2760,6 +3053,7 @@ dependencies = [ [[package]] name = "iced_accessibility" version = "0.1.0" +source = "git+https://github.com/pop-os/libcosmic#6caccaba337ed9bab21c5fe3c2aa7392e322e89c" dependencies = [ "accesskit", "accesskit_winit", @@ -2768,8 +3062,9 @@ dependencies = [ [[package]] name = "iced_core" version = "0.14.0" +source = "git+https://github.com/pop-os/libcosmic#6caccaba337ed9bab21c5fe3c2aa7392e322e89c" dependencies = [ - "bitflags 2.13.1", + "bitflags 2.11.0", "bytes", "cosmic-client-toolkit", "dnd", @@ -2784,7 +3079,6 @@ dependencies = [ "serde", "smol_str", "thiserror 2.0.18", - "unicode-segmentation", "web-time", "window_clipboard", ] @@ -2792,6 +3086,7 @@ dependencies = [ [[package]] name = "iced_debug" version = "0.14.0" +source = "git+https://github.com/pop-os/libcosmic#6caccaba337ed9bab21c5fe3c2aa7392e322e89c" dependencies = [ "iced_core", "iced_futures", @@ -2801,6 +3096,7 @@ dependencies = [ [[package]] name = "iced_futures" version = "0.14.0" +source = "git+https://github.com/pop-os/libcosmic#6caccaba337ed9bab21c5fe3c2aa7392e322e89c" dependencies = [ "futures", "iced_core", @@ -2814,8 +3110,9 @@ dependencies = [ [[package]] name = "iced_graphics" version = "0.14.0" +source = "git+https://github.com/pop-os/libcosmic#6caccaba337ed9bab21c5fe3c2aa7392e322e89c" dependencies = [ - "bitflags 2.13.1", + "bitflags 2.11.0", "bytemuck", "cosmic-text", "half", @@ -2834,6 +3131,7 @@ dependencies = [ [[package]] name = "iced_program" version = "0.14.0" +source = "git+https://github.com/pop-os/libcosmic#6caccaba337ed9bab21c5fe3c2aa7392e322e89c" dependencies = [ "iced_graphics", "iced_runtime", @@ -2842,6 +3140,7 @@ dependencies = [ [[package]] name = "iced_renderer" version = "0.14.0" +source = "git+https://github.com/pop-os/libcosmic#6caccaba337ed9bab21c5fe3c2aa7392e322e89c" dependencies = [ "iced_graphics", "iced_tiny_skia", @@ -2853,6 +3152,7 @@ dependencies = [ [[package]] name = "iced_runtime" version = "0.14.0" +source = "git+https://github.com/pop-os/libcosmic#6caccaba337ed9bab21c5fe3c2aa7392e322e89c" dependencies = [ "bytes", "cosmic-client-toolkit", @@ -2867,6 +3167,7 @@ dependencies = [ [[package]] name = "iced_tiny_skia" version = "0.14.0" +source = "git+https://github.com/pop-os/libcosmic#6caccaba337ed9bab21c5fe3c2aa7392e322e89c" dependencies = [ "bytemuck", "cosmic-text", @@ -2883,8 +3184,10 @@ dependencies = [ [[package]] name = "iced_wgpu" version = "0.14.0" +source = "git+https://github.com/pop-os/libcosmic#6caccaba337ed9bab21c5fe3c2aa7392e322e89c" dependencies = [ - "bitflags 2.13.1", + "as-raw-xcb-connection", + "bitflags 2.11.0", "bytemuck", "cosmic-client-toolkit", "cryoglyph", @@ -2900,16 +3203,19 @@ dependencies = [ "rustc-hash 2.1.2", "rustix 0.38.44", "thiserror 2.0.18", + "tiny-xlib", "wayland-backend", "wayland-client", "wayland-protocols", "wayland-sys", "wgpu", + "x11rb", ] [[package]] name = "iced_widget" version = "0.14.2" +source = "git+https://github.com/pop-os/libcosmic#6caccaba337ed9bab21c5fe3c2aa7392e322e89c" dependencies = [ "cosmic-client-toolkit", "dnd", @@ -2927,6 +3233,7 @@ dependencies = [ [[package]] name = "iced_winit" version = "0.14.0" +source = "git+https://github.com/pop-os/libcosmic#6caccaba337ed9bab21c5fe3c2aa7392e322e89c" dependencies = [ "cosmic-client-toolkit", "cursor-icon", @@ -3379,9 +3686,9 @@ dependencies = [ [[package]] name = "idna_adapter" -version = "1.2.2" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb68373c0d6620ef8105e855e7745e18b0d00d3bdb07fb532e434244cdb9a714" +checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" dependencies = [ "icu_normalizer", "icu_properties", @@ -3402,15 +3709,6 @@ dependencies = [ "zune-jpeg 0.5.15", ] -[[package]] -name = "image-extras" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60d02eb2c9ccbbab470538fce34c7bc3be7b4e59268e65a3171367b296cdb842" -dependencies = [ - "image", -] - [[package]] name = "image-webp" version = "0.2.4" @@ -3445,7 +3743,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" dependencies = [ "equivalent", - "hashbrown 0.17.1", + "hashbrown 0.17.0", "serde", "serde_core", ] @@ -3456,7 +3754,7 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd5b3eaf1a28b758ac0faa5a4254e8ab2705605496f1b1f3fbbc3988ad73d199" dependencies = [ - "bitflags 2.13.1", + "bitflags 2.11.0", "inotify-sys", "libc", ] @@ -3471,12 +3769,22 @@ dependencies = [ ] [[package]] -name = "input" -version = "0.10.0" +name = "inout" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9793345a65d71317763a33066b5d8351f8760dde8d4930fe9e39b5f14a7959d" +checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" dependencies = [ - "bitflags 2.13.1", + "block-padding", + "generic-array", +] + +[[package]] +name = "input" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbdc09524a91f9cacd26f16734ff63d7dc650daffadd2b6f84d17a285bd875a9" +dependencies = [ + "bitflags 2.11.0", "input-sys", "libc", "log", @@ -3525,6 +3833,24 @@ dependencies = [ "windows-sys 0.48.0", ] +[[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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.18" @@ -3539,9 +3865,9 @@ checksum = "2ceaf4c6c48465bead8cb6a0b7c4ee0c86ecbb31239032b9c66ab9a08d2f3ee1" [[package]] name = "jiff" -version = "0.2.24" +version = "0.2.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f00b5dbd620d61dfdcb6007c9c1f6054ebd75319f163d886a9055cec1155073d" +checksum = "1a3546dc96b6d42c5f24902af9e2538e82e39ad350b0c766eb3fbf2d8f3d8359" dependencies = [ "jiff-static", "jiff-tzdb-platform", @@ -3554,9 +3880,9 @@ dependencies = [ [[package]] name = "jiff-static" -version = "0.2.24" +version = "0.2.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e000de030ff8022ea1da3f466fbb0f3a809f5e51ed31f6dd931c35181ad8e6d7" +checksum = "2a8c8b344124222efd714b73bb41f8b5120b27a7cc1c75593a6ff768d9d05aa4" dependencies = [ "proc-macro2", "quote", @@ -3648,9 +3974,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.98" +version = "0.3.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67df7112613f8bfd9150013a0314e196f4800d3201ae742489d999db2f979f08" +checksum = "2e04e2ef80ce82e13552136fabeef8a5ed1f985a96805761cbb9a2c34e7664d9" dependencies = [ "cfg-if", "futures-util", @@ -3673,7 +3999,7 @@ version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fbe853b403ae61a04233030ae8a79d94975281ed9770a1f9e246732b534b28d" dependencies = [ - "bitflags 2.13.1", + "bitflags 2.11.0", "serde", ] @@ -3715,11 +4041,11 @@ dependencies = [ [[package]] name = "kqueue-sys" -version = "1.1.2" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07293a4e297ac234359b510362495713f75ea345d5307140414f20c69ffeb087" +checksum = "ed9625ffda8729b85e45cf04090035ac368927b8cebc34898e7c120f52e4838b" dependencies = [ - "bitflags 2.13.1", + "bitflags 1.3.2", "libc", ] @@ -3758,13 +4084,14 @@ checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" [[package]] name = "libc" -version = "0.2.186" +version = "0.2.184" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" +checksum = "48f5d2a454e16a5ea0f4ced81bd44e4cfc7bd3a507b61887c99fd3538b28e4af" [[package]] -name = "libcosmic-yoda" -version = "0.1.0-yoda.2" +name = "libcosmic" +version = "1.0.0" +source = "git+https://github.com/pop-os/libcosmic#6caccaba337ed9bab21c5fe3c2aa7392e322e89c" dependencies = [ "apply", "ashpd 0.12.3", @@ -3778,7 +4105,6 @@ dependencies = [ "cosmic-theme", "css-color", "derive_setters", - "enumflags2", "float-cmp 0.10.0", "freedesktop-desktop-entry", "futures", @@ -3794,7 +4120,6 @@ dependencies = [ "iced_widget", "iced_winit", "image", - "image-extras", "jiff", "libc", "log", @@ -3844,14 +4169,42 @@ checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" [[package]] name = "libredox" -version = "0.1.17" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f02ab6bace2054fb888a3c16f990117b579d14a3088e472d63c6011fa185c9d3" +checksum = "e02f3bb43d335493c96bf3fd3a321600bf6bd07ed34bc64118e9293bdffea46c" dependencies = [ - "bitflags 2.13.1", + "bitflags 2.11.0", "libc", "plain", - "redox_syscall 0.8.1", + "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.0", + "cc", + "convert_case", + "cookie-factory", + "libc", + "libspa-sys", + "nix 0.30.1", + "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]] @@ -3885,6 +4238,12 @@ version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" +[[package]] +name = "linux-raw-sys" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a385b1be4e5c3e362ad2ffa73c392e53f031eaa5b7d648e64cd87f27f6063d7" + [[package]] name = "linux-raw-sys" version = "0.9.4" @@ -3933,9 +4292,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.33" +version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" [[package]] name = "logind-zbus" @@ -3949,9 +4308,9 @@ dependencies = [ [[package]] name = "lru" -version = "0.16.4" +version = "0.16.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f66e8d5d03f609abc3a39e6f08e4164ebf1447a732906d39eb9b99b7919ef39" +checksum = "a1dc47f592c06f33f8e3aea9591776ec7c9f9e4124778ff8a3c3b87159f7e593" [[package]] name = "lyon" @@ -3965,9 +4324,9 @@ dependencies = [ [[package]] name = "lyon_algorithms" -version = "1.0.20" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8575c0d003ae459399623c4def180c63b77f343b1a7fee64f249b349e7699a31" +checksum = "9815fac08e6fd96733a11dce4f9d15a3f338e96a2e2311ee21e1b738efc2bc0f" dependencies = [ "lyon_path", "num-traits", @@ -4064,11 +4423,11 @@ dependencies = [ [[package]] name = "metal" -version = "0.33.0" +version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7047791b5bc903b8cd963014b355f71dc9864a9a0b727057676c1dcae5cbc15" +checksum = "00c15a6f673ff72ddcc22394663290f870fb224c1bfce55734a75c414150e605" dependencies = [ - "bitflags 2.13.1", + "bitflags 2.11.0", "block", "core-graphics-types 0.2.0", "foreign-types", @@ -4080,6 +4439,7 @@ dependencies = [ [[package]] name = "mime" version = "0.1.0" +source = "git+https://github.com/pop-os/window_clipboard.git?tag=sctk-0.20#f68595ee0e62fbd6589f4709b5aaa5c3c7ea5f6c" dependencies = [ "smithay-clipboard", ] @@ -4090,6 +4450,12 @@ 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" @@ -4125,6 +4491,7 @@ dependencies = [ [[package]] name = "mpris2-zbus" version = "0.1.0" +source = "git+https://github.com/pop-os/dbus-settings-bindings#507e342c21d3ce6ae41b1d4f3fa2f0ad5ee23e75" dependencies = [ "futures-util", "jiff", @@ -4142,13 +4509,13 @@ checksum = "13d2233c9842d08cfe13f9eac96e207ca6a2ea10b80259ebe8ad0268be27d2af" [[package]] name = "naga" -version = "28.0.0" +version = "27.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "618f667225063219ddfc61251087db8a9aec3c3f0950c916b614e403486f1135" +checksum = "066cf25f0e8b11ee0df221219010f213ad429855f57c494f995590c861a9a7d8" dependencies = [ "arrayvec", "bit-set", - "bitflags 2.13.1", + "bitflags 2.11.0", "cfg-if", "cfg_aliases", "codespan-reporting", @@ -4172,7 +4539,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4" dependencies = [ - "bitflags 2.13.1", + "bitflags 2.11.0", "jni-sys 0.3.1", "log", "ndk-sys", @@ -4202,30 +4569,40 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" dependencies = [ - "bitflags 2.13.1", + "bitflags 2.11.0", "cfg-if", "cfg_aliases", "libc", ] [[package]] -name = "nmrs" -version = "3.4.2" +name = "nix" +version = "0.30.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33e42c05118e18f29f55eefa2fbfd98a06b0c7a1a829bdb0f328137d358c6013" +checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" +dependencies = [ + "bitflags 2.11.0", + "cfg-if", + "cfg_aliases", + "libc", +] + +[[package]] +name = "nm-secret-agent-manager" +version = "0.1.0" +source = "git+https://github.com/pop-os/dbus-settings-bindings#507e342c21d3ce6ae41b1d4f3fa2f0ad5ee23e75" dependencies = [ - "async-trait", - "base64", - "bitflags 2.13.1", - "futures", - "futures-timer", - "log", - "serde", - "thiserror 2.0.18", - "tokio", - "uuid", "zbus", - "zvariant", +] + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", ] [[package]] @@ -4243,7 +4620,7 @@ version = "8.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4d3d07927151ff8575b7087f245456e549fea62edf0ec4e565a5ee50c8402bc3" dependencies = [ - "bitflags 2.13.1", + "bitflags 2.11.0", "fsevent-sys", "inotify", "kqueue", @@ -4261,7 +4638,7 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42b8cfee0e339a0337359f3c88165702ac6e600dc01c0cc9579a92d62b08477a" dependencies = [ - "bitflags 2.13.1", + "bitflags 2.11.0", ] [[package]] @@ -4273,6 +4650,20 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "num" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23" +dependencies = [ + "num-bigint", + "num-complex", + "num-integer", + "num-iter", + "num-rational", + "num-traits", +] + [[package]] name = "num-bigint" version = "0.4.6" @@ -4284,10 +4675,19 @@ dependencies = [ ] [[package]] -name = "num-conv" -version = "0.2.2" +name = "num-complex" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441" +checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-conv" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6673768db2d862beb9b39a78fdcb1a69439615d5794a1be50caa9bc92c81967" [[package]] name = "num-derive" @@ -4309,6 +4709,17 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-iter" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + [[package]] name = "num-rational" version = "0.4.2" @@ -4352,6 +4763,12 @@ dependencies = [ "syn", ] +[[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" @@ -4403,7 +4820,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e4e89ad9e3d7d297152b17d39ed92cd50ca8063a89a9fa569046d41568891eff" dependencies = [ - "bitflags 2.13.1", + "bitflags 2.11.0", "block2 0.5.1", "libc", "objc2 0.5.2", @@ -4419,7 +4836,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d49e936b501e5c5bf01fda3a9452ff86dc3ea98ad5f283e1455153142d97518c" dependencies = [ - "bitflags 2.13.1", + "bitflags 2.11.0", "block2 0.6.2", "objc2 0.6.4", "objc2-core-foundation", @@ -4432,7 +4849,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "617fbf49e071c178c0b24c080767db52958f716d9eabdf0890523aeae54773ef" dependencies = [ - "bitflags 2.13.1", + "bitflags 2.11.0", "block2 0.5.1", "objc2 0.5.2", "objc2-foundation 0.2.2", @@ -4444,7 +4861,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" dependencies = [ - "bitflags 2.13.1", + "bitflags 2.11.0", "block2 0.6.2", "dispatch2", "objc2 0.6.4", @@ -4456,7 +4873,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e022c9d066895efa1345f8e33e584b9f958da2fd4cd116792e15e07e4720a807" dependencies = [ - "bitflags 2.13.1", + "bitflags 2.11.0", "libc", "objc2-core-foundation", ] @@ -4479,7 +4896,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d425caf1df73233f29fd8a5c3e5edbc30d2d4307870f802d18f00d83dc5141a6" dependencies = [ - "bitflags 2.13.1", + "bitflags 2.11.0", "objc2-core-foundation", "objc2-core-graphics", ] @@ -4496,7 +4913,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8" dependencies = [ - "bitflags 2.13.1", + "bitflags 2.11.0", "block2 0.5.1", "libc", "objc2 0.5.2", @@ -4508,7 +4925,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272" dependencies = [ - "bitflags 2.13.1", + "bitflags 2.11.0", "block2 0.6.2", "objc2 0.6.4", "objc2-core-foundation", @@ -4520,7 +4937,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6" dependencies = [ - "bitflags 2.13.1", + "bitflags 2.11.0", "block2 0.5.1", "objc2 0.5.2", "objc2-foundation 0.2.2", @@ -4532,7 +4949,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a" dependencies = [ - "bitflags 2.13.1", + "bitflags 2.11.0", "block2 0.5.1", "objc2 0.5.2", "objc2-foundation 0.2.2", @@ -4545,7 +4962,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d87d638e33c06f577498cbcc50491496a3ed4246998a7fbba7ccb98b1e7eab22" dependencies = [ - "bitflags 2.13.1", + "bitflags 2.11.0", "objc2 0.6.4", "objc2-core-foundation", "objc2-foundation 0.3.2", @@ -4574,9 +4991,9 @@ checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" [[package]] name = "orbclient" -version = "0.3.54" +version = "0.3.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a570f6bca41d29acb2139229a7c873ec99bc9a313bd10804081d89bfac8ff329" +checksum = "59aed3b33578edcfa1bc96a321d590d31832b6ad55a26f0313362ce687e9abd6" dependencies = [ "libc", "libredox", @@ -4728,7 +5145,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" dependencies = [ "phf_shared 0.11.3", - "rand 0.8.6", + "rand 0.8.5", ] [[package]] @@ -4795,18 +5212,18 @@ checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315" [[package]] name = "pin-project" -version = "1.1.13" +version = "1.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2466b2336ed02bcdca6b294417127b90ec92038d1d5c4fbeac971a922e0e0924" +checksum = "f1749c7ed4bcaf4c3d0a3efc28538844fb29bcdd7d2b67b2be7e20ba861ff517" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.13" +version = "1.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c96395f0a926bc13b1c17622aaddda1ecb55d49c8f1bf9777e4d877800a43f8b" +checksum = "d9b20ed30f105399776b9c883e68e536ef602a16ae6f596d2c473591d6ad64c6" dependencies = [ "proc-macro2", "quote", @@ -4837,10 +5254,38 @@ dependencies = [ ] [[package]] -name = "pkg-config" -version = "0.3.33" +name = "pipewire" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e" +checksum = "9688b89abf11d756499f7c6190711d6dbe5a3acdb30c8fbf001d6596d06a8d44" +dependencies = [ + "anyhow", + "bitflags 2.11.0", + "libc", + "libspa", + "libspa-sys", + "nix 0.30.1", + "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.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" [[package]] name = "plain" @@ -4867,7 +5312,7 @@ version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60769b8b31b2a9f263dae2776c37b1b28ae246943cf719eb6946a1db05128a61" dependencies = [ - "bitflags 2.13.1", + "bitflags 2.11.0", "crc32fast", "fdeflate", "flate2", @@ -4902,9 +5347,9 @@ checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" [[package]] name = "portable-atomic-util" -version = "0.2.7" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2a106d1259c23fac8e543272398ae0e3c0b8d33c88ed73d0cc71b0f1d902618" +checksum = "091397be61a01d4be58e7841595bd4bfedb15f1cd54977d79b8271e94ed799a3" dependencies = [ "portable-atomic", ] @@ -5005,16 +5450,37 @@ dependencies = [ ] [[package]] -name = "profiling" -version = "1.0.18" +name = "procfs" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d595e54a326bc53c1c197b32d295e14b169e3cfeaa8dc82b529f947fba6bcf5" +checksum = "25485360a54d6861439d60facef26de713b1e126bf015ec8f98239467a2b82f7" +dependencies = [ + "bitflags 2.11.0", + "procfs-core", + "rustix 1.1.4", +] + +[[package]] +name = "procfs-core" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6401bf7b6af22f78b563665d15a22e9aef27775b79b149a66ca022468a4e405" +dependencies = [ + "bitflags 2.11.0", + "hex", +] + +[[package]] +name = "profiling" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3eb8486b569e12e2c32ad3e204dbaba5e4b5b216e9367044f25f1dba42341773" [[package]] name = "pxfm" -version = "0.1.29" +version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0c5ccf5294c6ccd63a74f1565028353830a9c2f5eb0c682c355c471726a6e3f" +checksum = "b5a041e753da8b807c9255f28de81879c78c876392ff2469cde94799b2896b9d" [[package]] name = "quick-error" @@ -5024,14 +5490,23 @@ checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" [[package]] name = "quick-xml" -version = "0.39.4" +version = "0.38.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdcc8dd4e2f670d309a5f0e83fe36dfdc05af317008fea29144da1a2ac858e5e" +checksum = "b66c2058c55a409d601666cffe35f04333cf1013010882cec174a7467cd4e21c" dependencies = [ "memchr", "serde", ] +[[package]] +name = "quick-xml" +version = "0.39.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "958f21e8e7ceb5a1aa7fa87fab28e7c75976e0bfe7e23ff069e0a260f894067d" +dependencies = [ + "memchr", +] + [[package]] name = "quote" version = "1.0.45" @@ -5055,35 +5530,23 @@ checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" [[package]] name = "rand" -version = "0.8.6" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ - "libc", - "rand_chacha 0.3.1", "rand_core 0.6.4", ] [[package]] name = "rand" -version = "0.9.4" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" dependencies = [ - "rand_chacha 0.9.0", + "rand_chacha", "rand_core 0.9.5", ] -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core 0.6.4", -] - [[package]] name = "rand_chacha" version = "0.9.0" @@ -5099,9 +5562,6 @@ name = "rand_core" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom 0.2.17", -] [[package]] name = "rand_core" @@ -5141,32 +5601,22 @@ 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.13.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.13.1", + "bitflags 2.11.0", ] [[package]] name = "redox_syscall" -version = "0.8.1" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b44b894f2a6e36457d665d1e08c3866add6ed5e70050c1b4ba8a8ddedb02ce7" +checksum = "f450ad9c3b1da563fb6948a8e0fb0fb9269711c9c73d9ea1de5058c79c8d643a" dependencies = [ - "bitflags 2.13.1", + "bitflags 2.11.0", ] [[package]] @@ -5292,7 +5742,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db09040cc89e461f1a265139777a2bde7f8d8c67c4936f700c63ce3e2904d468" dependencies = [ "base64", - "bitflags 2.13.1", + "bitflags 2.11.0", "serde", "serde_derive", "unicode-ident", @@ -5304,7 +5754,7 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4147b952f3f819eca0e99527022f7d6a8d05f111aeb0a62960c74eb283bec8fc" dependencies = [ - "bitflags 2.13.1", + "bitflags 2.11.0", "once_cell", "serde", "serde_derive", @@ -5379,7 +5829,7 @@ version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ - "bitflags 2.13.1", + "bitflags 2.11.0", "errno", "libc", "linux-raw-sys 0.4.15", @@ -5392,7 +5842,7 @@ version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" dependencies = [ - "bitflags 2.13.1", + "bitflags 2.11.0", "errno", "libc", "linux-raw-sys 0.12.1", @@ -5411,7 +5861,7 @@ version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd3c7c96f8a08ee34eff8857b11b49b07d71d1c3f4e88f8a88d4c9e9f90b1702" dependencies = [ - "bitflags 2.13.1", + "bitflags 2.11.0", "bytemuck", "core_maths", "log", @@ -5423,12 +5873,6 @@ 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" @@ -5487,6 +5931,25 @@ dependencies = [ "tiny-skia", ] +[[package]] +name = "secret-service" +version = "5.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a62d7f86047af0077255a29494136b9aaaf697c76ff70b8e49cded4e2623c14" +dependencies = [ + "aes", + "cbc", + "futures-util", + "generic-array", + "getrandom 0.2.17", + "hkdf", + "num", + "once_cell", + "serde", + "sha2", + "zbus", +] + [[package]] name = "secure-string" version = "0.3.0" @@ -5577,13 +6040,21 @@ dependencies = [ ] [[package]] -name = "serde_with" -version = "3.20.0" +name = "serde_spanned" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e72c1c2cb7b223fafb600a619537a871c2818583d619401b785e7c0b746ccde2" +checksum = "6662b5879511e06e8999a8a235d848113e942c9124f211511b16466ee2995f26" +dependencies = [ + "serde_core", +] + +[[package]] +name = "serde_with" +version = "3.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd5414fad8e6907dbdd5bc441a50ae8d6e26151a03b1de04d89a5576de61d01f" dependencies = [ "base64", - "bs58", "chrono", "hex", "indexmap 1.9.3", @@ -5598,9 +6069,9 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.20.0" +version = "3.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b90c488738ecb4fb0262f41f43bc40efc5868d9fb744319ddf5f5317f417bfac" +checksum = "d3db8978e608f1fe7357e211969fd9abdcae80bac1ba7a3369bb7eb6b404eb65" dependencies = [ "darling 0.23.0", "proc-macro2", @@ -5608,12 +6079,6 @@ dependencies = [ "syn", ] -[[package]] -name = "sha1_smol" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbfa15b3dddfee50a0fff136974b3e1bde555604ba463834a7eb7deb6417705d" - [[package]] name = "sha2" version = "0.10.9" @@ -5683,9 +6148,9 @@ dependencies = [ [[package]] name = "siphasher" -version = "1.0.3" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ee5873ec9cce0195efcb7a4e9507a04cd49aec9c83d0389df45b1ef7ba2e649" +checksum = "b2aa850e253778c88a04c3d7323b043aeda9d3e30d5971937c1855769763678e" [[package]] name = "skrifa" @@ -5724,7 +6189,7 @@ version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0512da38f5e2b31201a93524adb8d3136276fa4fe4aafab4e1f727a82b534cc0" dependencies = [ - "bitflags 2.13.1", + "bitflags 2.11.0", "bytemuck", "calloop", "calloop-wayland-source", @@ -5751,6 +6216,7 @@ dependencies = [ [[package]] name = "smithay-clipboard" version = "0.8.0" +source = "git+https://github.com/pop-os/smithay-clipboard?tag=sctk-0.20#859b02c88f45c554049a67c6ddeec1692ce0e20b" dependencies = [ "libc", "raw-window-handle", @@ -5781,11 +6247,14 @@ dependencies = [ [[package]] name = "softbuffer" version = "0.4.1" +source = "git+https://github.com/pop-os/softbuffer?tag=cosmic-4.0#a3f77e251e7422803f693df6e3fc313c010c4dcb" dependencies = [ + "as-raw-xcb-connection", "bytemuck", "cfg_aliases", "cocoa", "core-graphics", + "drm 0.11.1", "fastrand", "foreign-types", "js-sys", @@ -5795,12 +6264,14 @@ dependencies = [ "raw-window-handle", "redox_syscall 0.5.18", "rustix 0.38.44", + "tiny-xlib", "wasm-bindgen", "wayland-backend", "wayland-client", "wayland-sys", "web-sys", "windows-sys 0.52.0", + "x11rb", ] [[package]] @@ -5809,7 +6280,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.13.1", + "bitflags 2.11.0", ] [[package]] @@ -5861,6 +6332,12 @@ dependencies = [ "syn", ] +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + [[package]] name = "svg_fmt" version = "0.4.5" @@ -5891,6 +6368,7 @@ dependencies = [ [[package]] name = "switcheroo-control" version = "0.1.0" +source = "git+https://github.com/pop-os/dbus-settings-bindings#507e342c21d3ce6ae41b1d4f3fa2f0ad5ee23e75" dependencies = [ "zbus", ] @@ -5926,6 +6404,19 @@ dependencies = [ "libc", ] +[[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" @@ -5938,6 +6429,12 @@ 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" @@ -6049,6 +6546,7 @@ dependencies = [ [[package]] name = "timedate-zbus" version = "0.1.0" +source = "git+https://github.com/pop-os/dbus-settings-bindings#507e342c21d3ce6ae41b1d4f3fa2f0ad5ee23e75" dependencies = [ "zbus", ] @@ -6079,6 +6577,19 @@ dependencies = [ "strict-num", ] +[[package]] +name = "tiny-xlib" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0324504befd01cab6e0c994f34b2ffa257849ee019d3fb3b64fb2c858887d89e" +dependencies = [ + "as-raw-xcb-connection", + "ctor-lite", + "libloading", + "pkg-config", + "tracing", +] + [[package]] name = "tinystr" version = "0.8.3" @@ -6113,9 +6624,9 @@ checksum = "b130bd8a58c163224b44e217b4239ca7b927d82bf6cc2fea1fc561d15056e3f7" [[package]] name = "tokio" -version = "1.52.3" +version = "1.51.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fc7f01b389ac15039e4dc9531aa973a135d7a4135281b12d7c1bc79fd57fffe" +checksum = "f66bf9585cda4b724d3e78ab34b73fb2bbaba9011b9bfdf69dc836382ea13b8c" dependencies = [ "bytes", "libc", @@ -6149,20 +6660,6 @@ dependencies = [ "futures-core", "pin-project-lite", "tokio", - "tokio-util", -] - -[[package]] -name = "tokio-util" -version = "0.7.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" -dependencies = [ - "bytes", - "futures-core", - "futures-sink", - "pin-project-lite", - "tokio", ] [[package]] @@ -6174,6 +6671,21 @@ dependencies = [ "serde", ] +[[package]] +name = "toml" +version = "1.1.2+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81f3d15e84cbcd896376e6730314d59fb5a87f31e4b038454184435cd57defee" +dependencies = [ + "indexmap 2.14.0", + "serde_core", + "serde_spanned", + "toml_datetime", + "toml_parser", + "toml_writer", + "winnow 1.0.1", +] + [[package]] name = "toml_datetime" version = "1.1.1+spec-1.1.0" @@ -6192,7 +6704,7 @@ dependencies = [ "indexmap 2.14.0", "toml_datetime", "toml_parser", - "winnow", + "winnow 1.0.1", ] [[package]] @@ -6201,9 +6713,15 @@ version = "1.1.2+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" dependencies = [ - "winnow", + "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" @@ -6292,9 +6810,9 @@ checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c" [[package]] name = "typenum" -version = "1.20.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40ce102ab67701b8526c123c1bab5cbe42d7040ccfd0f64af1a385808d2f43de" +checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" [[package]] name = "udev" @@ -6422,7 +6940,7 @@ checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" [[package]] name = "upower_dbus" version = "0.3.2" -source = "git+https://github.com/pop-os/dbus-settings-bindings#eed01dd3609e90e3c8cd043656734c500956c793" +source = "git+https://github.com/pop-os/dbus-settings-bindings#507e342c21d3ce6ae41b1d4f3fa2f0ad5ee23e75" dependencies = [ "serde", "serde_repr", @@ -6489,14 +7007,13 @@ checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" [[package]] name = "uuid" -version = "1.24.0" +version = "1.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf3923a6f5c4c6382e0b653c4117f48d631ea17f38ed86e2a828e6f7412f5239" +checksum = "5ac8b6f42ead25368cf5b098aeb3dc8a1a2c05a3eee8a9a1a68c640edbfc79d9" dependencies = [ "getrandom 0.4.2", "js-sys", "serde_core", - "sha1_smol", "wasm-bindgen", ] @@ -6506,6 +7023,12 @@ 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" @@ -6530,11 +7053,11 @@ checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasip2" -version = "1.0.3+wasi-0.2.9" +version = "1.0.2+wasi-0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20064672db26d7cdc89c7798c48a0fdfac8213434a1186e5ef29fd560ae223d6" +checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" dependencies = [ - "wit-bindgen 0.57.1", + "wit-bindgen", ] [[package]] @@ -6543,14 +7066,14 @@ version = "0.4.0+wasi-0.3.0-rc-2026-01-06" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" dependencies = [ - "wit-bindgen 0.51.0", + "wit-bindgen", ] [[package]] name = "wasm-bindgen" -version = "0.2.121" +version = "0.2.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49ace1d07c165b0864824eee619580c4689389afa9dc9ed3a4c75040d82e6790" +checksum = "0551fc1bb415591e3372d0bc4780db7e587d84e2a7e79da121051c5c4b89d0b0" dependencies = [ "cfg-if", "once_cell", @@ -6561,9 +7084,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.71" +version = "0.4.67" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96492d0d3ffba25305a7dc88720d250b1401d7edca02cc3bcd50633b424673b8" +checksum = "03623de6905b7206edd0a75f69f747f134b7f0a2323392d664448bf2d3c5d87e" dependencies = [ "js-sys", "wasm-bindgen", @@ -6571,9 +7094,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.121" +version = "0.2.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e68e6f4afd367a562002c05637acb8578ff2dea1943df76afb9e83d177c8578" +checksum = "7fbdf9a35adf44786aecd5ff89b4563a90325f9da0923236f6104e603c7e86be" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -6581,9 +7104,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.121" +version = "0.2.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d95a9ec35c64b2a7cb35d3fead40c4238d0940c86d107136999567a4703259f2" +checksum = "dca9693ef2bab6d4e6707234500350d8dad079eb508dca05530c85dc3a529ff2" dependencies = [ "bumpalo", "proc-macro2", @@ -6594,9 +7117,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.121" +version = "0.2.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4e0100b01e9f0d03189a92b96772a1fb998639d981193d7dbab487302513441" +checksum = "39129a682a6d2d841b6c429d0c51e5cb0ed1a03829d8b3d1e69a011e62cb3d3b" dependencies = [ "unicode-ident", ] @@ -6629,7 +7152,7 @@ version = "0.244.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" dependencies = [ - "bitflags 2.13.1", + "bitflags 2.11.0", "hashbrown 0.15.5", "indexmap 2.14.0", "semver", @@ -6669,7 +7192,7 @@ version = "0.31.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "645c7c96bb74690c3189b5c9cb4ca1627062bb23693a4fad9d8c3de958260144" dependencies = [ - "bitflags 2.13.1", + "bitflags 2.11.0", "rustix 1.1.4", "wayland-backend", "wayland-scanner", @@ -6681,7 +7204,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "625c5029dbd43d25e6aa9615e88b829a5cad13b2819c4ae129fdbb7c31ab4c7e" dependencies = [ - "bitflags 2.13.1", + "bitflags 2.11.0", "cursor-icon", "wayland-backend", ] @@ -6703,7 +7226,7 @@ version = "0.32.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "563a85523cade2429938e790815fd7319062103b9f4a2dc806e9b53b95982d8f" dependencies = [ - "bitflags 2.13.1", + "bitflags 2.11.0", "wayland-backend", "wayland-client", "wayland-scanner", @@ -6716,7 +7239,7 @@ version = "20250721.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40a1f863128dcaaec790d7b4b396cc9b9a7a079e878e18c47e6c2d2c5a8dcbb1" dependencies = [ - "bitflags 2.13.1", + "bitflags 2.11.0", "wayland-backend", "wayland-client", "wayland-protocols", @@ -6729,7 +7252,7 @@ version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e9567599ef23e09b8dad6e429e5738d4509dfc46b3b21f32841a304d16b29c8" dependencies = [ - "bitflags 2.13.1", + "bitflags 2.11.0", "wayland-backend", "wayland-client", "wayland-protocols", @@ -6742,7 +7265,7 @@ version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b6d8cf1eb2c1c31ed1f5643c88a6e53538129d4af80030c8cabd1f9fa884d91" dependencies = [ - "bitflags 2.13.1", + "bitflags 2.11.0", "wayland-backend", "wayland-client", "wayland-protocols", @@ -6755,7 +7278,7 @@ version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eb04e52f7836d7c7976c78ca0250d61e33873c34156a2a1fc9474828ec268234" dependencies = [ - "bitflags 2.13.1", + "bitflags 2.11.0", "wayland-backend", "wayland-client", "wayland-protocols", @@ -6770,7 +7293,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c324a910fd86ebdc364a3e61ec1f11737d3b1d6c273c0239ee8ff4bc0d24b4a" dependencies = [ "proc-macro2", - "quick-xml", + "quick-xml 0.39.2", "quote", ] @@ -6780,7 +7303,7 @@ version = "0.31.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cc1846eb04c49182e04f4a099e2a830a2b745610bbc1d61246e206f29c7000a0" dependencies = [ - "bitflags 2.13.1", + "bitflags 2.11.0", "downcast-rs", "rustix 1.1.4", "wayland-backend", @@ -6801,9 +7324,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.98" +version = "0.3.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b572dff8bcf38bad0fa19729c89bb5748b2b9b1d8be70cf90df697e3a8f32aa" +checksum = "cd70027e39b12f0849461e08ffc50b9cd7688d942c1c8e3c7b22273236b4dd0a" dependencies = [ "js-sys", "wasm-bindgen", @@ -6827,13 +7350,12 @@ checksum = "a28ac98ddc8b9274cb41bb4d9d4d5c425b6020c50c46f25559911905610b4a88" [[package]] name = "wgpu" -version = "28.0.0" +version = "27.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9cb534d5ffd109c7d1135f34cdae29e60eab94855a625dcfe1705f8bc7ad79f" +checksum = "bfe68bac7cde125de7a731c3400723cadaaf1703795ad3f4805f187459cd7a77" dependencies = [ "arrayvec", - "bitflags 2.13.1", - "bytemuck", + "bitflags 2.11.0", "cfg-if", "cfg_aliases", "document-features", @@ -6857,14 +7379,14 @@ dependencies = [ [[package]] name = "wgpu-core" -version = "28.0.1" +version = "27.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d23f4642f53f666adcfd2d3218ab174d1e6681101aef18696b90cbe64d1c10f9" +checksum = "27a75de515543b1897b26119f93731b385a19aea165a1ec5f0e3acecc229cae7" dependencies = [ "arrayvec", "bit-set", "bit-vec", - "bitflags 2.13.1", + "bitflags 2.11.0", "bytemuck", "cfg_aliases", "document-features", @@ -6889,42 +7411,42 @@ dependencies = [ [[package]] name = "wgpu-core-deps-apple" -version = "28.0.0" +version = "27.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87b7b696b918f337c486bf93142454080a32a37832ba8a31e4f48221890047da" +checksum = "0772ae958e9be0c729561d5e3fd9a19679bcdfb945b8b1a1969d9bfe8056d233" dependencies = [ "wgpu-hal", ] [[package]] name = "wgpu-core-deps-emscripten" -version = "28.0.0" +version = "27.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34b251c331f84feac147de3c4aa3aa45112622a95dd7ee1b74384fa0458dbd79" +checksum = "b06ac3444a95b0813ecfd81ddb2774b66220b264b3e2031152a4a29fda4da6b5" dependencies = [ "wgpu-hal", ] [[package]] name = "wgpu-core-deps-windows-linux-android" -version = "28.0.0" +version = "27.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68ca976e72b2c9964eb243e281f6ce7f14a514e409920920dcda12ae40febaae" +checksum = "71197027d61a71748e4120f05a9242b2ad142e3c01f8c1b47707945a879a03c3" dependencies = [ "wgpu-hal", ] [[package]] name = "wgpu-hal" -version = "28.0.1" +version = "27.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d6cb474beb218824dcc9e1ce679d973f719262789bfb27407da560cac20eeb" +checksum = "5b21cb61c57ee198bc4aff71aeadff4cbb80b927beb912506af9c780d64313ce" dependencies = [ "android_system_properties", "arrayvec", "ash", "bit-set", - "bitflags 2.13.1", + "bitflags 2.11.0", "block", "bytemuck", "cfg-if", @@ -6932,6 +7454,7 @@ dependencies = [ "core-graphics-types 0.2.0", "glow", "glutin_wgl_sys", + "gpu-alloc", "gpu-allocator", "gpu-descriptor", "hashbrown 0.16.1", @@ -6958,20 +7481,21 @@ dependencies = [ "wasm-bindgen", "web-sys", "wgpu-types", - "windows 0.62.2", - "windows-core 0.62.2", + "windows 0.58.0", + "windows-core 0.58.0", ] [[package]] name = "wgpu-types" -version = "28.0.0" +version = "27.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e18308757e594ed2cd27dddbb16a139c42a683819d32a2e0b1b0167552f5840c" +checksum = "afdcf84c395990db737f2dd91628706cb31e86d72e53482320d368e52b5da5eb" dependencies = [ - "bitflags 2.13.1", + "bitflags 2.11.0", "bytemuck", "js-sys", "log", + "thiserror 2.0.18", "web-sys", ] @@ -7009,10 +7533,12 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "window_clipboard" version = "0.4.1" +source = "git+https://github.com/pop-os/window_clipboard.git?tag=sctk-0.20#f68595ee0e62fbd6589f4709b5aaa5c3c7ea5f6c" dependencies = [ "clipboard-win", "clipboard_macos", "clipboard_wayland", + "clipboard_x11", "dnd", "mime 0.1.0", "raw-window-handle", @@ -7021,27 +7547,25 @@ dependencies = [ [[package]] name = "windows" -version = "0.61.3" +version = "0.58.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893" +checksum = "dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6" dependencies = [ - "windows-collections 0.2.0", - "windows-core 0.61.2", - "windows-future 0.2.1", - "windows-link 0.1.3", - "windows-numerics 0.2.0", + "windows-core 0.58.0", + "windows-targets 0.52.6", ] [[package]] name = "windows" -version = "0.62.2" +version = "0.61.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "527fadee13e0c05939a6a05d5bd6eec6cd2e3dbd648b9f8e447c6518133d8580" +checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893" dependencies = [ - "windows-collections 0.3.2", - "windows-core 0.62.2", - "windows-future 0.3.2", - "windows-numerics 0.3.1", + "windows-collections", + "windows-core 0.61.2", + "windows-future", + "windows-link 0.1.3", + "windows-numerics", ] [[package]] @@ -7054,12 +7578,16 @@ dependencies = [ ] [[package]] -name = "windows-collections" -version = "0.3.2" +name = "windows-core" +version = "0.58.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23b2d95af1a8a14a3c7367e1ed4fc9c20e0a26e79551b1454d72583c97cc6610" +checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99" dependencies = [ - "windows-core 0.62.2", + "windows-implement 0.58.0", + "windows-interface 0.58.0", + "windows-result 0.2.0", + "windows-strings 0.1.0", + "windows-targets 0.52.6", ] [[package]] @@ -7068,8 +7596,8 @@ version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" dependencies = [ - "windows-implement", - "windows-interface", + "windows-implement 0.60.2", + "windows-interface 0.59.3", "windows-link 0.1.3", "windows-result 0.3.4", "windows-strings 0.4.2", @@ -7081,8 +7609,8 @@ version = "0.62.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" dependencies = [ - "windows-implement", - "windows-interface", + "windows-implement 0.60.2", + "windows-interface 0.59.3", "windows-link 0.2.1", "windows-result 0.4.1", "windows-strings 0.5.1", @@ -7096,18 +7624,18 @@ checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e" dependencies = [ "windows-core 0.61.2", "windows-link 0.1.3", - "windows-threading 0.1.0", + "windows-threading", ] [[package]] -name = "windows-future" -version = "0.3.2" +name = "windows-implement" +version = "0.58.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cb" +checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" dependencies = [ - "windows-core 0.62.2", - "windows-link 0.2.1", - "windows-threading 0.2.1", + "proc-macro2", + "quote", + "syn", ] [[package]] @@ -7121,6 +7649,17 @@ dependencies = [ "syn", ] +[[package]] +name = "windows-interface" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "windows-interface" version = "0.59.3" @@ -7155,13 +7694,12 @@ dependencies = [ ] [[package]] -name = "windows-numerics" -version = "0.3.1" +name = "windows-result" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e2e40844ac143cdb44aead537bbf727de9b044e107a0f1220392177d15b0f26" +checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" dependencies = [ - "windows-core 0.62.2", - "windows-link 0.2.1", + "windows-targets 0.52.6", ] [[package]] @@ -7182,6 +7720,16 @@ dependencies = [ "windows-link 0.2.1", ] +[[package]] +name = "windows-strings" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" +dependencies = [ + "windows-result 0.2.0", + "windows-targets 0.52.6", +] + [[package]] name = "windows-strings" version = "0.4.2" @@ -7302,15 +7850,6 @@ dependencies = [ "windows-link 0.1.3", ] -[[package]] -name = "windows-threading" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3949bd5b99cafdf1c7ca86b43ca564028dfe27d66958f2470940f73d86d75b37" -dependencies = [ - "windows-link 0.2.1", -] - [[package]] name = "windows_aarch64_gnullvm" version = "0.48.5" @@ -7452,8 +7991,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#a610ac9c7a72b39ff102ed4d946291618dc725b6" dependencies = [ - "bitflags 2.13.1", + "bitflags 2.11.0", "cfg_aliases", "cursor-icon", "dpi", @@ -7471,14 +8011,16 @@ dependencies = [ "winit-wayland", "winit-web", "winit-win32", + "winit-x11", ] [[package]] name = "winit-android" version = "0.31.0-beta.2" +source = "git+https://github.com/pop-os/winit.git?tag=cosmic-0.14#a610ac9c7a72b39ff102ed4d946291618dc725b6" dependencies = [ "android-activity", - "bitflags 2.13.1", + "bitflags 2.11.0", "dpi", "ndk", "raw-window-handle", @@ -7490,8 +8032,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#a610ac9c7a72b39ff102ed4d946291618dc725b6" dependencies = [ - "bitflags 2.13.1", + "bitflags 2.11.0", "block2 0.6.2", "dispatch2", "dpi", @@ -7511,6 +8054,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#a610ac9c7a72b39ff102ed4d946291618dc725b6" dependencies = [ "memmap2 0.9.10", "objc2 0.6.4", @@ -7518,14 +8062,16 @@ dependencies = [ "smol_str", "tracing", "winit-core", + "x11-dl", "xkbcommon-dl", ] [[package]] name = "winit-core" version = "0.31.0-beta.2" +source = "git+https://github.com/pop-os/winit.git?tag=cosmic-0.14#a610ac9c7a72b39ff102ed4d946291618dc725b6" dependencies = [ - "bitflags 2.13.1", + "bitflags 2.11.0", "cursor-icon", "dpi", "keyboard-types", @@ -7537,13 +8083,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#a610ac9c7a72b39ff102ed4d946291618dc725b6" dependencies = [ - "bitflags 2.13.1", + "bitflags 2.11.0", "dpi", "libredox", "orbclient", "raw-window-handle", - "redox_event", + "redox_syscall 0.7.4", "smol_str", "tracing", "winit-core", @@ -7552,8 +8099,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#a610ac9c7a72b39ff102ed4d946291618dc725b6" dependencies = [ - "bitflags 2.13.1", + "bitflags 2.11.0", "block2 0.6.2", "dispatch2", "dpi", @@ -7571,9 +8119,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#a610ac9c7a72b39ff102ed4d946291618dc725b6" dependencies = [ "ahash", - "bitflags 2.13.1", + "bitflags 2.11.0", "calloop", "cursor-icon", "dpi", @@ -7596,9 +8145,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#a610ac9c7a72b39ff102ed4d946291618dc725b6" dependencies = [ "atomic-waker", - "bitflags 2.13.1", + "bitflags 2.11.0", "concurrent-queue", "cursor-icon", "dpi", @@ -7617,8 +8167,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#a610ac9c7a72b39ff102ed4d946291618dc725b6" dependencies = [ - "bitflags 2.13.1", + "bitflags 2.11.0", "cursor-icon", "dpi", "raw-window-handle", @@ -7629,11 +8180,43 @@ dependencies = [ "winit-core", ] +[[package]] +name = "winit-x11" +version = "0.31.0-beta.2" +source = "git+https://github.com/pop-os/winit.git?tag=cosmic-0.14#a610ac9c7a72b39ff102ed4d946291618dc725b6" +dependencies = [ + "bitflags 2.11.0", + "bytemuck", + "calloop", + "cursor-icon", + "dpi", + "libc", + "percent-encoding", + "raw-window-handle", + "rustix 1.1.4", + "smol_str", + "tracing", + "winit-common", + "winit-core", + "x11-dl", + "x11rb", + "xkbcommon-dl", +] + [[package]] name = "winnow" -version = "1.0.3" +version = "0.7.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0592e1c9d151f854e6fd382574c3a0855250e1d9b2f99d9281c6e6391af352f1" +checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945" +dependencies = [ + "memchr", +] + +[[package]] +name = "winnow" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09dac053f1cd375980747450bfc7250c264eaae0583872e845c0c7cd578872b5" dependencies = [ "memchr", ] @@ -7647,12 +8230,6 @@ 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" @@ -7702,7 +8279,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" dependencies = [ "anyhow", - "bitflags 2.13.1", + "bitflags 2.11.0", "indexmap 2.14.0", "log", "serde", @@ -7747,6 +8324,39 @@ dependencies = [ "either", ] +[[package]] +name = "x11-dl" +version = "2.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38735924fedd5314a6e548792904ed8c6de6636285cb9fec04d5b1db85c1516f" +dependencies = [ + "libc", + "once_cell", + "pkg-config", +] + +[[package]] +name = "x11rb" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9993aa5be5a26815fe2c3eacfc1fde061fc1a1f094bf1ad2a18bf9c495dd7414" +dependencies = [ + "as-raw-xcb-connection", + "gethostname", + "libc", + "libloading", + "once_cell", + "rustix 1.1.4", + "x11rb-protocol", + "xcursor", +] + +[[package]] +name = "x11rb-protocol" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea6fc2961e4ef194dcbfe56bb845534d0dc8098940c7e5c012a258bfec6701bd" + [[package]] name = "xcursor" version = "0.3.10" @@ -7761,7 +8371,8 @@ checksum = "2fb433233f2df9344722454bc7e96465c9d03bff9d77c248f9e7523fe79585b5" [[package]] name = "xdg-shell-wrapper-config" -version = "1.3.0" +version = "0.1.0" +source = "git+https://github.com/pop-os/cosmic-panel#d518c7d25ef96f1a9696aa8cce008656bf66ece4" dependencies = [ "serde", "wayland-protocols-wlr", @@ -7828,7 +8439,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d039de8032a9a8856a6be89cea3e5d12fdd82306ab7c94d74e6deab2460651c5" dependencies = [ - "bitflags 2.13.1", + "bitflags 2.11.0", "dlib", "log", "once_cell", @@ -7846,9 +8457,9 @@ dependencies = [ [[package]] name = "xml" -version = "1.3.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "636f85e5ca6488e96401b61eb7de54f4e44755c988af0f52cf90230c312a1a89" +checksum = "b8aa498d22c9bbaf482329839bc5620c46be275a19a812e9a22a2b07529a642a" [[package]] name = "xml-rs" @@ -7908,9 +8519,9 @@ dependencies = [ [[package]] name = "zbus" -version = "5.18.0" +version = "5.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe18fb60dc696039e738717b76eaea21e7a4489bbb1885020b43c94236d7e98a" +checksum = "ca82f95dbd3943a40a53cfded6c2d0a2ca26192011846a1810c4256ef92c60bc" dependencies = [ "async-broadcast", "async-executor", @@ -7936,7 +8547,7 @@ dependencies = [ "uds_windows", "uuid", "windows-sys 0.61.2", - "winnow", + "winnow 0.7.15", "zbus_macros", "zbus_names", "zvariant", @@ -7968,9 +8579,9 @@ dependencies = [ [[package]] name = "zbus_macros" -version = "5.18.0" +version = "5.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe96480bed92df2b442a1a30df364e12d08eed03aeb061f2b8dc6afb2be91119" +checksum = "897e79616e84aac4b2c46e9132a4f63b93105d54fe8c0e8f6bffc21fa8d49222" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -7983,22 +8594,22 @@ dependencies = [ [[package]] name = "zbus_names" -version = "4.3.4" +version = "4.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8bf88b4a3ff53e883001e0e0115b297a9d53c31b9c1edd2bfdd853e3428624e" +checksum = "ffd8af6d5b78619bab301ff3c560a5bd22426150253db278f164d6cf3b72c50f" dependencies = [ "serde", - "winnow", + "winnow 0.7.15", "zvariant", ] [[package]] name = "zbus_xml" -version = "5.1.1" +version = "5.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8067892e940ed1727dea64690378601603b31d62dfde019a5335fbb7c0e0ed9" +checksum = "441a0064125265655bccc3a6af6bef56814d9277ac83fce48b1cd7e160b80eac" dependencies = [ - "quick-xml", + "quick-xml 0.38.4", "serde", "zbus_names", "zvariant", @@ -8032,9 +8643,9 @@ dependencies = [ [[package]] name = "zerofrom" -version = "0.1.8" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ec05a11813ea801ff6d75110ad09cd0824ddba17dfe17128ea0d5f68e6c5272" +checksum = "69faa1f2a1ea75661980b013019ed6687ed0e83d069bc1114e2cc74c6c04c4df" dependencies = [ "zerofrom-derive", ] @@ -8092,73 +8703,6 @@ dependencies = [ "syn", ] -[[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", -] - -[[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" @@ -8197,24 +8741,24 @@ dependencies = [ [[package]] name = "zvariant" -version = "5.13.1" +version = "5.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bee2a0bcd2a907786a456fff45aaaaf54c9ba5f50b71ae9ec1a4edd200c94911" +checksum = "5708299b21903bbe348e94729f22c49c55d04720a004aa350f1f9c122fd2540b" dependencies = [ "endi", "enumflags2", "serde", "url", - "winnow", + "winnow 0.7.15", "zvariant_derive", "zvariant_utils", ] [[package]] name = "zvariant_derive" -version = "5.13.1" +version = "5.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38a708216a18780796770bfe3f4739c7c83a3e8f789b755534bbbc06e4e23e12" +checksum = "5b59b012ebe9c46656f9cc08d8da8b4c726510aef12559da3e5f1bf72780752c" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -8225,21 +8769,13 @@ dependencies = [ [[package]] name = "zvariant_utils" -version = "3.5.0" +version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90cb9383f9b45290407a1258b202d3f8f01db719eb60b4e4055c6375af4fc7c7" +checksum = "f75c23a64ef8f40f13a6989991e643554d9bef1d682a281160cf0c1bc389c5e9" dependencies = [ "proc-macro2", "quote", "serde", "syn", - "winnow", + "winnow 0.7.15", ] - -[[patch.unused]] -name = "cosmic-settings-airplane-mode-subscription" -version = "1.0.7" - -[[patch.unused]] -name = "winit-x11" -version = "0.31.0-beta.2" diff --git a/Cargo.toml b/Cargo.toml index ccbe504a..5e6940de 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,11 +25,11 @@ resolver = "3" [workspace.dependencies] anyhow = "1.0.102" -cctk = { package = "cosmic-client-toolkit", path = "../cosmic-protocols/client-toolkit" } +cctk = { git = "https://github.com/pop-os/cosmic-protocols", package = "cosmic-client-toolkit", rev = "d0e95be" } cosmic-applets-config = { path = "cosmic-applets-config" } -cosmic-protocols = { path = "../cosmic-protocols", default-features = false, features = [ +cosmic-protocols = { git = "https://github.com/pop-os/cosmic-protocols", default-features = false, features = [ "client", -]} +], rev = "d0e95be" } futures = "0.3" futures-util = "0.3" @@ -38,7 +38,7 @@ i18n-embed = { version = "0.16.0", features = [ "desktop-requester", ] } i18n-embed-fl = "0.10" -cosmic = { package = "libcosmic-yoda", path = "../libcosmic", default-features = false, features = [ +libcosmic = { git = "https://github.com/pop-os/libcosmic", default-features = false, features = [ "applet", "applet-token", "dbus-config", @@ -48,7 +48,6 @@ cosmic = { package = "libcosmic-yoda", path = "../libcosmic", default-features = "desktop-systemd-scope", "winit", ] } -cosmic-comp-config = { path = "../cosmic-comp/cosmic-comp-config" } rust-embed = "8.11.0" rust-embed-utils = "8.11.0" rustc-hash = "2.1" @@ -59,14 +58,14 @@ tracing-subscriber = { version = "0.3.22", features = ["env-filter"] } tracing-log = "0.2.0" tokio = { version = "1.49.0", features = ["full"] } # cosmic-config = { path = "../libcosmic/cosmic-config" } -cosmic-config = { path = "../libcosmic/cosmic-config" } +cosmic-config = { git = "https://github.com/pop-os/libcosmic" } serde = { version = "1.0.228", features = ["derive"] } -zlink = "0.5.0" [profile.release] -opt-level = 3 -panic = "abort" -lto = "thin" +# opt-level = 3 +# panic = "abort" +# lto = "thin" +opt-level = 1 [workspace.metadata.cargo-machete] ignored = ["libcosmic"] @@ -83,48 +82,12 @@ ignored = ["libcosmic"] # winit = { git = "https://github.com/rust-windowing/winit.git", rev = "241b7a80bba96c91fa3901729cd5dec66abb9be4" } # winit = { path = "../winit" } -[patch."https://github.com/pop-os/libcosmic"] -cosmic-config = { path = "/home/lionel/Projets/COSMIC/libcosmic/cosmic-config" } -cosmic-theme = { path = "/home/lionel/Projets/COSMIC/libcosmic/cosmic-theme" } -iced = { path = "/home/lionel/Projets/COSMIC/libcosmic/iced" } -iced_accessibility = { path = "/home/lionel/Projets/COSMIC/libcosmic/iced/accessibility" } -iced_core = { path = "/home/lionel/Projets/COSMIC/libcosmic/iced/core" } -iced_futures = { path = "/home/lionel/Projets/COSMIC/libcosmic/iced/futures" } -iced_graphics = { path = "/home/lionel/Projets/COSMIC/libcosmic/iced/graphics" } -iced_renderer = { path = "/home/lionel/Projets/COSMIC/libcosmic/iced/renderer" } -iced_runtime = { path = "/home/lionel/Projets/COSMIC/libcosmic/iced/runtime" } -iced_tiny_skia = { path = "/home/lionel/Projets/COSMIC/libcosmic/iced/tiny_skia" } -iced_wgpu = { path = "/home/lionel/Projets/COSMIC/libcosmic/iced/wgpu" } -iced_widget = { path = "/home/lionel/Projets/COSMIC/libcosmic/iced/widget" } -iced_winit = { path = "/home/lionel/Projets/COSMIC/libcosmic/iced/winit" } - [patch."https://github.com/smithay/client-toolkit.git"] sctk = { package = "smithay-client-toolkit", version = "0.20.0" } [patch."https://github.com/pop-os/cosmic-protocols"] -cosmic-protocols = { path = "/home/lionel/Projets/COSMIC/cosmic-protocols" } -cosmic-client-toolkit = { path = "/home/lionel/Projets/COSMIC/cosmic-protocols/client-toolkit" } - -[patch."https://github.com/pop-os/cosmic-panel"] -cosmic-panel-config = { path = "/home/lionel/Projets/COSMIC/cosmic-panel/cosmic-panel-config" } -xdg-shell-wrapper-config = { path = "/home/lionel/Projets/COSMIC/cosmic-panel/xdg-shell-wrapper-config" } - -[patch."https://github.com/pop-os/cosmic-notifications"] -cosmic-notifications-config = { path = "/home/lionel/Projets/COSMIC/cosmic-notifications/cosmic-notifications-config" } -cosmic-notifications-util = { path = "/home/lionel/Projets/COSMIC/cosmic-notifications/cosmic-notifications-util" } - -[patch."https://github.com/pop-os/cosmic-settings"] -cosmic-settings-a11y-manager-subscription = { path = "/home/lionel/Projets/COSMIC/cosmic-settings/subscriptions/a11y-manager" } -cosmic-settings-accessibility-subscription = { path = "/home/lionel/Projets/COSMIC/cosmic-settings/subscriptions/accessibility" } -cosmic-settings-airplane-mode-subscription = { path = "/home/lionel/Projets/COSMIC/cosmic-settings/subscriptions/airplane-mode" } -cosmic-settings-daemon-subscription = { path = "/home/lionel/Projets/COSMIC/cosmic-settings/subscriptions/settings-daemon" } -cosmic-settings-upower-subscription = { path = "/home/lionel/Projets/COSMIC/cosmic-settings/subscriptions/upower" } - -[patch."https://github.com/pop-os/cosmic-settings/"] -cosmic-settings-airplane-mode-subscription = { path = "/home/lionel/Projets/COSMIC/cosmic-settings/subscriptions/airplane-mode" } - -[patch."https://github.com/pop-os/cosmic-text.git"] -cosmic-text = { path = "../cosmic-text" } +cosmic-protocols = { git = "https://github.com/pop-os/cosmic-protocols//", branch = "main" } +cosmic-client-toolkit = { git = "https://github.com/pop-os/cosmic-protocols//", branch = "main" } # [patch.'https://github.com/pop-os/dbus-settings-bindings'] # cosmic-dbus-networkmanager = { path = "../dbus-settings-bindings/networkmanager" } diff --git a/cosmic-app-list/Cargo.toml b/cosmic-app-list/Cargo.toml index 4616aa97..7c398b97 100644 --- a/cosmic-app-list/Cargo.toml +++ b/cosmic-app-list/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cosmic-app-list" -version = "1.3.0" +version = "1.0.2" edition = "2024" license = "GPL-3.0-only" @@ -13,13 +13,13 @@ futures.workspace = true i18n-embed.workspace = true i18n-embed-fl.workspace = true image = { version = "0.25.9", default-features = false } -cosmic.workspace = true +libcosmic.workspace = true memmap2 = "0.9.10" fastrand = "2.3.0" rust-embed.workspace = true rustix.workspace = true rustc-hash.workspace = true -switcheroo-control = { path = "../../dbus-settings-bindings/switcheroo-control" } +switcheroo-control = { git = "https://github.com/pop-os/dbus-settings-bindings" } tokio.workspace = true tracing-log.workspace = true tracing-subscriber.workspace = true diff --git a/cosmic-app-list/cosmic-app-list-config/Cargo.toml b/cosmic-app-list/cosmic-app-list-config/Cargo.toml index badf15ab..08d87f2e 100644 --- a/cosmic-app-list/cosmic-app-list-config/Cargo.toml +++ b/cosmic-app-list/cosmic-app-list-config/Cargo.toml @@ -1,10 +1,10 @@ [package] name = "cosmic-app-list-config" -version = "1.3.0" +version = "1.0.2" edition = "2024" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -cosmic.workspace = true +libcosmic.workspace = true serde = { workspace = true, features = ["derive"] } diff --git a/cosmic-app-list/i18n/ca/cosmic_app_list.ftl b/cosmic-app-list/i18n/ca/cosmic_app_list.ftl index 340e296c..e69de29b 100644 --- a/cosmic-app-list/i18n/ca/cosmic_app_list.ftl +++ b/cosmic-app-list/i18n/ca/cosmic_app_list.ftl @@ -1,8 +0,0 @@ -new-window = Nova Finestra -quit = Surt -run = Executa -run-on = Executa a { $gpu } -quit-all = Surt de totes -run-on-default = (Per defecte) -cosmic-app-list = Safata d'Aplicacions -pin = Ancora a la safata diff --git a/cosmic-app-list/i18n/el/cosmic_app_list.ftl b/cosmic-app-list/i18n/el/cosmic_app_list.ftl index e77f522a..6a425d48 100644 --- a/cosmic-app-list/i18n/el/cosmic_app_list.ftl +++ b/cosmic-app-list/i18n/el/cosmic_app_list.ftl @@ -1,8 +1 @@ new-window = Νέο παράθυρο -quit = Έξοδος -cosmic-app-list = Περιοχή εφαρμογών -run = Εκτέλεση -run-on = Εκτέλεση με { $gpu } -quit-all = Έξοδος από όλα -run-on-default = (Προεπιλογή) -pin = Καρφίτσωμα στην περιοχή εφαρμογών diff --git a/cosmic-app-list/i18n/en/cosmic_app_list.ftl b/cosmic-app-list/i18n/en/cosmic_app_list.ftl index 028a1558..c412cd51 100644 --- a/cosmic-app-list/i18n/en/cosmic_app_list.ftl +++ b/cosmic-app-list/i18n/en/cosmic_app_list.ftl @@ -5,15 +5,4 @@ quit-all = Quit All new-window = New Window run = Run run-on = Run on {$gpu} -run-on-default = (Default) -edit-launcher = Edit launcher -launcher-name = Name -launcher-command = Command -launcher-icon = Icon -launcher-icon-theme = Theme -launcher-icon-search = Search icons -launcher-icon-catalog-loading = Loading icons -launcher-icon-catalog-empty = No icons -launcher-icons = icons -save = Save -cancel = Cancel +run-on-default = (Default) \ No newline at end of file diff --git a/cosmic-app-list/i18n/fr/cosmic_app_list.ftl b/cosmic-app-list/i18n/fr/cosmic_app_list.ftl index 16bb7465..a065b203 100644 --- a/cosmic-app-list/i18n/fr/cosmic_app_list.ftl +++ b/cosmic-app-list/i18n/fr/cosmic_app_list.ftl @@ -3,17 +3,6 @@ pin = Épingler à la barre d'applis quit = Quitter quit-all = Tout quitter new-window = Nouvelle fenêtre -run = Exécuter +run = Lancer run-on = Lancer avec { $gpu } run-on-default = (Défaut) -edit-launcher = Modifier le lanceur -launcher-name = Nom -launcher-command = Commande -launcher-icon = Icône -launcher-icon-theme = Thème -launcher-icon-search = Rechercher une icône -launcher-icon-catalog-loading = Chargement des icônes -launcher-icon-catalog-empty = Aucune icône -launcher-icons = icônes -save = Enregistrer -cancel = Annuler diff --git a/cosmic-app-list/i18n/kk/cosmic_app_list.ftl b/cosmic-app-list/i18n/kk/cosmic_app_list.ftl index a1b070a2..c0e5a033 100644 --- a/cosmic-app-list/i18n/kk/cosmic_app_list.ftl +++ b/cosmic-app-list/i18n/kk/cosmic_app_list.ftl @@ -1,5 +1,5 @@ quit = Шығу -run = Орындау +run = Іске қосу run-on = { $gpu } арқылы іске қосу run-on-default = (Әдепкі) cosmic-app-list = Қолданбалар сөресі diff --git a/cosmic-app-list/i18n/kmr/cosmic_app_list.ftl b/cosmic-app-list/i18n/kmr/cosmic_app_list.ftl index 793d1880..e69de29b 100644 --- a/cosmic-app-list/i18n/kmr/cosmic_app_list.ftl +++ b/cosmic-app-list/i18n/kmr/cosmic_app_list.ftl @@ -1,8 +0,0 @@ -run = Bixebitîne -run-on = Li ser { $gpu } bixebitîne -run-on-default = (Berdest) -pin = Bo destgeha sepanan bi dar bixe -quit = Biqedîne -quit-all = Hemûyan biqedîne -new-window = Çarçoveya nû -cosmic-app-list = Destgeha sepanan diff --git a/cosmic-app-list/i18n/lo/cosmic_app_list.ftl b/cosmic-app-list/i18n/lo/cosmic_app_list.ftl deleted file mode 100644 index e69de29b..00000000 diff --git a/cosmic-app-list/i18n/ru/cosmic_app_list.ftl b/cosmic-app-list/i18n/ru/cosmic_app_list.ftl index c1195520..a7d98a5b 100644 --- a/cosmic-app-list/i18n/ru/cosmic_app_list.ftl +++ b/cosmic-app-list/i18n/ru/cosmic_app_list.ftl @@ -3,6 +3,6 @@ pin = Закрепить на панели приложений quit = Выйти quit-all = Завершить все new-window = Новое окно -run = Выполнить +run = Запустить run-on = Запустить на { $gpu } run-on-default = (По умолчанию) diff --git a/cosmic-app-list/i18n/sr/cosmic_app_list.ftl b/cosmic-app-list/i18n/sr/cosmic_app_list.ftl index 4cf077b9..e69de29b 100644 --- a/cosmic-app-list/i18n/sr/cosmic_app_list.ftl +++ b/cosmic-app-list/i18n/sr/cosmic_app_list.ftl @@ -1,8 +0,0 @@ -new-window = Нови прозор -quit = Изађи -run = Покрени -run-on = Покрени на { $gpu } -quit-all = Изађи из свега -run-on-default = (подразумевано) -cosmic-app-list = Системска касета -pin = Закачи у системску касету diff --git a/cosmic-app-list/i18n/uk/cosmic_app_list.ftl b/cosmic-app-list/i18n/uk/cosmic_app_list.ftl index ec1833cd..aed01bf2 100644 --- a/cosmic-app-list/i18n/uk/cosmic_app_list.ftl +++ b/cosmic-app-list/i18n/uk/cosmic_app_list.ftl @@ -3,6 +3,6 @@ pin = Закріпити quit = Вийти quit-all = Закрити всі new-window = Нове вікно -run = Виконати +run = Запустити run-on = Запустити на { $gpu } run-on-default = (Основна) diff --git a/cosmic-app-list/src/app.rs b/cosmic-app-list/src/app.rs index 55d815b8..932ffd6c 100755 --- a/cosmic-app-list/src/app.rs +++ b/cosmic-app-list/src/app.rs @@ -2,8 +2,7 @@ // SPDX-License-Identifier: GPL-3.0-only use crate::{ - fl, icon_catalog, - launcher_edit::{self, LauncherEditRequest}, + fl, wayland_subscription::{ OutputUpdate, ToplevelRequest, ToplevelUpdate, WaylandImage, WaylandRequest, WaylandUpdate, wayland_subscription, @@ -44,11 +43,11 @@ use cosmic::{ surface, theme::{self, Button, Container}, widget::{ - DndDestination, Image, button, container, divider, dnd_source, grid, + DndDestination, Image, button, container, divider, dnd_source, icon::{self, from_name}, image::Handle, rectangle_tracker::{RectangleTracker, RectangleUpdate, rectangle_tracker_subscription}, - scrollable, svg, text, text_input, + svg, text, }, }; use cosmic::{ @@ -65,8 +64,6 @@ use tokio::time::sleep; use url::Url; static MIME_TYPE: &str = "text/uri-list"; -const MAX_VISIBLE_ICON_CHOICES: usize = 120; -const ICON_CATALOG_COLUMNS: usize = 6; pub fn run() -> cosmic::iced::Result { cosmic::applet::run::(()) @@ -190,11 +187,6 @@ impl DockItem { dot_border_radius: [f32; 4], window_id: window::Id, filter: Option<&dyn Fn(&ToplevelInfo) -> bool>, - // Yoda: multiplier on the computed icon size (1.0 = default, - // >1.0 = magnified e.g. on hover for the macOS Tahoe effect). - // Applied to the icon's rendered width/height only — indicator - // dot and surrounding layout stay at base size. - icon_scale: f32, ) -> Element<'_, Message> { let Self { toplevels, @@ -213,35 +205,17 @@ impl DockItem { }; let toplevel_count = filtered_toplevels.len(); - // Cairo-like : pastille plus petite + atténuée quand toutes les fenêtres - // de cette app sont minimisées. - let all_minimized = toplevel_count > 0 - && filtered_toplevels - .iter() - .all(|(info, _)| info.state.contains(&State::Minimized)); - let app_icon = AppletIconData::new(applet); - // Yoda: scaled icon size for hover magnification. Clamped so - // tiny floats don't round to 0 and huge ones stay within u16. - let scaled_icon_size = ((f32::from(app_icon.icon_size) * icon_scale).round() as i32) - .clamp(1, u16::MAX as i32) as u16; let cosmic_icon = cosmic::widget::icon( fde::IconSource::from_unknown(desktop_info.icon().unwrap_or_default()).as_cosmic_icon(), ) // sets the preferred icon size variant .size(128) - .width(scaled_icon_size.into()) - .height(scaled_icon_size.into()); + .width(app_icon.icon_size.into()) + .height(app_icon.icon_size.into()); let indicator = { - // Padding réduit quand minimisée → pastille plus petite. - let effective_radius = if all_minimized { - (app_icon.dot_radius * 0.55).max(1.0) - } else { - app_icon.dot_radius - }; - let container = if toplevel_count <= 1 { vertical_space().height(Length::Fixed(0.0)) } else { @@ -255,34 +229,22 @@ impl DockItem { } } .apply(container) - .padding(effective_radius); + .padding(app_icon.dot_radius); if toplevel_count == 0 { container } else { - container.class(theme::Container::custom(move |theme| { - let cosmic = theme.cosmic(); - let accent: iced::Color = cosmic.accent_color().into(); - let on_bg: iced::Color = cosmic.on_bg_color().into(); - // Teinte neutre atténuée quand toutes les fenêtres sont minimisées. - let muted = iced::Color { a: 0.45, ..on_bg }; - - let fill = if all_minimized { - muted - } else if is_focused { - accent + container.class(theme::Container::custom(move |theme| container::Style { + background: if is_focused { + Some(Background::Color(theme.cosmic().accent_color().into())) } else { - on_bg - }; - - container::Style { - background: Some(Background::Color(fill)), - border: Border { - radius: dot_border_radius.into(), - ..Default::default() - }, + Some(Background::Color(theme.cosmic().on_bg_color().into())) + }, + border: Border { + radius: dot_border_radius.into(), ..Default::default() - } + }, + ..Default::default() })) } }; @@ -390,35 +352,10 @@ pub struct Popup { popup_type: PopupType, } -#[derive(Debug, Clone)] -struct LauncherEditState { - original_app_id: String, - source_path: PathBuf, - original_name: String, - original_exec: String, - name: String, - exec: String, - icon: String, - terminal: bool, - saving: bool, - error: Option, - icon_catalog: IconCatalogState, -} - -#[derive(Debug, Clone)] -struct IconCatalogState { - theme: String, - query: String, - entries: Vec, - loading: bool, - truncated: bool, -} - #[derive(Clone, Default)] struct CosmicAppList { core: cosmic::app::Core, popup: Option, - launcher_edit: Option, subscription_ctr: u32, item_ctr: u32, desktop_entries: Vec, @@ -437,21 +374,6 @@ struct CosmicAppList { output_list: FxHashMap, locales: Vec, hovered_toplevel: Option, - /// Yoda: which dock icon the pointer is currently over (for hover - /// magnification). None = no dock icon hovered. - hovered_dock_item: Option, - /// Yoda: animated "virtual cursor" center used by the fisheye - /// formula — lerps toward the real hovered icon's center on each - /// AnimTick, so the bell curve slides smoothly from one icon to the - /// next instead of snapping. - anim_hover_center: Option<(f32, f32)>, - /// Yoda: fade-in/out intensity of the magnification effect - /// (0.0 = icons flat, 1.0 = full fisheye). Targets 1.0 while the - /// pointer is over any dock icon, 0.0 otherwise. Lerped on AnimTick. - anim_hover_intensity: f32, - /// Yoda: timestamp of the last AnimTick, for dt-based exponential - /// smoothing. `None` on first tick. - anim_last_tick: Option, overflow_favorites_popup: Option, overflow_active_popup: Option, } @@ -460,33 +382,13 @@ struct CosmicAppList { pub enum PopupType { RightClickMenu, ToplevelList, - LauncherEditor, } #[derive(Debug, Clone)] -#[allow(dead_code)] enum Message { Wayland(WaylandUpdate), PinApp(u32), UnpinApp(u32), - EditLauncher(u32), - LauncherNameChanged(String), - LauncherExecChanged(String), - LauncherIconChanged(String), - LauncherIconSearchChanged(String), - LauncherIconSelected(String), - ReloadLauncherIconCatalog, - LauncherIconCatalogLoaded(icon_catalog::IconCatalog), - SaveLauncherEdit, - CancelLauncherEdit, - LauncherEditSaved(Result), - /// Yoda: pointer entered (Some) or left (None) a dock icon — drives - /// the macOS Tahoe-style hover magnification effect. - DockItemHover(Option), - /// Yoda: ticked at ~60fps by the animation subscription. Advances - /// anim_hover_center + anim_hover_intensity toward their targets so - /// the fisheye effect transitions smoothly instead of snapping. - AnimTick(std::time::Instant), Popup(u32, window::Id), Pressed(window::Id), ToplevelListPopup(u32, window::Id), @@ -724,143 +626,6 @@ pub fn menu_control_padding() -> Padding { [spacing.space_xxs, spacing.space_s].into() } -fn launcher_icon_editor(edit: &LauncherEditState) -> Element<'_, Message> { - let spacing = theme::spacing(); - let selected_icon = edit.icon.trim(); - let query = edit.icon_catalog.query.trim().to_ascii_lowercase(); - let mut total_matches = 0usize; - let mut visible_count = 0usize; - let mut icon_grid = grid() - .width(Length::Fill) - .column_spacing(spacing.space_xxs) - .row_spacing(spacing.space_xxs); - - for entry in edit.icon_catalog.entries.iter().filter(|entry| { - query.is_empty() || entry.name.to_ascii_lowercase().contains(query.as_str()) - }) { - total_matches += 1; - if visible_count >= MAX_VISIBLE_ICON_CHOICES { - continue; - } - - if visible_count > 0 && visible_count % ICON_CATALOG_COLUMNS == 0 { - icon_grid = icon_grid.insert_row(); - } - - let selected = selected_icon == entry.name; - let icon_preview = cosmic::widget::icon( - fde::IconSource::from_unknown(entry.name.as_str()).as_cosmic_icon(), - ) - .size(32) - .width(Length::Fixed(32.0)) - .height(Length::Fixed(32.0)); - - let label = text::caption(entry.name.as_str()) - .ellipsize(Ellipsize::End(EllipsizeHeightLimit::Lines(1))) - .width(Length::Fill) - .center(); - - let tile = column![icon_preview, label] - .align_x(Alignment::Center) - .spacing(4) - .width(Length::Fixed(70.0)); - - let tile_button = button::custom(tile) - .class(if selected { - Button::Suggested - } else { - Button::Image - }) - .selected(selected) - .on_press(Message::LauncherIconSelected(entry.name.clone())) - .padding(6) - .width(Length::Fixed(74.0)) - .height(Length::Fixed(76.0)); - - icon_grid = icon_grid.push(tile_button); - visible_count += 1; - } - - let current_icon = - cosmic::widget::icon(fde::IconSource::from_unknown(edit.icon.as_str()).as_cosmic_icon()) - .size(32) - .width(Length::Fixed(36.0)) - .height(Length::Fixed(36.0)); - - let icon_value = row![ - current_icon, - text_input("", edit.icon.as_str()) - .label(fl!("launcher-icon")) - .on_input(Message::LauncherIconChanged) - .on_submit(|_| Message::SaveLauncherEdit) - .width(Length::Fill) - .size(14), - button::icon(from_name("view-refresh-symbolic")) - .on_press(Message::ReloadLauncherIconCatalog) - .padding(spacing.space_xxs), - ] - .spacing(spacing.space_xs) - .align_y(Alignment::Center); - - let visible_total = if edit.icon_catalog.truncated { - format!( - "{}/{}+ {}", - visible_count, - total_matches, - fl!("launcher-icons") - ) - } else { - format!( - "{}/{} {}", - visible_count, - total_matches, - fl!("launcher-icons") - ) - }; - - let catalog_header = row![ - text::caption(format!( - "{}: {}", - fl!("launcher-icon-theme"), - edit.icon_catalog.theme - )), - horizontal_space(), - text::caption(visible_total), - ] - .align_y(Alignment::Center); - - let catalog_body: Element<_> = if edit.icon_catalog.loading { - container(text::body(fl!("launcher-icon-catalog-loading"))) - .center(Length::Fill) - .height(Length::Fixed(220.0)) - .into() - } else if total_matches == 0 { - container(text::body(fl!("launcher-icon-catalog-empty"))) - .center(Length::Fill) - .height(Length::Fixed(220.0)) - .into() - } else { - scrollable(icon_grid) - .height(Length::Fixed(240.0)) - .width(Length::Fill) - .into() - }; - - column![ - icon_value, - catalog_header, - text_input("", edit.icon_catalog.query.as_str()) - .label(fl!("launcher-icon-search")) - .on_input(Message::LauncherIconSearchChanged) - .width(Length::Fill) - .size(14), - catalog_body, - ] - .spacing(spacing.space_s) - .width(Length::Fill) - .into() -} - fn find_desktop_entries<'a>( desktop_entries: &'a [fde::DesktopEntry], app_ids: &'a [String], @@ -882,79 +647,6 @@ impl CosmicAppList { .collect::>(); } - /// Yoda: macOS-Tahoe fisheye-style magnification. Returns the - /// per-icon size multiplier based on the distance (in pixels) from - /// the currently hovered icon's center to this icon's center. - /// - /// Uses a gaussian bell curve so the hovered icon peaks at - /// 1.0 + PEAK, immediate neighbors still bulge noticeably, and icons - /// further away relax back to 1.0× — that's the smooth neighbour - /// deformation people associate with the macOS Dock. - /// - /// Falls back to binary 1.3×/1.0× when the rectangle tracker hasn't - /// populated yet (first render, or just after layout changes). - /// - /// Uses the animated hover center (anim_hover_center) and intensity - /// (anim_hover_intensity) so inter-icon transitions slide smoothly - /// and the whole effect fades in/out at the dock's edges. - fn icon_scale_for(&self, id: &DockItemId) -> f32 { - const PEAK: f32 = 0.35; - // sigma expressed in multiples of the hovered icon's size — - // 1.4 means the ±1 neighbors sit ~0.7σ away and still bulge - // visibly, while ±3+ has collapsed to ~1.0× (fisheye footprint - // close to 5 icons wide, Tahoe-ish). - const SIGMA_FACTOR: f32 = 1.4; - - // No intensity at all → skip the rest. - if self.anim_hover_intensity < 0.001 { - return 1.0; - } - - // Prefer the animated center (smooth); fall back to the real - // hovered icon's center (first render, before tick fires). - let hover_center = self.anim_hover_center.or_else(|| { - let hovered_id = self.hovered_dock_item.as_ref()?; - let r = self.rectangles.get(hovered_id)?; - Some((r.x + r.width / 2.0, r.y + r.height / 2.0)) - }); - let Some(hover_center) = hover_center else { - // No coords yet — visibly peak on the exact hovered id so - // the very first frame still responds. - return if self.hovered_dock_item.as_ref() == Some(id) { - 1.0 + PEAK * self.anim_hover_intensity - } else { - 1.0 - }; - }; - - let this_rect = match self.rectangles.get(id) { - Some(r) => r, - None => return 1.0, - }; - - let is_horizontal = matches!( - self.core.applet.anchor, - PanelAnchor::Top | PanelAnchor::Bottom - ); - let this_center = if is_horizontal { - this_rect.x + this_rect.width / 2.0 - } else { - this_rect.y + this_rect.height / 2.0 - }; - let hover_axis = if is_horizontal { hover_center.0 } else { hover_center.1 }; - let distance = (this_center - hover_axis).abs(); - let icon_extent = if is_horizontal { - this_rect.width - } else { - this_rect.height - } - .max(1.0); - let sigma = icon_extent * SIGMA_FACTOR; - // exp(-t²) bell curve, t = distance / sigma - let t = distance / sigma; - 1.0 + PEAK * self.anim_hover_intensity * (-t * t).exp() - } - fn is_on_current_monitor_and_workspace(&self, toplevel_info: &ToplevelInfo) -> bool { use cosmic_app_list_config::ToplevelFilter; @@ -999,45 +691,10 @@ impl CosmicAppList { .collect(); } - fn sync_pinned_list_from_config(&mut self) { - for item in self.pinned_list.drain(..) { - if !item.toplevels.is_empty() { - self.active_list.push(item); - } - } - - self.pinned_list = find_desktop_entries(&self.desktop_entries, &self.config.favorites) - .zip(&self.config.favorites) - .map(|(de, original_id)| { - if let Some(p) = self - .active_list - .iter() - .position(|dock_item| dock_item.desktop_info.id() == de.id()) - { - let mut d = self.active_list.remove(p); - d.desktop_info = de.clone(); - d.original_app_id.clone_from(original_id); - d - } else { - self.item_ctr += 1; - DockItem { - id: self.item_ctr, - toplevels: Vec::new(), - desktop_info: de.clone(), - original_app_id: original_id.clone(), - } - } - }) - .collect(); - } - /// Close any open popups. fn close_popups(&mut self) -> Task> { let mut commands = Vec::new(); if let Some(popup) = self.popup.take() { - if popup.popup_type == PopupType::LauncherEditor { - self.launcher_edit = None; - } commands.push(destroy_popup(popup.id)); } if let Some(popup) = self.overflow_active_popup.take() { @@ -1411,198 +1068,6 @@ impl cosmic::Application for CosmicAppList { return destroy_popup(popup_id); } } - Message::EditLauncher(id) => { - let Some(dock_item) = self.pinned_list.iter().find(|t| t.id == id).cloned() else { - return Task::none(); - }; - let Some(exec) = dock_item.desktop_info.exec() else { - return Task::none(); - }; - let Some(existing_popup) = self.popup.as_mut() else { - return Task::none(); - }; - - let original_name = dock_item - .desktop_info - .desktop_entry("Name") - .map(ToString::to_string) - .or_else(|| { - dock_item - .desktop_info - .name(&self.locales) - .map(Cow::into_owned) - }) - .unwrap_or_else(|| dock_item.original_app_id.clone()); - let original_exec = exec.to_string(); - let original_icon = dock_item - .desktop_info - .icon() - .unwrap_or_default() - .to_string(); - let icon_theme = cosmic::icon_theme::default(); - - self.launcher_edit = Some(LauncherEditState { - original_app_id: dock_item.original_app_id.clone(), - source_path: dock_item.desktop_info.path.clone(), - original_name: original_name.clone(), - original_exec: original_exec.clone(), - name: original_name, - exec: original_exec, - icon: original_icon.clone(), - terminal: dock_item.desktop_info.terminal(), - saving: false, - error: None, - icon_catalog: IconCatalogState { - theme: icon_theme.clone(), - query: String::new(), - entries: Vec::new(), - loading: true, - truncated: false, - }, - }); - - existing_popup.dock_item = dock_item; - existing_popup.popup_type = PopupType::LauncherEditor; - - return Task::perform( - icon_catalog::load_icon_catalog(icon_theme, original_icon), - |catalog| cosmic::Action::App(Message::LauncherIconCatalogLoaded(catalog)), - ); - } - Message::LauncherNameChanged(name) => { - if let Some(edit) = self.launcher_edit.as_mut() - && !edit.saving - { - edit.name = name; - edit.error = None; - } - } - Message::LauncherExecChanged(exec) => { - if let Some(edit) = self.launcher_edit.as_mut() - && !edit.saving - { - edit.exec = exec; - edit.error = None; - } - } - Message::LauncherIconChanged(icon) => { - if let Some(edit) = self.launcher_edit.as_mut() - && !edit.saving - { - edit.icon = icon; - edit.error = None; - } - } - Message::LauncherIconSearchChanged(query) => { - if let Some(edit) = self.launcher_edit.as_mut() { - edit.icon_catalog.query = query; - } - } - Message::LauncherIconSelected(icon) => { - if let Some(edit) = self.launcher_edit.as_mut() - && !edit.saving - { - edit.icon = icon; - edit.error = None; - } - } - Message::ReloadLauncherIconCatalog => { - if let Some(edit) = self.launcher_edit.as_mut() { - edit.icon_catalog.theme = cosmic::icon_theme::default(); - edit.icon_catalog.entries.clear(); - edit.icon_catalog.loading = true; - edit.icon_catalog.truncated = false; - - return Task::perform( - icon_catalog::load_icon_catalog( - edit.icon_catalog.theme.clone(), - edit.icon.clone(), - ), - |catalog| cosmic::Action::App(Message::LauncherIconCatalogLoaded(catalog)), - ); - } - } - Message::LauncherIconCatalogLoaded(catalog) => { - if let Some(edit) = self.launcher_edit.as_mut() - && edit.icon_catalog.theme == catalog.theme - { - edit.icon_catalog.entries = catalog.entries; - edit.icon_catalog.loading = false; - edit.icon_catalog.truncated = catalog.truncated; - } - } - Message::SaveLauncherEdit => { - let Some(edit) = self.launcher_edit.as_mut() else { - return Task::none(); - }; - if edit.saving { - return Task::none(); - } - if let Err(error) = - launcher_edit::validate_launcher_fields(&edit.name, &edit.exec, &edit.icon) - { - edit.error = Some(error); - return Task::none(); - } - - let request = LauncherEditRequest { - current_app_id: edit.original_app_id.clone(), - source_path: edit.source_path.clone(), - name: edit.name.clone(), - exec: edit.exec.clone(), - icon: edit.icon.clone(), - terminal: edit.terminal, - replace_localized_name: edit.name.trim() != edit.original_name.trim(), - disable_dbus_activation: edit.exec.trim() != edit.original_exec.trim(), - }; - - edit.saving = true; - edit.error = None; - - return Task::perform(launcher_edit::save_launcher_edit(request), |result| { - cosmic::Action::App(Message::LauncherEditSaved(result)) - }); - } - Message::CancelLauncherEdit => { - return self.close_popups(); - } - Message::LauncherEditSaved(result) => match result { - Ok(result) => { - tracing::info!( - app_id = result.new_app_id, - path = ?result.path, - "saved editable launcher" - ); - - let mut favorites = self.config.favorites.clone(); - let mut favorites_changed = false; - for favorite in &mut favorites { - if *favorite == result.old_app_id && *favorite != result.new_app_id { - *favorite = result.new_app_id.clone(); - favorites_changed = true; - } - } - - if favorites_changed { - self.config.update_pinned( - favorites.clone(), - &Config::new(APP_ID, AppListConfig::VERSION).unwrap(), - ); - self.config.favorites = favorites; - } - - self.update_desktop_entries(); - self.sync_pinned_list_from_config(); - self.launcher_edit = None; - return self.close_popups(); - } - Err(error) => { - if let Some(edit) = self.launcher_edit.as_mut() { - edit.saving = false; - edit.error = Some(error); - } - } - }, Message::Activate(handle) => { if let Some(tx) = self.wayland_sender.as_ref() { let _ = tx.send(WaylandRequest::Toplevel(ToplevelRequest::Activate(handle))); @@ -2055,9 +1520,6 @@ impl cosmic::Application for CosmicAppList { }, Message::ClosePopup => { if let Some(p) = self.popup.take() { - if p.popup_type == PopupType::LauncherEditor { - self.launcher_edit = None; - } return destroy_popup(p.id); } } @@ -2072,16 +1534,42 @@ impl cosmic::Application for CosmicAppList { } Message::ConfigUpdated(config) => { self.config = config; - self.update_desktop_entries(); - self.sync_pinned_list_from_config(); + // drain to active list + for item in self.pinned_list.drain(..) { + if !item.toplevels.is_empty() { + self.active_list.push(item); + } + } + + // pull back configured items into the favorites list + self.pinned_list = + find_desktop_entries(&self.desktop_entries, &self.config.favorites) + .zip(&self.config.favorites) + .map(|(de, original_id)| { + if let Some(p) = self + .active_list + .iter() + // match using heuristic id + .position(|dock_item| dock_item.desktop_info.id() == de.id()) + { + let mut d = self.active_list.remove(p); + // but use the id from the config + d.original_app_id.clone_from(original_id); + d + } else { + self.item_ctr += 1; + DockItem { + id: self.item_ctr, + toplevels: Vec::new(), + desktop_info: de.clone(), + original_app_id: original_id.clone(), + } + } + }) + .collect(); } Message::CloseRequested(id) => { - if let Some(popup) = &self.popup - && popup.id == id - { - if popup.popup_type == PopupType::LauncherEditor { - self.launcher_edit = None; - } + if Some(id) == self.popup.as_ref().map(|p| p.id) { self.popup = None; } if self.overflow_active_popup.is_some_and(|p| p == id) { @@ -2094,52 +1582,6 @@ impl cosmic::Application for CosmicAppList { Message::GpuRequest(gpus) => { self.gpus = gpus; } - Message::DockItemHover(id) => { - self.hovered_dock_item = id; - // Seed the animated center on the very first hover so - // the bell doesn't "fly in" from (0,0). - if self.anim_hover_center.is_none() - && let Some(hovered_id) = self.hovered_dock_item.as_ref() - && let Some(r) = self.rectangles.get(hovered_id) - { - self.anim_hover_center = Some(( - r.x + r.width / 2.0, - r.y + r.height / 2.0, - )); - } - } - Message::AnimTick(now) => { - // dt-based exponential smoothing: reach ~99% of the - // target in ~120ms at 60fps (about 7 ticks). - let dt = self - .anim_last_tick - .map(|prev| now.saturating_duration_since(prev).as_secs_f32()) - .unwrap_or(0.016) - .min(0.1); // clamp so a long pause doesn't snap - self.anim_last_tick = Some(now); - - // Intensity: target 1.0 when any icon is hovered, 0.0 else. - let intensity_target = if self.hovered_dock_item.is_some() { 1.0 } else { 0.0 }; - let tau = 0.060_f32; // time-constant (s); smaller = snappier - let alpha = 1.0 - (-dt / tau).exp(); - self.anim_hover_intensity += (intensity_target - self.anim_hover_intensity) * alpha; - - // Hovered-center smoothing: chase the real rect's center. - if let Some(hovered_id) = self.hovered_dock_item.as_ref() - && let Some(r) = self.rectangles.get(hovered_id) - { - let target = (r.x + r.width / 2.0, r.y + r.height / 2.0); - let current = self.anim_hover_center.unwrap_or(target); - self.anim_hover_center = Some(( - current.0 + (target.0 - current.0) * alpha, - current.1 + (target.1 - current.1) * alpha, - )); - } else if self.anim_hover_intensity < 0.01 { - // Nothing hovered + intensity faded out → forget the - // animated center so the next hover seeds fresh. - self.anim_hover_center = None; - } - } Message::OpenActive => { let create_new = self.overflow_active_popup.is_none(); let mut cmds = vec![self.close_popups()]; @@ -2321,9 +1763,7 @@ impl cosmic::Application for CosmicAppList { .filter(|(info, _)| self.is_on_current_monitor_and_workspace(info)) .any(|y| focused_item.contains(&y.0.foreign_toplevel)); - let dock_id = dock_item.id; - let icon_scale = self.icon_scale_for(&DockItemId::from(dock_id)); - let tooltip = self.core + self.core .applet .applet_tooltip::( dock_item.as_icon( @@ -2336,7 +1776,6 @@ impl cosmic::Application for CosmicAppList { dot_radius, self.core.main_window_id().unwrap(), Some(&|info| self.is_on_current_monitor_and_workspace(info)), - icon_scale, ), dock_item .desktop_info @@ -2346,10 +1785,7 @@ impl cosmic::Application for CosmicAppList { self.popup.is_some(), Message::Surface, None, - ); - cosmic::widget::mouse_area(tooltip) - .on_enter(Message::DockItemHover(Some(DockItemId::from(dock_id)))) - .on_exit(Message::DockItemHover(None)) + ) .into() }) .collect(); @@ -2400,10 +1836,6 @@ impl cosmic::Application for CosmicAppList { dot_radius, self.core.main_window_id().unwrap(), Some(&|info| self.is_on_current_monitor_and_workspace(info)), - // Yoda: no magnification on DnD-preview icons — these - // float around as the user drags, so static 1.0 is - // less visually confusing. - 1.0, ), ); } else if self.is_listening_for_dnd && self.pinned_list.is_empty() { @@ -2443,10 +1875,8 @@ impl cosmic::Application for CosmicAppList { .iter() .filter(|(info, _)| self.is_on_current_monitor_and_workspace(info)) .any(|y| focused_item.contains(&y.0.foreign_toplevel)); - let dock_id = dock_item.id; - let icon_scale = self.icon_scale_for(&DockItemId::from(dock_id)); - let tooltip = self.core + self.core .applet .applet_tooltip( dock_item.as_icon( @@ -2459,7 +1889,6 @@ impl cosmic::Application for CosmicAppList { dot_radius, self.core.main_window_id().unwrap(), Some(&|info| self.is_on_current_monitor_and_workspace(info)), - icon_scale, ), dock_item .desktop_info @@ -2469,10 +1898,7 @@ impl cosmic::Application for CosmicAppList { self.popup.is_some(), Message::Surface, None, - ); - cosmic::widget::mouse_area(tooltip) - .on_enter(Message::DockItemHover(Some(DockItemId::from(dock_id)))) - .on_exit(Message::DockItemHover(None)) + ) .into() }) .collect(); @@ -2753,19 +2179,6 @@ impl cosmic::Application for CosmicAppList { }), ); - if is_pinned && desktop_info.exec().is_some() { - content = content.push( - menu_button( - row![ - icon::icon(from_name("edit-symbolic").into()).size(16), - text::body(fl!("edit-launcher")) - ] - .spacing(8), - ) - .on_press(Message::EditLauncher(*id)), - ); - } - if !toplevels.is_empty() { content = content.push(divider::horizontal::light()); content = match toplevels.len() { @@ -2787,7 +2200,7 @@ impl cosmic::Application for CosmicAppList { //TODO: move style to libcosmic .class(theme::Container::custom(|theme| { let cosmic = theme.cosmic(); - let component = &cosmic.background(false).component; + let component = &cosmic.background.component; container::Style { icon_color: Some(component.on.into()), text_color: Some(component.on.into()), @@ -2812,84 +2225,6 @@ impl cosmic::Application for CosmicAppList { ) .into() } - PopupType::LauncherEditor => { - let Some(edit) = self.launcher_edit.as_ref() else { - return text::body("").into(); - }; - - let spacing = theme::spacing(); - let can_save = !edit.saving - && launcher_edit::validate_launcher_fields( - &edit.name, &edit.exec, &edit.icon, - ) - .is_ok(); - - let mut form = column![ - text::title4(fl!("edit-launcher")), - text_input("", edit.name.as_str()) - .label(fl!("launcher-name")) - .on_input(Message::LauncherNameChanged) - .on_submit(|_| Message::SaveLauncherEdit) - .width(Length::Fill) - .size(14), - text_input("", edit.exec.as_str()) - .label(fl!("launcher-command")) - .on_input(Message::LauncherExecChanged) - .on_submit(|_| Message::SaveLauncherEdit) - .width(Length::Fill) - .size(14), - launcher_icon_editor(edit), - ] - .spacing(spacing.space_s) - .width(Length::Fill); - - if let Some(error) = edit.error.as_ref() { - form = form.push(text::caption(error.as_str()).class( - cosmic::theme::Text::Color(theme.cosmic().destructive_color().into()), - )); - } - - let cancel = - button::custom(text::body(fl!("cancel")).center().width(Length::Fill)) - .on_press_maybe(if edit.saving { - None - } else { - Some(Message::CancelLauncherEdit) - }) - .padding([spacing.space_xxs, spacing.space_s]) - .width(142); - - let save = button::custom(text::body(fl!("save")).center().width(Length::Fill)) - .class(Button::Suggested) - .on_press_maybe(if can_save { - Some(Message::SaveLauncherEdit) - } else { - None - }) - .padding([spacing.space_xxs, spacing.space_s]) - .width(142); - - let actions = row![horizontal_space(), cancel, save] - .spacing(spacing.space_xxs) - .align_y(Alignment::Center); - - let content = column![form, actions] - .spacing(spacing.space_m) - .padding(spacing.space_m) - .width(Length::Fill); - - self.core - .applet - .popup_container(container(content).width(Length::Fill)) - .limits( - Limits::NONE - .min_width(480.) - .min_height(1.) - .max_width(520.) - .max_height(1000.), - ) - .into() - } PopupType::ToplevelList => match self.core.applet.anchor { PanelAnchor::Left | PanelAnchor::Right => { let mut content = @@ -2978,10 +2313,6 @@ impl cosmic::Application for CosmicAppList { dot_radius, id, Some(&|info| self.is_on_current_monitor_and_workspace(info)), - // Yoda: icons in the overflow popup are - // already smaller-grid — keep them at 1.0 - // so the popup doesn't reshuffle on hover. - 1.0, ), dock_item .desktop_info @@ -3090,10 +2421,6 @@ impl cosmic::Application for CosmicAppList { dot_radius, id, Some(&|info| self.is_on_current_monitor_and_workspace(info)), - // Yoda: popup icons stay at 1.0 (hover - // magnification is applied to the main - // dock row only). - 1.0, ), dock_item .desktop_info @@ -3152,21 +2479,7 @@ impl cosmic::Application for CosmicAppList { } fn subscription(&self) -> Subscription { - // Yoda: ~60fps animation ticks for the fisheye magnification. - // Only emitted when an animation is actually in progress (hover - // intensity >0 OR still fading out) — keeps the panel idle when - // the pointer is nowhere near the dock. - let anim_active = self.hovered_dock_item.is_some() - || self.anim_hover_intensity > 0.001; - let anim_subscription = if anim_active { - cosmic::iced::time::every(std::time::Duration::from_millis(16)) - .map(Message::AnimTick) - } else { - Subscription::none() - }; - Subscription::batch([ - anim_subscription, wayland_subscription().map(Message::Wayland), listen_with(|e, _, id| match e { cosmic::iced::core::Event::PlatformSpecific(event::PlatformSpecific::Wayland( diff --git a/cosmic-app-list/src/icon_catalog.rs b/cosmic-app-list/src/icon_catalog.rs deleted file mode 100644 index 2cb05e1a..00000000 --- a/cosmic-app-list/src/icon_catalog.rs +++ /dev/null @@ -1,440 +0,0 @@ -use std::{ - cmp::Ordering, - collections::{HashMap, HashSet, VecDeque}, - fs, - path::{Component, Path, PathBuf}, -}; - -const FALLBACK_THEMES: &[&str] = &["Cosmic", "hicolor", "gnome", "Yaru"]; -const MAX_THEME_CHAIN: usize = 24; -const MAX_SCAN_DEPTH: usize = 5; -const MAX_CATALOG_ENTRIES: usize = 2_500; - -#[derive(Debug, Clone)] -pub struct IconCatalog { - pub theme: String, - pub entries: Vec, - pub truncated: bool, -} - -#[derive(Debug, Clone)] -pub struct IconCatalogEntry { - pub name: String, -} - -#[derive(Debug, Clone, Eq, PartialEq)] -struct CandidateRank { - preferred: u8, - category: u8, - symbolic: u8, - theme_depth: usize, - extension: u8, -} - -impl Ord for CandidateRank { - fn cmp(&self, other: &Self) -> Ordering { - ( - self.preferred, - self.category, - self.symbolic, - self.theme_depth, - self.extension, - ) - .cmp(&( - other.preferred, - other.category, - other.symbolic, - other.theme_depth, - other.extension, - )) - } -} - -impl PartialOrd for CandidateRank { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -#[derive(Debug, Clone)] -struct Candidate { - name: String, - rank: CandidateRank, -} - -pub async fn load_icon_catalog(theme: String, preferred_icon: String) -> IconCatalog { - build_icon_catalog(theme, preferred_icon) -} - -fn build_icon_catalog(theme: String, preferred_icon: String) -> IconCatalog { - let theme = if theme.trim().is_empty() { - "Cosmic".to_string() - } else { - theme - }; - let preferred_name = icon_name_from_value(preferred_icon.trim()); - let theme_chain = theme_chain(&theme); - let mut candidates = HashMap::new(); - - for (theme_depth, theme_name) in theme_chain.iter().enumerate() { - for theme_dir in theme_dirs(theme_name) { - scan_icon_tree( - &theme_dir, - theme_depth, - preferred_name.as_deref(), - &mut candidates, - ); - } - } - - for pixmap_dir in pixmap_dirs() { - scan_icon_tree( - &pixmap_dir, - theme_chain.len() + 1, - preferred_name.as_deref(), - &mut candidates, - ); - } - - let mut entries = candidates.into_values().collect::>(); - entries.sort_by(|a, b| a.rank.cmp(&b.rank).then_with(|| a.name.cmp(&b.name))); - - let truncated = entries.len() > MAX_CATALOG_ENTRIES; - entries.truncate(MAX_CATALOG_ENTRIES); - - IconCatalog { - theme, - entries: entries - .into_iter() - .map(|candidate| IconCatalogEntry { - name: candidate.name, - }) - .collect(), - truncated, - } -} - -fn theme_chain(theme: &str) -> Vec { - let mut queue = VecDeque::from([theme.to_string()]); - let mut seen = HashSet::new(); - let mut chain = Vec::new(); - - while let Some(theme_name) = queue.pop_front() { - let key = theme_name.to_ascii_lowercase(); - if !seen.insert(key) { - continue; - } - - chain.push(theme_name.clone()); - if chain.len() >= MAX_THEME_CHAIN { - break; - } - - for parent in read_theme_inherits(&theme_name) { - queue.push_back(parent); - } - } - - for fallback in FALLBACK_THEMES { - if chain.len() >= MAX_THEME_CHAIN { - break; - } - let key = fallback.to_ascii_lowercase(); - if seen.insert(key) { - chain.push((*fallback).to_string()); - } - } - - chain -} - -fn read_theme_inherits(theme: &str) -> Vec { - theme_dirs(theme) - .into_iter() - .find_map(|dir| fs::read_to_string(dir.join("index.theme")).ok()) - .map(|contents| parse_inherits(&contents)) - .unwrap_or_default() -} - -fn parse_inherits(contents: &str) -> Vec { - let mut in_icon_theme = false; - - for raw_line in contents.lines() { - let line = raw_line.trim(); - if line.is_empty() || line.starts_with('#') { - continue; - } - - if line.starts_with('[') && line.ends_with(']') { - in_icon_theme = line == "[Icon Theme]"; - continue; - } - - if in_icon_theme && let Some(value) = line.strip_prefix("Inherits=") { - return value - .split(',') - .map(str::trim) - .filter(|value| !value.is_empty()) - .map(ToOwned::to_owned) - .collect(); - } - } - - Vec::new() -} - -fn scan_icon_tree( - root: &Path, - theme_depth: usize, - preferred_name: Option<&str>, - candidates: &mut HashMap, -) { - let mut stack = vec![(root.to_path_buf(), 0usize)]; - - while let Some((dir, depth)) = stack.pop() { - if depth > MAX_SCAN_DEPTH { - continue; - } - - let Ok(entries) = fs::read_dir(&dir) else { - continue; - }; - - for entry in entries.filter_map(Result::ok) { - let path = entry.path(); - let Ok(file_type) = entry.file_type() else { - continue; - }; - - if file_type.is_dir() { - stack.push((path, depth + 1)); - } else if (file_type.is_file() || file_type.is_symlink()) - && let Some(candidate) = candidate_from_path(&path, theme_depth, preferred_name) - { - insert_candidate(candidates, candidate); - } - } - } -} - -fn insert_candidate(candidates: &mut HashMap, candidate: Candidate) { - let key = candidate.name.to_ascii_lowercase(); - match candidates.get_mut(&key) { - Some(existing) if candidate.rank < existing.rank => { - *existing = candidate; - } - None => { - candidates.insert(key, candidate); - } - _ => {} - } -} - -fn candidate_from_path( - path: &Path, - theme_depth: usize, - preferred_name: Option<&str>, -) -> Option { - let extension = extension_rank(path)?; - let name = path.file_stem()?.to_str()?.trim(); - if name.is_empty() { - return None; - } - - let symbolic = u8::from(name.ends_with("-symbolic") || path_has_component(path, "symbolic")); - let preferred = u8::from(preferred_name != Some(name)); - - Some(Candidate { - name: name.to_string(), - rank: CandidateRank { - preferred, - category: category_rank(path), - symbolic, - theme_depth, - extension, - }, - }) -} - -fn extension_rank(path: &Path) -> Option { - match path.extension()?.to_str()?.to_ascii_lowercase().as_str() { - "svg" => Some(0), - "png" => Some(1), - "xpm" => Some(2), - _ => None, - } -} - -fn category_rank(path: &Path) -> u8 { - for component in path.components().filter_map(component_str) { - match component { - "apps" | "applications" => return 0, - "categories" => return 1, - "places" => return 2, - "devices" => return 3, - "mimetypes" => return 4, - "actions" => return 5, - "status" => return 6, - _ => {} - } - } - - 7 -} - -fn path_has_component(path: &Path, needle: &str) -> bool { - path.components() - .filter_map(component_str) - .any(|component| component == needle) -} - -fn component_str(component: Component<'_>) -> Option<&str> { - component.as_os_str().to_str() -} - -fn icon_name_from_value(value: &str) -> Option { - if value.is_empty() { - return None; - } - - let path = Path::new(value); - if value.contains('/') { - return path - .file_stem() - .and_then(|name| name.to_str()) - .map(ToOwned::to_owned); - } - - path.file_stem() - .and_then(|name| name.to_str()) - .map(ToOwned::to_owned) - .or_else(|| Some(value.to_string())) -} - -fn theme_dirs(theme: &str) -> Vec { - icon_base_dirs() - .into_iter() - .map(|base| base.join(theme)) - .filter(|path| path.is_dir()) - .collect() -} - -fn icon_base_dirs() -> Vec { - let mut dirs = Vec::new(); - - if let Some(home) = std::env::home_dir() { - push_existing_unique(&mut dirs, home.join(".icons")); - } - - if let Some(data_home) = xdg_data_home() { - push_existing_unique(&mut dirs, data_home.join("icons")); - } - - for data_dir in xdg_data_dirs() { - push_existing_unique(&mut dirs, data_dir.join("icons")); - } - - dirs -} - -fn pixmap_dirs() -> Vec { - let mut dirs = Vec::new(); - - if let Some(data_home) = xdg_data_home() { - push_existing_unique(&mut dirs, data_home.join("pixmaps")); - } - - for data_dir in xdg_data_dirs() { - push_existing_unique(&mut dirs, data_dir.join("pixmaps")); - } - - push_existing_unique(&mut dirs, PathBuf::from("/usr/share/pixmaps")); - dirs -} - -fn xdg_data_home() -> Option { - std::env::var_os("XDG_DATA_HOME") - .filter(|value| !value.is_empty()) - .map(PathBuf::from) - .or_else(|| std::env::home_dir().map(|home| home.join(".local/share"))) -} - -fn xdg_data_dirs() -> Vec { - std::env::var_os("XDG_DATA_DIRS") - .filter(|value| !value.is_empty()) - .map(|value| std::env::split_paths(&value).collect()) - .unwrap_or_else(|| { - vec![ - PathBuf::from("/usr/local/share"), - PathBuf::from("/usr/share"), - ] - }) -} - -fn push_existing_unique(dirs: &mut Vec, path: PathBuf) { - if path.exists() && !dirs.iter().any(|existing| existing == &path) { - dirs.push(path); - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn parses_inherits_only_from_icon_theme_section() { - let contents = r#" -[Other] -Inherits=Wrong - -[Icon Theme] -Name=Demo -Inherits=Cosmic, hicolor , Adwaita -"#; - - assert_eq!(parse_inherits(contents), ["Cosmic", "hicolor", "Adwaita"]); - } - - #[test] - fn normalizes_icon_names_from_plain_names_and_paths() { - assert_eq!(icon_name_from_value("firefox").as_deref(), Some("firefox")); - assert_eq!( - icon_name_from_value("/usr/share/icons/hicolor/scalable/apps/firefox.svg").as_deref(), - Some("firefox") - ); - } - - #[test] - fn keeps_the_best_duplicate_candidate() { - let mut candidates = HashMap::new(); - insert_candidate( - &mut candidates, - Candidate { - name: "demo".to_string(), - rank: CandidateRank { - preferred: 1, - category: 7, - symbolic: 1, - theme_depth: 4, - extension: 2, - }, - }, - ); - insert_candidate( - &mut candidates, - Candidate { - name: "demo".to_string(), - rank: CandidateRank { - preferred: 0, - category: 0, - symbolic: 0, - theme_depth: 0, - extension: 0, - }, - }, - ); - - assert_eq!(candidates["demo"].rank.preferred, 0); - assert_eq!(candidates["demo"].rank.category, 0); - } -} diff --git a/cosmic-app-list/src/launcher_edit.rs b/cosmic-app-list/src/launcher_edit.rs deleted file mode 100644 index c354e1e6..00000000 --- a/cosmic-app-list/src/launcher_edit.rs +++ /dev/null @@ -1,378 +0,0 @@ -// Copyright 2026 System76 -// SPDX-License-Identifier: GPL-3.0-only - -use std::{ - env, - ffi::OsString, - io::ErrorKind, - path::{Path, PathBuf}, -}; - -use tokio::fs; - -#[derive(Clone, Debug)] -pub struct LauncherEditRequest { - pub current_app_id: String, - pub source_path: PathBuf, - pub name: String, - pub exec: String, - pub icon: String, - pub terminal: bool, - pub replace_localized_name: bool, - pub disable_dbus_activation: bool, -} - -#[derive(Clone, Debug)] -pub struct LauncherEditResult { - pub old_app_id: String, - pub new_app_id: String, - pub path: PathBuf, -} - -#[derive(Clone, Debug)] -struct ValidLauncherEdit { - current_app_id: String, - source_path: PathBuf, - name: String, - exec: String, - icon: String, - terminal: bool, - replace_localized_name: bool, - disable_dbus_activation: bool, -} - -pub fn validate_launcher_fields(name: &str, exec: &str, icon: &str) -> Result<(), String> { - validate_required("Name", name)?; - validate_required("Command", exec)?; - validate_optional("Icon", icon)?; - Ok(()) -} - -pub async fn save_launcher_edit( - request: LauncherEditRequest, -) -> Result { - let request = validate_request(request)?; - let (new_app_id, target_path) = - target_launcher_path(&request.current_app_id, &request.source_path)?; - - let source = read_source_desktop_entry(&request).await?; - let rendered = render_editable_desktop_entry(&source, &request); - - let Some(parent) = target_path.parent() else { - return Err("Desktop file target has no parent directory".to_string()); - }; - - fs::create_dir_all(parent) - .await - .map_err(|err| format!("Could not create applications directory: {err}"))?; - - let tmp_path = temporary_path(&target_path)?; - fs::write(&tmp_path, rendered) - .await - .map_err(|err| format!("Could not write temporary desktop file: {err}"))?; - - if let Err(err) = fs::rename(&tmp_path, &target_path).await { - let _ = fs::remove_file(&tmp_path).await; - return Err(format!("Could not install desktop file: {err}")); - } - - Ok(LauncherEditResult { - old_app_id: request.current_app_id, - new_app_id, - path: target_path, - }) -} - -fn validate_request(request: LauncherEditRequest) -> Result { - let name = validate_required("Name", &request.name)?; - let exec = validate_required("Command", &request.exec)?; - let icon = validate_optional("Icon", &request.icon)?; - - Ok(ValidLauncherEdit { - current_app_id: request.current_app_id, - source_path: request.source_path, - name, - exec, - icon, - terminal: request.terminal, - replace_localized_name: request.replace_localized_name, - disable_dbus_activation: request.disable_dbus_activation, - }) -} - -fn validate_required(label: &str, value: &str) -> Result { - let value = validate_optional(label, value)?; - if value.is_empty() { - return Err(format!("{label} cannot be empty")); - } - Ok(value) -} - -fn validate_optional(label: &str, value: &str) -> Result { - if value.contains('\0') || value.contains('\n') || value.contains('\r') { - return Err(format!("{label} cannot contain line breaks")); - } - Ok(value.trim().to_string()) -} - -async fn read_source_desktop_entry(request: &ValidLauncherEdit) -> Result { - if request.source_path.as_os_str().is_empty() { - return Ok(minimal_desktop_entry()); - } - - match fs::read_to_string(&request.source_path).await { - Ok(source) => Ok(source), - Err(err) if err.kind() == ErrorKind::NotFound => Ok(minimal_desktop_entry()), - Err(err) => Err(format!("Could not read source desktop file: {err}")), - } -} - -fn minimal_desktop_entry() -> String { - "[Desktop Entry]\nType=Application\n".to_string() -} - -fn user_applications_dir() -> Result { - if let Some(xdg_data_home) = env::var_os("XDG_DATA_HOME").filter(|value| !value.is_empty()) { - return Ok(PathBuf::from(xdg_data_home).join("applications")); - } - - let Some(home) = env::var_os("HOME").filter(|value| !value.is_empty()) else { - return Err("Neither XDG_DATA_HOME nor HOME is set".to_string()); - }; - - Ok(PathBuf::from(home).join(".local/share/applications")) -} - -fn target_launcher_path(app_id: &str, source_path: &Path) -> Result<(String, PathBuf), String> { - let applications_dir = user_applications_dir()?; - if source_path.starts_with(&applications_dir) { - return Ok((app_id.to_string(), source_path.to_path_buf())); - } - - let desktop_id = normalize_desktop_id(app_id)?; - Ok(( - desktop_id.clone(), - applications_dir.join(format!("{desktop_id}.desktop")), - )) -} - -fn normalize_desktop_id(app_id: &str) -> Result { - if app_id.contains('\0') || app_id.contains('\n') || app_id.contains('\r') { - return Err("Desktop ID cannot contain line breaks".to_string()); - } - - let desktop_id = app_id - .chars() - .map(|c| if c == '/' { '-' } else { c }) - .collect::(); - - if desktop_id.trim().is_empty() { - return Err("Desktop ID cannot be empty".to_string()); - } - - Ok(desktop_id) -} - -fn temporary_path(target_path: &Path) -> Result { - let Some(file_name) = target_path.file_name() else { - return Err("Desktop file target has no filename".to_string()); - }; - - let mut tmp_file_name = OsString::from("."); - tmp_file_name.push(file_name); - tmp_file_name.push(format!(".tmp-{}", std::process::id())); - - Ok(target_path.with_file_name(tmp_file_name)) -} - -fn render_editable_desktop_entry(source: &str, request: &ValidLauncherEdit) -> String { - let mut updates = vec![ - ("Type", "Application".to_string()), - ("Name", request.name.clone()), - ("Exec", request.exec.clone()), - ("Icon", request.icon.clone()), - ( - "Terminal", - if request.terminal { "true" } else { "false" }.to_string(), - ), - ("X-COSMIC-UserEditable", "true".to_string()), - ("X-COSMIC-SourceDesktopId", request.current_app_id.clone()), - ]; - - if !request.source_path.as_os_str().is_empty() { - updates.push(( - "X-COSMIC-SourceDesktopPath", - request.source_path.to_string_lossy().to_string(), - )); - } - - if request.disable_dbus_activation { - updates.push(("DBusActivatable", "false".to_string())); - } - - set_desktop_entry_keys( - source, - &updates, - request.replace_localized_name, - request.disable_dbus_activation, - ) -} - -fn set_desktop_entry_keys( - source: &str, - updates: &[(&str, String)], - replace_localized_name: bool, - remove_try_exec: bool, -) -> String { - let mut out = Vec::new(); - let mut seen = vec![false; updates.len()]; - let mut in_desktop_entry = false; - let mut saw_desktop_entry = false; - - for line in source.lines() { - if let Some(section) = section_name(line) { - if in_desktop_entry { - insert_missing_keys(&mut out, updates, &seen); - } - - in_desktop_entry = section == "Desktop Entry"; - saw_desktop_entry |= in_desktop_entry; - if in_desktop_entry { - seen.fill(false); - } - - out.push(line.to_string()); - continue; - } - - if in_desktop_entry && let Some(key) = desktop_entry_key(line) { - if replace_localized_name && key.starts_with("Name[") { - continue; - } - - if remove_try_exec && key == "TryExec" { - continue; - } - - if let Some(index) = updates - .iter() - .position(|(update_key, _)| *update_key == key) - { - out.push(format!("{}={}", updates[index].0, updates[index].1)); - seen[index] = true; - continue; - } - } - - out.push(line.to_string()); - } - - if in_desktop_entry { - insert_missing_keys(&mut out, updates, &seen); - } - - if !saw_desktop_entry { - let mut with_desktop_entry = Vec::with_capacity(out.len() + updates.len() + 2); - with_desktop_entry.push("[Desktop Entry]".to_string()); - insert_missing_keys( - &mut with_desktop_entry, - updates, - &vec![false; updates.len()], - ); - if !out.is_empty() { - with_desktop_entry.push(String::new()); - with_desktop_entry.extend(out); - } - out = with_desktop_entry; - } - - let mut rendered = out.join("\n"); - rendered.push('\n'); - rendered -} - -fn insert_missing_keys(out: &mut Vec, updates: &[(&str, String)], seen: &[bool]) { - for (index, (key, value)) in updates.iter().enumerate() { - if !seen[index] { - out.push(format!("{key}={value}")); - } - } -} - -fn section_name(line: &str) -> Option<&str> { - let trimmed = line.trim(); - trimmed - .strip_prefix('[') - .and_then(|value| value.strip_suffix(']')) -} - -fn desktop_entry_key(line: &str) -> Option<&str> { - let line = line.trim_start(); - if line.starts_with('#') || line.starts_with(';') { - return None; - } - - line.split_once('=').map(|(key, _)| key.trim_end()) -} - -#[cfg(test)] -mod tests { - use super::*; - - fn request() -> ValidLauncherEdit { - ValidLauncherEdit { - current_app_id: "org.example.App".to_string(), - source_path: PathBuf::from("/usr/share/applications/org.example.App.desktop"), - name: "Example".to_string(), - exec: "example --new".to_string(), - icon: "example-custom".to_string(), - terminal: false, - replace_localized_name: true, - disable_dbus_activation: true, - } - } - - #[test] - fn updates_desktop_entry_without_dropping_action_groups() { - let source = "\ -[Desktop Entry] -Type=Application -Name=Old -Name[fr]=Ancien -Exec=old -TryExec=old -Icon=old -DBusActivatable=true -Actions=new-window; - -[Desktop Action new-window] -Name=New Window -Exec=old --new-window -"; - - let rendered = render_editable_desktop_entry(source, &request()); - - assert!(rendered.contains("Name=Example\n")); - assert!(!rendered.contains("Name[fr]=")); - assert!(rendered.contains("Exec=example --new\n")); - assert!(rendered.contains("Icon=example-custom\n")); - assert!(rendered.contains("DBusActivatable=false\n")); - assert!(!rendered.contains("TryExec=")); - assert!(rendered.contains("[Desktop Action new-window]\n")); - assert!(rendered.contains("Exec=old --new-window\n")); - } - - #[test] - fn preserves_localized_names_when_name_is_unchanged() { - let mut edit = request(); - edit.replace_localized_name = false; - - let rendered = render_editable_desktop_entry( - "[Desktop Entry]\nName=Example\nName[fr]=Exemple\nExec=old\n", - &edit, - ); - - assert!(rendered.contains("Name=Example\n")); - assert!(rendered.contains("Name[fr]=Exemple\n")); - } -} diff --git a/cosmic-app-list/src/lib.rs b/cosmic-app-list/src/lib.rs index f6893335..51079cc4 100644 --- a/cosmic-app-list/src/lib.rs +++ b/cosmic-app-list/src/lib.rs @@ -2,8 +2,6 @@ // SPDX-License-Identifier: GPL-3.0-only mod app; -mod icon_catalog; -mod launcher_edit; mod localize; mod wayland_handler; mod wayland_subscription; diff --git a/cosmic-app-list/src/wayland_handler.rs b/cosmic-app-list/src/wayland_handler.rs index d27409dc..03c16973 100644 --- a/cosmic-app-list/src/wayland_handler.rs +++ b/cosmic-app-list/src/wayland_handler.rs @@ -388,10 +388,7 @@ impl CaptureData { }, ) .unwrap(); - if let Err(err) = self.conn.flush() { - tracing::error!("Wayland flush failed during screencopy session create: {err}"); - return None; - } + self.conn.flush().unwrap(); let formats = session .wait_while(|data| data.formats.is_none()) @@ -440,10 +437,7 @@ impl CaptureData { session: capture_session.clone(), }, ); - if let Err(err) = self.conn.flush() { - tracing::error!("Wayland flush failed during screencopy capture: {err}"); - return None; - } + self.conn.flush().unwrap(); // TODO: wait for server to release buffer? let res = session @@ -715,10 +709,7 @@ pub(crate) fn wayland_handler( if app_data.exit { break; } - if let Err(err) = event_loop.dispatch(None, &mut app_data) { - tracing::error!("Wayland event loop terminated: {err}"); - break; - } + event_loop.dispatch(None, &mut app_data).unwrap(); } } diff --git a/cosmic-applet-a11y/Cargo.toml b/cosmic-applet-a11y/Cargo.toml index dd93f90e..a5bb12b4 100644 --- a/cosmic-applet-a11y/Cargo.toml +++ b/cosmic-applet-a11y/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cosmic-applet-a11y" -version = "1.3.0" +version = "1.0.2" edition = "2024" [dependencies] @@ -9,7 +9,7 @@ cctk.workspace = true cosmic-protocols.workspace = true i18n-embed-fl.workspace = true i18n-embed.workspace = true -cosmic.workspace = true +libcosmic.workspace = true rust-embed.workspace = true tokio.workspace = true tracing-log.workspace = true @@ -17,7 +17,9 @@ tracing-subscriber.workspace = true tracing.workspace = true [dependencies.cosmic-settings-a11y-manager-subscription] -path = "../../cosmic-settings/subscriptions/a11y-manager" +git = "https://github.com/pop-os/cosmic-settings" +# path = "../../cosmic-settings/subscriptions/a11y-manager" [dependencies.cosmic-settings-accessibility-subscription] -path = "../../cosmic-settings/subscriptions/accessibility" +git = "https://github.com/pop-os/cosmic-settings" +# path = "../../cosmic-settings/subscriptions/accessibility" diff --git a/cosmic-applet-a11y/i18n/el/cosmic_applet_a11y.ftl b/cosmic-applet-a11y/i18n/el/cosmic_applet_a11y.ftl index 609965b5..e69de29b 100644 --- a/cosmic-applet-a11y/i18n/el/cosmic_applet_a11y.ftl +++ b/cosmic-applet-a11y/i18n/el/cosmic_applet_a11y.ftl @@ -1,6 +0,0 @@ -screen-reader = Ανάγνωση οθόνης -invert-colors = Αναστροφή χρωμάτων -high-contrast = Υψηλή αντίθεση -settings = Ρυθμίσεις προσβασιμότητας... -magnifier = Μεγεθυντικός φακός -filter-colors = Φίλτρο χρωμάτων diff --git a/cosmic-applet-a11y/i18n/ja/cosmic_applet_a11y.ftl b/cosmic-applet-a11y/i18n/ja/cosmic_applet_a11y.ftl index 564e0d4f..75249579 100644 --- a/cosmic-applet-a11y/i18n/ja/cosmic_applet_a11y.ftl +++ b/cosmic-applet-a11y/i18n/ja/cosmic_applet_a11y.ftl @@ -2,5 +2,5 @@ screen-reader = スクリーンリーダー magnifier = 拡大鏡 settings = アクセシビリティ設定… invert-colors = 色を反転 -filter-colors = カラーフィルター +filter-colors = 色のフィルター high-contrast = ハイコントラスト diff --git a/cosmic-applet-a11y/i18n/kmr/cosmic_applet_a11y.ftl b/cosmic-applet-a11y/i18n/kmr/cosmic_applet_a11y.ftl index 4c772791..e69de29b 100644 --- a/cosmic-applet-a11y/i18n/kmr/cosmic_applet_a11y.ftl +++ b/cosmic-applet-a11y/i18n/kmr/cosmic_applet_a11y.ftl @@ -1,6 +0,0 @@ -screen-reader = Xwînerê dîmenderê -magnifier = Mezinker -invert-colors = Rengan berevajî bike -settings = Sazkariyên gihîştinê... -filter-colors = Rengan parzûn bike -high-contrast = Dijbiriya bilind diff --git a/cosmic-applet-a11y/i18n/lo/cosmic_applet_a11y.ftl b/cosmic-applet-a11y/i18n/lo/cosmic_applet_a11y.ftl deleted file mode 100644 index e69de29b..00000000 diff --git a/cosmic-applet-a11y/i18n/sr/cosmic_applet_a11y.ftl b/cosmic-applet-a11y/i18n/sr/cosmic_applet_a11y.ftl index 76d163eb..e69de29b 100644 --- a/cosmic-applet-a11y/i18n/sr/cosmic_applet_a11y.ftl +++ b/cosmic-applet-a11y/i18n/sr/cosmic_applet_a11y.ftl @@ -1,6 +0,0 @@ -screen-reader = Читач екрана -invert-colors = Обрни боје -high-contrast = Високи контраст -filter-colors = Филтрирај боје -settings = Подешавања приступачности... -magnifier = Увеличавач diff --git a/cosmic-applet-a11y/src/app.rs b/cosmic-applet-a11y/src/app.rs index 6a328339..ad5f2a1e 100644 --- a/cosmic-applet-a11y/src/app.rs +++ b/cosmic-applet-a11y/src/app.rs @@ -26,9 +26,10 @@ use cosmic::{ }; use cosmic_settings_a11y_manager_subscription::{ - AccessibilityEvent, AccessibilityRequest, ColorFilter, + self as cosmic_a11y_manager, AccessibilityEvent, AccessibilityRequest, ColorFilter, }; use cosmic_settings_accessibility_subscription::{self as accessibility}; +use std::sync::LazyLock; use tokio::sync::mpsc::UnboundedSender; pub fn run() -> cosmic::iced::Result { @@ -51,7 +52,6 @@ struct CosmicA11yApplet { } #[derive(Debug, Clone)] -#[allow(dead_code)] enum Message { TogglePopup, CloseRequested(window::Id), diff --git a/cosmic-applet-a11y/src/backend/wayland/mod.rs b/cosmic-applet-a11y/src/backend/wayland/mod.rs index 12340588..fa9796f0 100644 --- a/cosmic-applet-a11y/src/backend/wayland/mod.rs +++ b/cosmic-applet-a11y/src/backend/wayland/mod.rs @@ -2,12 +2,13 @@ // SPDX-License-Identifier: GPL-3.0-only use anyhow; -use cctk::sctk::reexports::calloop::{self}; +use cctk::sctk::reexports::calloop::{self, channel::SyncSender}; use cosmic::iced::{ self, Subscription, - futures::{self, SinkExt}, + futures::{self, SinkExt, StreamExt, channel::mpsc}, stream, }; +use cosmic_protocols::a11y::v1::client::cosmic_a11y_manager_v1::Filter; use cosmic_settings_a11y_manager_subscription::{ self as thread, AccessibilityEvent, AccessibilityRequest, }; diff --git a/cosmic-applet-audio/Cargo.toml b/cosmic-applet-audio/Cargo.toml index 8293cf56..96d37cad 100644 --- a/cosmic-applet-audio/Cargo.toml +++ b/cosmic-applet-audio/Cargo.toml @@ -1,15 +1,15 @@ [package] name = "cosmic-applet-audio" -version = "1.3.0" +version = "1.0.2" edition = "2024" license = "GPL-3.0-only" [dependencies] -futures.workspace = true i18n-embed-fl.workspace = true i18n-embed.workspace = true -cosmic.workspace = true -mpris2-zbus = { path = "../../dbus-settings-bindings/mpris2" } +libcosmic.workspace = true +mpris2-zbus = { git = "https://github.com/pop-os/dbus-settings-bindings" } +# mpris2-zbus = { path = "../../dbus-settings-bindings/mpris2" } rust-embed.workspace = true serde.workspace = true tokio.workspace = true @@ -19,9 +19,7 @@ tracing.workspace = true url = "2" urlencoding = "2.1.3" zbus.workspace = true -zlink.workspace = true -intmap = "3.1.3" -[dependencies.cosmic-settings-audio-client] -path = "../../cosmic-settings-daemon/audio-client" -features = ["codec"] +[dependencies.cosmic-settings-sound-subscription] +git = "https://github.com/pop-os/cosmic-settings" +# path = "../../cosmic-settings/subscriptions/sound" diff --git a/cosmic-applet-audio/i18n/el/cosmic_applet_audio.ftl b/cosmic-applet-audio/i18n/el/cosmic_applet_audio.ftl index 1bdfccf1..e69de29b 100644 --- a/cosmic-applet-audio/i18n/el/cosmic_applet_audio.ftl +++ b/cosmic-applet-audio/i18n/el/cosmic_applet_audio.ftl @@ -1,7 +0,0 @@ -output = Έξοδος -show-media-controls = Εμφάνιση στοιχείων ελέγχου πολυμέσων στη γραμμή συστήματος -disconnected = Το PulseAudio αποσυνδέθηκε -no-device = Καμία επιλεγμένη συσκευή -input = Είσοδος -unknown-artist = Άγνωστος -sound-settings = Ρυθμίσεις ήχου... diff --git a/cosmic-applet-audio/i18n/hu/cosmic_applet_audio.ftl b/cosmic-applet-audio/i18n/hu/cosmic_applet_audio.ftl index b3fdb11d..c26f9fb0 100644 --- a/cosmic-applet-audio/i18n/hu/cosmic_applet_audio.ftl +++ b/cosmic-applet-audio/i18n/hu/cosmic_applet_audio.ftl @@ -2,6 +2,6 @@ output = Kimenet input = Bemenet show-media-controls = Médiavezérlők megjelenítése a panelen sound-settings = Hangbeállítások… -disconnected = A PulseAudio-kapcsolat megszakadt -no-device = Nincs eszköz kiválasztva +disconnected = PulseAudio nincs csatlakozva +no-device = Nincs kiválasztott eszköz unknown-artist = Ismeretlen diff --git a/cosmic-applet-audio/i18n/kmr/cosmic_applet_audio.ftl b/cosmic-applet-audio/i18n/kmr/cosmic_applet_audio.ftl index 373ab157..e69de29b 100644 --- a/cosmic-applet-audio/i18n/kmr/cosmic_applet_audio.ftl +++ b/cosmic-applet-audio/i18n/kmr/cosmic_applet_audio.ftl @@ -1,7 +0,0 @@ -output = Deran -input = Ketan -unknown-artist = Nenas -sound-settings = Sazkariyên dengê... -disconnected = Girêdana PulseAudio qut bû -no-device = Tu amûr nehatine hilbijartin -show-media-controls = Kontrolên mediyayê li ser destgehê nîşan bide diff --git a/cosmic-applet-audio/i18n/lo/cosmic_applet_audio.ftl b/cosmic-applet-audio/i18n/lo/cosmic_applet_audio.ftl deleted file mode 100644 index e69de29b..00000000 diff --git a/cosmic-applet-audio/i18n/sr/cosmic_applet_audio.ftl b/cosmic-applet-audio/i18n/sr/cosmic_applet_audio.ftl index 9ddf8185..e69de29b 100644 --- a/cosmic-applet-audio/i18n/sr/cosmic_applet_audio.ftl +++ b/cosmic-applet-audio/i18n/sr/cosmic_applet_audio.ftl @@ -1,7 +0,0 @@ -show-media-controls = Прикажи управљања медијима на траци -disconnected = Пулс-аудио откачен -no-device = Ниједан уређај није изабран -input = Улаз -output = Излаз -unknown-artist = Непознато -sound-settings = Подешавања звука... diff --git a/cosmic-applet-audio/src/lib.rs b/cosmic-applet-audio/src/lib.rs index 7647100d..5f49d0f9 100644 --- a/cosmic-applet-audio/src/lib.rs +++ b/cosmic-applet-audio/src/lib.rs @@ -2,13 +2,12 @@ // SPDX-License-Identifier: GPL-3.0-only mod localize; -mod model; mod mouse_area; use crate::localize::localize; use config::{AudioAppletConfig, amplification_sink, amplification_source}; use cosmic::{ - Apply, Element, Renderer, Task, Theme, app, + Element, Renderer, Task, Theme, app, applet::{ column as applet_column, cosmic_panel_config::PanelAnchor, @@ -19,24 +18,18 @@ use cosmic::{ cosmic_config::CosmicConfigEntry, cosmic_theme::Spacing, iced::{ - self, Alignment, Length, Rectangle, Subscription, + self, Alignment, Length, Subscription, futures::StreamExt, widget::{self, column, row, slider}, window, }, surface, theme, - widget::{ - Row, button, container, divider, icon, - rectangle_tracker::{RectangleTracker, RectangleUpdate, rectangle_tracker_subscription}, - space, text, toggler, - }, + widget::{Row, button, container, divider, icon, space, text, toggler}, }; -use cosmic_settings_audio_client::{self as audio_client, CosmicAudioProxy}; -use futures::SinkExt; +use cosmic_settings_sound_subscription as css; use iced::platform_specific::shell::wayland::commands::popup::{destroy_popup, get_popup}; use mpris_subscription::{MprisRequest, MprisUpdate}; use mpris2_zbus::player::PlaybackStatus; -use std::{cell::RefCell, rc::Rc, sync::Arc}; mod config; mod mpris_subscription; @@ -57,10 +50,8 @@ pub struct Audio { core: cosmic::app::Core, /// Track the applet's popup window. popup: Option, - /// Varlink connection to `com.system76.CosmicSettings.Audio`. - audio_client: Option>>, - /// Known audio device state - model: model::Model, + /// 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. @@ -77,14 +68,12 @@ pub struct Audio { player_status: Option, /// Used to request an activation token for opening cosmic-settings. token_tx: Option>, - rectangle_tracker: Option>, - rectangle: Option, } impl Audio { fn output_icon_name(&self) -> &'static str { - let volume = self.model.active_sink.volume; - let mute = self.model.active_sink.mute; + let volume = self.model.sink_volume; + let mute = self.model.sink_mute; if mute || volume == 0 { "audio-volume-muted-symbolic" } else if volume < 33 { @@ -99,8 +88,8 @@ impl Audio { } fn input_icon_name(&self) -> &'static str { - let volume = self.model.active_source.volume; - let mute = self.model.active_source.mute; + let volume = self.model.source_volume; + let mute = self.model.source_mute; if mute || volume == 0 { "microphone-sensitivity-muted-symbolic" } else if volume < 33 { @@ -111,21 +100,6 @@ impl Audio { "microphone-sensitivity-high-symbolic" } } - - /// Refresh the volume ceilings and slider breakpoints from the - /// amplification config. - /// - /// Must run at startup, not only when the popup opens: the - /// scroll-to-adjust handler clamps against `max_sink_volume`, which - /// `Default` leaves at 0. Until the popup had been opened once, every - /// wheel event therefore resolved to `SetSinkVolume(0)`. - fn refresh_volume_limits(&mut self) { - (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, &[][..]) }; - } } #[derive(Debug, PartialEq, Eq, Default)] @@ -136,10 +110,8 @@ enum IsOpen { Input, } -#[derive(Clone, Debug)] +#[derive(Debug, Clone)] pub enum Message { - /// Connection to `com.system76.CosmicSettings`. - Client(Arc), Ignore, SetSinkVolume(u32), SetSourceVolume(u32), @@ -157,9 +129,8 @@ pub enum Message { MprisRequest(MprisRequest), Token(TokenUpdate), OpenSettings, - Subscription(audio_client::Event), + Subscription(css::Message), Surface(surface::Action), - Rectangle(RectangleUpdate), } // TODO @@ -271,13 +242,19 @@ 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 app = Self { - core, - ..Default::default() - }; - app.refresh_volume_limits(); + 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(); - (app, Task::none()) + ( + Self { + core, + model, + ..Default::default() + }, + Task::none(), + ) } fn core(&self) -> &cosmic::app::Core { @@ -294,14 +271,6 @@ impl cosmic::Application for Audio { fn update(&mut self, message: Message) -> app::Task { match message { - Message::Rectangle(u) => match u { - RectangleUpdate::Rectangle(r) => { - self.rectangle = Some(r.1); - } - RectangleUpdate::Init(tracker) => { - self.rectangle_tracker.replace(tracker); - } - }, Message::Ignore => {} Message::TogglePopup => { if let Some(p) = self.popup.take() { @@ -310,23 +279,26 @@ impl cosmic::Application for Audio { let new_id = window::Id::unique(); self.popup.replace(new_id); - self.refresh_volume_limits(); + (self.max_sink_volume, self.sink_breakpoints) = if amplification_sink() { + (150, &[100][..]) + } else { + (100, &[][..]) + }; - let mut popup_settings = self.core.applet.get_popup_settings( + (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(), new_id, None, None, None, ); - if let Some(r) = self.rectangle { - popup_settings.positioner.anchor_rect = Rectangle { - x: r.x as i32, - y: r.y as i32, - width: r.width as i32, - height: r.height as i32, - }; - } + return get_popup(popup_settings); } } @@ -346,74 +318,52 @@ impl cosmic::Application for Audio { } } Message::Subscription(message) => { - self.model.update(message); + return self + .model + .update(message) + .map(|message| Message::Subscription(message).into()); } Message::SetDefaultSink(pos) => { - if let Some(&pos) = self.model.sinks.sorted_index.get(pos) - && let Some(&node_id) = self.model.sinks.id.get(pos as usize) - && let Some(client) = self.audio_client.as_mut() - { - futures::executor::block_on(async { - _ = client.borrow_mut().conn.set_default(node_id, true).await; - }); - } + return self + .model + .set_default_sink(pos) + .map(|message| Message::Subscription(message).into()); } Message::SetDefaultSource(pos) => { - if let Some(&pos) = self.model.sources.sorted_index.get(pos) - && let Some(&node_id) = self.model.sources.id.get(pos as usize) - && let Some(client) = self.audio_client.as_mut() - { - futures::executor::block_on(async { - _ = client.borrow_mut().conn.set_default(node_id, true).await; - }); - } + return self + .model + .set_default_source(pos) + .map(|message| Message::Subscription(message).into()); } - Message::ToggleSinkMute => { - if let Some(ref mut client) = self.audio_client { - futures::executor::block_on(async { - _ = client.borrow_mut().conn.sink_mute_toggle().await; - }); - } - } + Message::ToggleSinkMute => self.model.toggle_sink_mute(), - Message::ToggleSourceMute => { - if let Some(ref mut client) = self.audio_client { - futures::executor::block_on(async { - _ = client.borrow_mut().conn.source_mute_toggle().await; - }); - } - } + Message::ToggleSourceMute => self.model.toggle_source_mute(), Message::SetSinkVolume(volume) => { - if let Some(ref mut client) = self.audio_client { - self.model.active_sink.volume = volume; - self.model.active_sink.volume_text = volume.to_string(); - futures::executor::block_on(async { - _ = client.borrow_mut().conn.set_sink_volume(volume).await; - }); - } + return self + .model + .set_sink_volume(volume) + .map(|message| Message::Subscription(message).into()); } Message::SetSourceVolume(volume) => { - if let Some(ref mut client) = self.audio_client { - self.model.active_source.volume = volume; - self.model.active_source.volume_text = volume.to_string(); - futures::executor::block_on(async { - _ = client.borrow_mut().conn.set_source_volume(volume).await; - }); - } + return self + .model + .set_source_volume(volume) + .map(|message| Message::Subscription(message).into()); } Message::ToggleMediaControlsInTopPanel(enabled) => { self.config.show_media_controls_in_top_panel = enabled; if let Ok(helper) = cosmic::cosmic_config::Config::new(Self::APP_ID, AudioAppletConfig::VERSION) - && let Err(err) = self.config.write_entry(&helper) { - tracing::error!(?err, "Error writing config"); + if let Err(err) = self.config.write_entry(&helper) { + tracing::error!(?err, "Error writing config"); + } } } Message::CloseRequested(id) => { @@ -511,12 +461,6 @@ impl cosmic::Application for Audio { cosmic::app::Action::Surface(a), )); } - Message::Client(client) => { - if let Some(client) = Arc::into_inner(client) { - self.audio_client = Some(Rc::new(RefCell::new(client))); - self.model = model::Model::default(); - } - } } Task::none() @@ -532,50 +476,7 @@ impl cosmic::Application for Audio { }), mpris_subscription::mpris_subscription(0).map(Message::Mpris), activation_token_subscription(0).map(Message::Token), - Subscription::run(|| { - iced::stream::channel( - 1, - move |mut emitter: futures::channel::mpsc::Sender<_>| async move { - loop { - let mut client = match audio_client::connect().await { - Ok(client) => client, - Err(why) => { - if let 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(Message::Client(Arc::new(client))).await; - while let Some(message) = stream.next().await { - match message { - Ok(event) => { - _ = emitter.send(Message::Subscription(event)).await; - } - Err(why) => { - tracing::error!(?why, "event error"); - } - } - } - } - } - }, - ) - }), - rectangle_tracker_subscription(0).map(|update| Message::Rectangle(update.1)), + Subscription::run(|| css::watch().map(Message::Subscription)), ]) } @@ -586,94 +487,63 @@ impl cosmic::Application for Audio { .icon_button(self.output_icon_name()) .on_press_down(Message::TogglePopup); - const WHEEL_STEP: f32 = 5.0; // 5% par cran logique - // Wayland axis_v120 envoie un cran physique en rafale de plusieurs - // ScrollDelta::Pixels (5–8 events ~15–20px), pour ~120px par cran. On - // accumule ces sub-events dans un thread_local et on n'émet qu'au - // passage d'un seuil — sinon `signum()` faisait croire que chaque - // sub-event = un cran, et un seul cran physique faisait chuter le - // volume jusqu'à 0 ("coupe le son"). - const PIXEL_THRESHOLD: f32 = 15.0; // px par cran logique - std::thread_local! { - static PIXEL_ACC: std::cell::Cell = const { std::cell::Cell::new(0.0) }; - } + const WHEEL_STEP: f32 = 5.0; // 5% per wheel event let btn = crate::mouse_area::MouseArea::new(btn).on_mouse_wheel(|delta| { let scroll_vector = match delta { - iced::mouse::ScrollDelta::Lines { y, .. } => { - PIXEL_ACC.with(|a| a.set(0.0)); - y * WHEEL_STEP - } - iced::mouse::ScrollDelta::Pixels { y, .. } => { - PIXEL_ACC.with(|acc_cell| { - let acc = acc_cell.get() + y; - let steps = (acc / PIXEL_THRESHOLD).trunc(); - acc_cell.set(acc - steps * PIXEL_THRESHOLD); - steps * WHEEL_STEP - }) - } + iced::mouse::ScrollDelta::Lines { y, .. } => y.signum() * WHEEL_STEP, // -1/0/1 + iced::mouse::ScrollDelta::Pixels { y, .. } => y.signum(), // -1/0/1 }; if scroll_vector == 0.0 { return Message::Ignore; } - let new_volume = (self.model.active_sink.volume as f64 + (scroll_vector as f64)) + 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.round() as u32) + Message::SetSinkVolume(new_volume as u32) }); - let mut has_playback_buttons = false; let playback_buttons = (!self.core.applet.suggested_bounds.as_ref().is_some_and(|c| { // if we have a configure for width and height, we're in a overflow popup c.width > 0. && c.height > 0. })) .then(|| self.playback_buttons()); - let mut ret = if let Some(playback_buttons) = playback_buttons - && !playback_buttons.is_empty() - { - has_playback_buttons = true; - Element::from(match self.core.applet.anchor { - PanelAnchor::Left | PanelAnchor::Right => Element::from( - applet_column::Column::with_children(playback_buttons) - .push(btn) - .align_x(Alignment::Center) - .height(Length::Shrink) - // TODO configurable variable from the panel? - .spacing( - -(self.core.applet.suggested_padding(true).0 as f32) - * self.core.applet.padding_overlap, + self.core + .applet + .autosize_window( + if let Some(playback_buttons) = playback_buttons + && !playback_buttons.is_empty() + { + match self.core.applet.anchor { + PanelAnchor::Left | PanelAnchor::Right => Element::from( + applet_column::Column::with_children(playback_buttons) + .push(btn) + .align_x(Alignment::Center) + .height(Length::Shrink) + // TODO configurable variable from the panel? + .spacing( + -(self.core.applet.suggested_padding(true).0 as f32) + * self.core.applet.padding_overlap, + ), ), - ), - PanelAnchor::Top | PanelAnchor::Bottom => { - applet_row::Row::with_children(playback_buttons) - .push(btn) - .align_y(Alignment::Center) - .width(Length::Shrink) - // TODO configurable variable from the panel? - .spacing( - -(self.core.applet.suggested_padding(true).0 as f32) - * self.core.applet.padding_overlap, - ) - .into() - } - }) - } else { - btn.into() - }; - - if let Some(tracker) = self.rectangle_tracker.as_ref() - && has_playback_buttons - { - ret = tracker.container(0, ret).into() - } - - if !self.core.applet.suggested_bounds.as_ref().is_some_and(|c| { - // if we have a configure for width and height, we're in a overflow popup - c.width > 0. && c.height > 0. - }) { - ret = self.core.applet.autosize_window(ret).into(); - } - ret + PanelAnchor::Top | PanelAnchor::Bottom => { + applet_row::Row::with_children(playback_buttons) + .push(btn) + .align_y(Alignment::Center) + .width(Length::Shrink) + // TODO configurable variable from the panel? + .spacing( + -(self.core.applet.suggested_padding(true).0 as f32) + * self.core.applet.padding_overlap, + ) + .into() + } + } + } else { + btn.into() + }, + ) + .into() } fn view_window(&self, _id: window::Id) -> Element<'_, Message> { @@ -681,21 +551,19 @@ impl cosmic::Application for Audio { space_xxs, space_s, .. } = theme::active().cosmic().spacing; - let sink: Option<&str> = self + let sink = self .model - .sinks - .active() - .map(|pos| self.model.sinks.sorted_display[pos].as_ref()); - let source: Option<&str> = self + .active_sink() + .and_then(|pos| self.model.sinks().get(pos)); + let source = self .model - .sources - .active() - .map(|pos| self.model.sources.sorted_display[pos].as_ref()); + .active_source() + .and_then(|pos| self.model.sources().get(pos)); let mut audio_content = { let output_slider = slider( 0..=self.max_sink_volume, - self.model.active_sink.volume, + self.model.sink_volume, Message::SetSinkVolume, ) .width(Length::FillPortion(5)) @@ -703,7 +571,7 @@ impl cosmic::Application for Audio { let input_slider = slider( 0..=self.max_source_volume, - self.model.active_source.volume, + self.model.source_volume, Message::SetSourceVolume, ) .width(Length::FillPortion(5)) @@ -722,7 +590,7 @@ impl cosmic::Application for Audio { .line_height(24) .on_press(Message::ToggleSinkMute), output_slider, - container(text(&self.model.active_sink.volume_text).size(16)) + container(text(&self.model.sink_volume_text).size(16)) .width(Length::FillPortion(1)) .align_x(Alignment::End) ] @@ -741,7 +609,7 @@ impl cosmic::Application for Audio { .line_height(24) .on_press(Message::ToggleSourceMute), input_slider, - container(text(&self.model.active_source.volume_text).size(16)) + container(text(&self.model.source_volume_text).size(16)) .width(Length::FillPortion(1)) .align_x(Alignment::End) ] @@ -756,7 +624,7 @@ impl cosmic::Application for Audio { Some(sink) => sink.to_owned(), None => fl!("no-device"), }, - &self.model.sinks.sorted_display, + self.model.sinks(), Message::OutputToggle, Message::SetDefaultSink, ), @@ -767,7 +635,7 @@ impl cosmic::Application for Audio { Some(source) => source.to_owned(), None => fl!("no-device"), }, - &self.model.sources.sorted_display, + self.model.sources(), Message::InputToggle, Message::SetDefaultSource, ) @@ -823,15 +691,19 @@ impl cosmic::Application for Audio { } if let Some(play) = self.is_play() { control_elements.push( - button::icon(icon::from_name(if play { PLAY } else { PAUSE }).symbolic(true)) - .extra_small() - .class(cosmic::theme::Button::AppletIcon) - .on_press(if play { - Message::MprisRequest(MprisRequest::Play) - } else { - Message::MprisRequest(MprisRequest::Pause) - }) - .into(), + button::icon( + icon::from_name(if play { PLAY } else { PAUSE }) + .size(32) + .symbolic(true), + ) + .extra_small() + .class(cosmic::theme::Button::AppletIcon) + .on_press(if play { + Message::MprisRequest(MprisRequest::Play) + } else { + Message::MprisRequest(MprisRequest::Pause) + }) + .into(), ); } if let Some(go_next) = self.go_next(32) { @@ -882,21 +754,20 @@ impl cosmic::Application for Audio { } } -fn revealer<'a>( +fn revealer( open: bool, title: String, selected: String, - devices: &'a [Arc], + devices: &[String], toggle: Message, mut change: impl FnMut(usize) -> Message + 'static, -) -> widget::Column<'a, Message, crate::Theme, Renderer> { +) -> widget::Column<'static, Message, crate::Theme, Renderer> { if open { - devices.iter().enumerate().fold( + devices.iter().cloned().enumerate().fold( column![revealer_head(open, title, selected, toggle)].width(Length::Fill), - move |col, (id, name)| { + |col, (id, name)| { col.push( - text::body(name.as_ref()) - .apply(menu_button) + menu_button(text::body(name)) .on_press(change(id)) .width(Length::Fill) .padding([8, 48]), @@ -914,9 +785,9 @@ fn revealer_head( selected: String, toggle: Message, ) -> cosmic::widget::Button<'static, Message> { - cosmic::widget::column::with_capacity(2) - .push(text::body(title).width(Length::Fill)) - .push(text::caption(selected)) - .apply(menu_button) - .on_press(toggle) + menu_button(column![ + text::body(title).width(Length::Fill), + text::caption(selected), + ]) + .on_press(toggle) } diff --git a/cosmic-applet-audio/src/model.rs b/cosmic-applet-audio/src/model.rs deleted file mode 100644 index 42c76b3c..00000000 --- a/cosmic-applet-audio/src/model.rs +++ /dev/null @@ -1,393 +0,0 @@ -// Copyright 2026 System76 -// SPDX-License-Identifier: GPL-3.0-only - -use std::sync::Arc; - -use cosmic_settings_audio_client::{self as audio_client, Availability, RouteInfo}; -use intmap::IntMap; - -pub type DeviceId = u32; -pub type NodeId = u32; - -#[derive(Debug, Default)] -pub struct Model { - 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, -} - -#[derive(Debug, Default)] -pub struct Nodes { - active: Option, - pub sorted_display: Box<[Arc]>, - pub sorted_index: Box<[u16]>, - 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 active(&self) -> Option { - self.active.and_then(|active| { - self.sorted_index - .iter() - .position(|idx| *idx as usize == active) - }) - } - - pub fn dropdown_sort(&mut self) { - let mut enumerated_displays = self - .display - .clone() - .into_iter() - .enumerate() - .map(|(index, display)| (index as u16, display)) - .collect::>(); - enumerated_displays.sort_by_key(|v| v.1.clone()); - let (indexes, displays): (Vec<_>, Vec<_>) = enumerated_displays.into_iter().unzip(); - self.sorted_display = displays.into_boxed_slice(); - self.sorted_index = indexes.into_boxed_slice(); - } - - 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); - self.dropdown_sort(); - if self.active == Some(pos) { - self.active = None; - } - true - } -} - -#[derive(Debug, Default)] -pub struct ActiveNode { - pub volume_text: String, - pub volume: u32, - pub mute: bool, -} - -impl Model { - pub fn update(&mut self, event: audio_client::Event) { - tracing::debug!(?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.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::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(Arc::default()); - 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(node_name( - &self.translate(&route.description), - &self.sinks.description[pos], - )); - } - } - - None - }) - .unwrap_or_else(|| { - node_name( - &node.device_profile_description, - &self.sinks.description[pos], - ) - }); - - self.sinks.dropdown_sort(); - - 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(Arc::default()); - 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(|(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(node_name( - &self.translate(&route.description), - &self.sources.description[pos], - )); - } - } - - None - }) - .unwrap_or_else(|| { - Some(node_name( - &node.device_profile_description, - &self.sources.description[pos], - )) - }) - { - self.sources.display[pos] = name; - self.sources.dropdown_sort(); - } 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 index == 0 { - *routes = vec![route]; - } else { - 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; - } - } - - 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_routes.remove(device_id); - } - - _ => (), - } - } - - 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] = node_name( - &self.translate(&route.description), - &self.sinks.description[pos], - ); - self.sinks.dropdown_sort(); - 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] = node_name( - &self.translate(&route.description), - &self.sources.description[pos], - ); - self.sources.dropdown_sort(); - break; - } - } - } - } - - pub fn translate(&self, description: &str) -> String { - description - .replace("High Definition", "HD") - .replace("DisplayPort", "DP") - .replace("Controller", "") - } -} - -fn node_name(route: &str, node: &str) -> Arc { - if route.is_empty() { - node.to_owned() - } else { - [route, " - ", node].concat() - } - .into() -} diff --git a/cosmic-applet-audio/src/mouse_area.rs b/cosmic-applet-audio/src/mouse_area.rs index 94efb4f7..6ae8547a 100644 --- a/cosmic-applet-audio/src/mouse_area.rs +++ b/cosmic-applet-audio/src/mouse_area.rs @@ -5,7 +5,7 @@ use cosmic::iced::core::Point; use cosmic::iced::core::{ Clipboard, Element, Layout, Length, Rectangle, Shell, Size, Widget, - event::Event, + event::{self, Event}, layout, mouse, overlay, renderer, touch, widget::{Operation, Tree, tree}, }; @@ -307,19 +307,6 @@ fn update( shell: &mut Shell<'_, Message>, state: &mut State, ) { - // Handle wheel events before the bounds check. The panel's nested - // compositor routes axis events to this surface only when the pointer - // is over the applet, but the cursor coordinates it sends may be in - // panel-local space rather than surface-local space. This makes - // cursor.is_over() unreliable for scroll events. - if let Event::Mouse(mouse::Event::WheelScrolled { delta }) = event { - if let Some(message) = widget.on_mouse_wheel.as_ref() { - shell.publish((message)(*delta)); - shell.capture_event(); - return; - } - } - if !cursor.is_over(layout.bounds()) { if !state.is_out_of_bounds { if widget @@ -429,4 +416,12 @@ fn update( } } } + + if let Some(message) = widget.on_mouse_wheel.as_ref() { + if let Event::Mouse(mouse::Event::WheelScrolled { delta }) = event { + shell.publish((message)(*delta)); + shell.capture_event(); + return; + } + } } diff --git a/cosmic-applet-battery/Cargo.toml b/cosmic-applet-battery/Cargo.toml index 647ad9a1..2218f638 100644 --- a/cosmic-applet-battery/Cargo.toml +++ b/cosmic-applet-battery/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cosmic-applet-battery" -version = "1.3.0" +version = "1.0.2" edition = "2024" license = "GPL-3.0-only" @@ -12,7 +12,7 @@ drm = "0.14.1" futures.workspace = true i18n-embed-fl.workspace = true i18n-embed.workspace = true -cosmic.workspace = true +libcosmic.workspace = true rust-embed.workspace = true rustc-hash.workspace = true tokio.workspace = true @@ -24,7 +24,9 @@ zbus.workspace = true serde.workspace = true [dependencies.cosmic-settings-upower-subscription] -path = "../../cosmic-settings/subscriptions/upower" +git = "https://github.com/pop-os/cosmic-settings" +# path = "../../cosmic-settings/subscriptions/upower" [dependencies.cosmic-settings-daemon-subscription] -path = "../../cosmic-settings/subscriptions/settings-daemon" +git = "https://github.com/pop-os/cosmic-settings" +# path = "../../cosmic-settings/subscriptions/settings-daemon" diff --git a/cosmic-applet-battery/i18n/el/cosmic_applet_battery.ftl b/cosmic-applet-battery/i18n/el/cosmic_applet_battery.ftl index 06eca770..e69de29b 100644 --- a/cosmic-applet-battery/i18n/el/cosmic_applet_battery.ftl +++ b/cosmic-applet-battery/i18n/el/cosmic_applet_battery.ftl @@ -1,14 +0,0 @@ -battery = Μπαταρία -power-settings = Ρυθμίσεις ενέργειας και μπαταρίας... -hours = ώ -until-empty = μέχρι την αποφόρτιση -battery-desc = Μειωμένη χρήση ενέργειας και επιδόσεις. -max-charge = Επεκτείνετε τη διάρκεια ζωής της μπαταρίας σας ορίζοντας ως μέγιστη τιμή φόρτισης το 80% -balanced = Ισορροπημένη -seconds = δ -dgpu-running = Η ανεξάρτητη GPU είναι ενεργή και μπορεί να μειώσει τη διάρκεια ζωής της μπαταρίας -performance = Υψηλές επιδόσεις -performance-desc = Υψηλές επιδόσεις και χρήση ενέργειας. -minutes = λ -balanced-desc = Τυπικές επιδόσεις και χρήση μπαταρίας. -dgpu-applications = Εφαρμογές που χρησιμοποιούν την ανεξάρτητη GPU { $gpu_name } diff --git a/cosmic-applet-battery/i18n/hu/cosmic_applet_battery.ftl b/cosmic-applet-battery/i18n/hu/cosmic_applet_battery.ftl index 01626a96..814d3605 100644 --- a/cosmic-applet-battery/i18n/hu/cosmic_applet_battery.ftl +++ b/cosmic-applet-battery/i18n/hu/cosmic_applet_battery.ftl @@ -1,13 +1,13 @@ battery = Akkumulátor -battery-desc = Csökkentett energiafogyasztás és teljesítmény. +battery-desc = Csökkentett energiafogyasztás és teljesítmény balanced = Kiegyensúlyozott -balanced-desc = Normál teljesítmény és akkumulátorhasználat. +balanced-desc = Normál teljesítmény és akkumulátorhasználat performance = Nagy teljesítmény -performance-desc = Nagy teljesítmény és energiafogyasztás. -max-charge = Az akkumulátor élettartamának növelése érdekében állítsd a maximális töltési szintet 80%-ra -seconds = mp -minutes = p -hours = ó +performance-desc = Nagy teljesítmény és energiafogyasztás +max-charge = Az akkumulátor élettartamának növelése érdekében állítsa a maximális töltési szintet 80%-ra +seconds = másodperc +minutes = perc +hours = óra until-empty = a lemerülésig power-settings = Energia- és akkumulátorbeállítások… dgpu-running = A dedikált GPU aktív, ami csökkentheti az akkumulátor élettartamát diff --git a/cosmic-applet-battery/i18n/kmr/cosmic_applet_battery.ftl b/cosmic-applet-battery/i18n/kmr/cosmic_applet_battery.ftl index 8c0d92d1..e69de29b 100644 --- a/cosmic-applet-battery/i18n/kmr/cosmic_applet_battery.ftl +++ b/cosmic-applet-battery/i18n/kmr/cosmic_applet_battery.ftl @@ -1,14 +0,0 @@ -battery = Betarî -seconds = ç -minutes = x -hours = d -until-empty = Heya vala dibe -power-settings = Sazkariyên hêz û betariyê... -battery-desc = Bikaranîna hêzê kêmkirî û performans. -balanced = Hevseng -balanced-desc = Performansa standard û bikaranîna betariyê. -performance = Performansa bilind -performance-desc = Performansa bilind û bikaranîna hêzê. -max-charge = Jiyana betariyê xwe dirêj bike bi riya danîna sînorê herî mezin yê tijîkrinê %80 -dgpu-running = YKG a veqetandî çalak e û dikare jiyana betariyê kêm bike -dgpu-applications = Sepanên ku YKG a veqetandî ya { $gpu_name } bi kar tînin diff --git a/cosmic-applet-battery/i18n/lo/cosmic_applet_battery.ftl b/cosmic-applet-battery/i18n/lo/cosmic_applet_battery.ftl deleted file mode 100644 index e69de29b..00000000 diff --git a/cosmic-applet-battery/i18n/sr/cosmic_applet_battery.ftl b/cosmic-applet-battery/i18n/sr/cosmic_applet_battery.ftl index 3d242270..e69de29b 100644 --- a/cosmic-applet-battery/i18n/sr/cosmic_applet_battery.ftl +++ b/cosmic-applet-battery/i18n/sr/cosmic_applet_battery.ftl @@ -1,14 +0,0 @@ -power-settings = Подешавања напајања и батерије... -hours = ч -until-empty = до празне -battery-desc = Смањен учинак и употреба струје. -balanced = Уравнотежено -battery = Батерија -seconds = с -performance = Најбрже -performance-desc = Висок учинак и употреба струје. -minutes = м -balanced-desc = Уобичајени учинак и употреба батерије. -max-charge = Повећајте век трајања батерије подешавањем највише вредности пуњења на 80% -dgpu-running = Засебна графичка је покренута и може умањити век трајања батерије -dgpu-applications = Програми који користе засебну графичку { $gpu_name } diff --git a/cosmic-applet-battery/i18n/zh-TW/cosmic_applet_battery.ftl b/cosmic-applet-battery/i18n/zh-TW/cosmic_applet_battery.ftl index f5378e27..33c18ba3 100644 --- a/cosmic-applet-battery/i18n/zh-TW/cosmic_applet_battery.ftl +++ b/cosmic-applet-battery/i18n/zh-TW/cosmic_applet_battery.ftl @@ -1,4 +1,4 @@ -battery = 電池 +battery = 省電 battery-desc = 降低效能與耗電量。 balanced = 平衡 balanced-desc = 標準效能與耗電量。 diff --git a/cosmic-applet-battery/src/app.rs b/cosmic-applet-battery/src/app.rs index 22d9e6cc..67e513c3 100644 --- a/cosmic-applet-battery/src/app.rs +++ b/cosmic-applet-battery/src/app.rs @@ -40,7 +40,7 @@ use cosmic_settings_upower_subscription::{ }; use rustc_hash::FxHashMap; -use std::{path::PathBuf, time::Duration}; +use std::{path::PathBuf, sync::LazyLock, time::Duration}; use tokio::sync::mpsc::UnboundedSender; // XXX improve @@ -169,24 +169,9 @@ impl CosmicBatteryApplet { self.charging_limit = Some(limit); self.update_battery(self.battery_percent, self.on_battery); } - - fn panel_icon_name(&self) -> &str { - if self.no_battery { - if self.screen_brightness.is_some() { - self.display_icon_name.as_str() - } else if self.kbd_brightness.is_some() { - "keyboard-brightness-symbolic" - } else { - "plugged-into-power-symbolic" - } - } else { - self.icon_name.as_str() - } - } } #[derive(Debug, Clone)] -#[allow(dead_code)] enum Message { TogglePopup, CloseRequested(window::Id), @@ -383,10 +368,6 @@ impl cosmic::Application for CosmicBatteryApplet { let _ = tx.send(()); } let mut tasks = vec![get_popup(popup_settings)]; - if let Some(tx) = &self.settings_daemon_sender { - let _ = tx.send(settings_daemon::Request::GetDisplayBrightness); - let _ = tx.send(settings_daemon::Request::GetMaxDisplayBrightness); - } // Try again every time a popup is opened if self.charging_limit.is_none() { tasks.push(Task::perform(get_charging_limit(), |limit| { @@ -526,7 +507,7 @@ impl cosmic::Application for CosmicBatteryApplet { Task::none() } - fn view(&self) -> Element<'_, Message> { + fn view(&self) -> Element { let is_horizontal = match self.core.applet.anchor { PanelAnchor::Top | PanelAnchor::Bottom => true, PanelAnchor::Left | PanelAnchor::Right => false, @@ -536,9 +517,19 @@ impl cosmic::Application for CosmicBatteryApplet { let applet_padding = self.core.applet.suggested_padding(true); let mut children = vec![ - icon::from_name(self.panel_icon_name()) - .size(suggested_size.0) - .into(), + icon::from_name(if self.no_battery { + if self.screen_brightness.is_some() { + self.display_icon_name.as_str() + } else if self.kbd_brightness.is_some() { + "keyboard-brightness-symbolic" + } else { + self.icon_name.as_str() + } + } else { + self.icon_name.as_str() + }) + .size(suggested_size.0) + .into(), ]; if self.config.show_percentage && !self.no_battery { diff --git a/cosmic-applet-bluetooth/Cargo.toml b/cosmic-applet-bluetooth/Cargo.toml index d3499343..03b67388 100644 --- a/cosmic-applet-bluetooth/Cargo.toml +++ b/cosmic-applet-bluetooth/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cosmic-applet-bluetooth" -version = "1.3.0" +version = "1.0.2" edition = "2024" license = "GPL-3.0-only" @@ -10,7 +10,7 @@ bluer = { version = "0.17", features = ["bluetoothd", "id"] } futures.workspace = true i18n-embed-fl.workspace = true i18n-embed.workspace = true -cosmic.workspace = true +libcosmic.workspace = true fastrand = "2.3.0" rust-embed.workspace = true rustc-hash.workspace = true diff --git a/cosmic-applet-bluetooth/i18n/ca/cosmic_applet_bluetooth.ftl b/cosmic-applet-bluetooth/i18n/ca/cosmic_applet_bluetooth.ftl index b069b4bf..e69de29b 100644 --- a/cosmic-applet-bluetooth/i18n/ca/cosmic_applet_bluetooth.ftl +++ b/cosmic-applet-bluetooth/i18n/ca/cosmic_applet_bluetooth.ftl @@ -1,12 +0,0 @@ -confirm-pin = Si us plau, verifiqueu que el següent PIN coincideix amb el mostrat a { $deviceName } -bluetooth = Bluetooth -unsuccessful = Error d'Emparellament -pairable = Emparellable -try-again = Reintentar -check-device = Si us plau, verifiqueu que { $deviceName } estigui encès, a l'abast i llest per emparellar-se. -other-devices = Altres dispositius Bluetooth -cancel = Cancel·lar -connected = Connectat -confirm = Confirmar -settings = Configuració del Bluetooth... -discoverable = Descobrible diff --git a/cosmic-applet-bluetooth/i18n/el/cosmic_applet_bluetooth.ftl b/cosmic-applet-bluetooth/i18n/el/cosmic_applet_bluetooth.ftl index 84134b90..98df71af 100644 --- a/cosmic-applet-bluetooth/i18n/el/cosmic_applet_bluetooth.ftl +++ b/cosmic-applet-bluetooth/i18n/el/cosmic_applet_bluetooth.ftl @@ -1,12 +1,2 @@ cancel = Ακύρωση confirm = Επιβεβαίωση -bluetooth = Bluetooth -confirm-pin = Επιβεβαιώστε ότι το ακόλουθο PIN είναι ίδιο με αυτό που εμφανίζεται στο { $deviceName } -unsuccessful = Ανεπιτυχής σύζευξη -pairable = Συνδέσιμο -try-again = Δοκιμή ξανά -check-device = Βεβαιωθείτε ότι το { $deviceName } είναι ενεργοποιημένο, βρίσκεται εντός εμβέλειας και είναι έτοιμο για σύζευξη. -other-devices = Άλλες συσκευές Bluetooth -connected = Συνδέθηκε -settings = Ρυθμίσεις Bluetooth... -discoverable = Ορατό diff --git a/cosmic-applet-bluetooth/i18n/hu/cosmic_applet_bluetooth.ftl b/cosmic-applet-bluetooth/i18n/hu/cosmic_applet_bluetooth.ftl index f12b24b3..ada54d0b 100644 --- a/cosmic-applet-bluetooth/i18n/hu/cosmic_applet_bluetooth.ftl +++ b/cosmic-applet-bluetooth/i18n/hu/cosmic_applet_bluetooth.ftl @@ -1,7 +1,7 @@ bluetooth = Bluetooth other-devices = Egyéb Bluetooth-eszközök settings = Bluetooth-beállítások… -connected = Kapcsolódva +connected = Csatlakoztatva confirm-pin = Ellenőrizd, hogy a következő PIN-kód megegyezik-e a(z) { $deviceName } eszközön megjelenő PIN-kóddal confirm = Megerősítés cancel = Mégse diff --git a/cosmic-applet-bluetooth/i18n/kmr/cosmic_applet_bluetooth.ftl b/cosmic-applet-bluetooth/i18n/kmr/cosmic_applet_bluetooth.ftl index 988f00ca..e69de29b 100644 --- a/cosmic-applet-bluetooth/i18n/kmr/cosmic_applet_bluetooth.ftl +++ b/cosmic-applet-bluetooth/i18n/kmr/cosmic_applet_bluetooth.ftl @@ -1,12 +0,0 @@ -cancel = Têk bibe -bluetooth = Bluetooth -connected = Hat girêdan -other-devices = Amûrên Bluetooth ên din -settings = Sazkariyên Bluetooth... -confirm-pin = Tika ye piştrast bike ku PIN a jêrîn bi ya ku li ser { $deviceName } tê xuyakirin re li hev tê -confirm = Bipejirîne -check-device = Piştrast bike ku { $deviceName } çalak e, di hundir rêjeyê de ye, û amade ye ji bo cotkirinê. -try-again = Dîsa hewl bide -discoverable = Dîtbar -pairable = Cotbar -unsuccessful = Cotkirin têkçûyî ye diff --git a/cosmic-applet-bluetooth/i18n/lo/cosmic_applet_bluetooth.ftl b/cosmic-applet-bluetooth/i18n/lo/cosmic_applet_bluetooth.ftl deleted file mode 100644 index e69de29b..00000000 diff --git a/cosmic-applet-bluetooth/i18n/sr/cosmic_applet_bluetooth.ftl b/cosmic-applet-bluetooth/i18n/sr/cosmic_applet_bluetooth.ftl index b6515b52..e69de29b 100644 --- a/cosmic-applet-bluetooth/i18n/sr/cosmic_applet_bluetooth.ftl +++ b/cosmic-applet-bluetooth/i18n/sr/cosmic_applet_bluetooth.ftl @@ -1,12 +0,0 @@ -confirm-pin = Потврдите да се овај ПИН подудара са оним приказаним на { $deviceName } -bluetooth = Блутут -unsuccessful = Неуспешно упаривање -pairable = Упарив -try-again = Покушај поново -check-device = Постарајте се да је { $deviceName } укључен, у домету и спреман за упаривање. -other-devices = Други блутут уређаји -cancel = Откажи -connected = Повезано -confirm = Потврди -settings = Подешавања блутута... -discoverable = Откривљив diff --git a/cosmic-applet-bluetooth/src/app.rs b/cosmic-applet-bluetooth/src/app.rs index 484b8bf3..2ca95ec5 100644 --- a/cosmic-applet-bluetooth/src/app.rs +++ b/cosmic-applet-bluetooth/src/app.rs @@ -21,10 +21,10 @@ use cosmic::{ widget::{Column, column, container, row}, }, theme, - widget::{button, divider, icon, indeterminate_circular, scrollable, text}, + widget::{button, divider, icon, scrollable, text}, }; use futures::FutureExt; -use std::{collections::HashMap, time::Duration}; +use std::{collections::HashMap, sync::LazyLock, time::Duration}; use tokio::sync::mpsc::Sender; use crate::{ @@ -63,7 +63,6 @@ impl CosmicBluetoothApplet { } #[derive(Debug, Clone)] -#[allow(dead_code)] enum Message { TogglePopup, CloseRequested(window::Id), @@ -146,7 +145,7 @@ impl cosmic::Application for CosmicBluetoothApplet { } Message::BluetoothEvent(e) => match e { BluerEvent::RequestResponse { - req: _, + req, state, err_msg, } => { @@ -368,12 +367,14 @@ impl cosmic::Application for CosmicBluetoothApplet { ); } BluerDeviceStatus::Paired => {} - BluerDeviceStatus::Connecting - | BluerDeviceStatus::Disconnecting - | BluerDeviceStatus::Pairing => { - row = row.push(indeterminate_circular().size(24.0)); + BluerDeviceStatus::Connecting | BluerDeviceStatus::Disconnecting => { + row = row.push( + icon::from_name("process-working-symbolic") + .size(24) + .symbolic(true), + ); } - BluerDeviceStatus::Disconnected => continue, + BluerDeviceStatus::Disconnected | BluerDeviceStatus::Pairing => continue, } known_bluetooth.push( diff --git a/cosmic-applet-bluetooth/src/bluetooth.rs b/cosmic-applet-bluetooth/src/bluetooth.rs index 3a859932..b3648acd 100644 --- a/cosmic-applet-bluetooth/src/bluetooth.rs +++ b/cosmic-applet-bluetooth/src/bluetooth.rs @@ -149,6 +149,8 @@ pub fn bluetooth_subscription( }, BluerSessionEvent::AgentEvent(e) => BluerEvent::AgentEvent(e), + + _ => return, }; _ = output.send(message).await; @@ -197,7 +199,6 @@ pub enum BluerRequest { } #[derive(Debug, Clone)] -#[allow(dead_code)] pub enum BluerEvent { RequestResponse { req: BluerRequest, @@ -567,21 +568,22 @@ impl BluerSessionState { tick(&mut interval).await; let new_status = adapter_clone.is_powered().await.unwrap_or_default(); devices = build_device_list(devices, &adapter_clone).await; - let power_changed = new_status != status; - status = new_status; - let state = BluerState { - devices: devices.clone(), - bluetooth_enabled: status, - }; - if power_changed && state.bluetooth_enabled { - for d in &state.devices { - if d.paired_and_trusted() { - _ = req_tx.send(BluerRequest::ConnectDevice(d.address)).await; + if new_status != status { + status = new_status; + let state = BluerState { + devices: devices.clone(), + bluetooth_enabled: status, + }; + if state.bluetooth_enabled { + for d in &state.devices { + if d.paired_and_trusted() { + _ = req_tx.send(BluerRequest::ConnectDevice(d.address)).await; + } } } _ = wake_up_discover_tx.send(()).await; + let _ = tx.send(BluerSessionEvent::ChangesProcessed(state)).await; } - let _ = tx.send(BluerSessionEvent::ChangesProcessed(state)).await; } }); } @@ -672,9 +674,6 @@ impl BluerSessionState { } } - // Always refresh power state from the adapter - // to avoid stale cached is_powered values - is_powered = adapter_clone.is_powered().await.unwrap_or_default(); let _ = tx .send(BluerSessionEvent::ChangesProcessed(BluerState { devices: devices.clone(), @@ -854,7 +853,13 @@ async fn bluer_state(adapter: &Adapter) -> BluerState { #[inline(never)] async fn build_device_list(mut devices: Vec, adapter: &Adapter) -> Vec { - let addrs = adapter.device_addresses().await.unwrap_or_default(); + let addrs: Vec
= adapter + .device_addresses() + .await + .unwrap_or_default() + .into_iter() + .filter(|addr| !devices.iter().any(|d| d.address == *addr)) + .collect(); devices.clear(); if addrs.len() > devices.capacity() { diff --git a/cosmic-applet-input-sources/Cargo.toml b/cosmic-applet-input-sources/Cargo.toml index fdc1ea24..92a7daf3 100644 --- a/cosmic-applet-input-sources/Cargo.toml +++ b/cosmic-applet-input-sources/Cargo.toml @@ -1,14 +1,14 @@ [package] name = "cosmic-applet-input-sources" -version = "1.3.0" +version = "1.0.2" edition = "2024" license = "GPL-3.0-only" [dependencies] -cosmic-comp-config.workspace = true +cosmic-comp-config = { git = "https://github.com/pop-os/cosmic-comp.git", rev = "5eb5af4" } i18n-embed-fl.workspace = true i18n-embed.workspace = true -cosmic.workspace = true +libcosmic.workspace = true rust-embed.workspace = true tokio.workspace = true tracing-log.workspace = true diff --git a/cosmic-applet-input-sources/i18n/el/cosmic_applet_input_sources.ftl b/cosmic-applet-input-sources/i18n/el/cosmic_applet_input_sources.ftl index 546be28d..e69de29b 100644 --- a/cosmic-applet-input-sources/i18n/el/cosmic_applet_input_sources.ftl +++ b/cosmic-applet-input-sources/i18n/el/cosmic_applet_input_sources.ftl @@ -1,2 +0,0 @@ -show-keyboard-layout = Εμφάνιση διάταξης πληκτρολογίου... -keyboard-settings = Ρυθμίσεις πληκτρολογίου... diff --git a/cosmic-applet-input-sources/i18n/kmr/cosmic_applet_input_sources.ftl b/cosmic-applet-input-sources/i18n/kmr/cosmic_applet_input_sources.ftl index 737324c5..e69de29b 100644 --- a/cosmic-applet-input-sources/i18n/kmr/cosmic_applet_input_sources.ftl +++ b/cosmic-applet-input-sources/i18n/kmr/cosmic_applet_input_sources.ftl @@ -1,2 +0,0 @@ -show-keyboard-layout = Awaya kilîtdankê nîşan bide... -keyboard-settings = Sazkariyên kilîtdankê... diff --git a/cosmic-applet-input-sources/i18n/lo/cosmic_applet_input_sources.ftl b/cosmic-applet-input-sources/i18n/lo/cosmic_applet_input_sources.ftl deleted file mode 100644 index e69de29b..00000000 diff --git a/cosmic-applet-input-sources/i18n/sr/cosmic_applet_input_sources.ftl b/cosmic-applet-input-sources/i18n/sr/cosmic_applet_input_sources.ftl index 4d552b47..e69de29b 100644 --- a/cosmic-applet-input-sources/i18n/sr/cosmic_applet_input_sources.ftl +++ b/cosmic-applet-input-sources/i18n/sr/cosmic_applet_input_sources.ftl @@ -1,2 +0,0 @@ -show-keyboard-layout = Прикажи распоред тастатуре... -keyboard-settings = Подешавања тастатуре... diff --git a/cosmic-applet-input-sources/src/lib.rs b/cosmic-applet-input-sources/src/lib.rs index 2c845de1..93e84283 100644 --- a/cosmic-applet-input-sources/src/lib.rs +++ b/cosmic-applet-input-sources/src/lib.rs @@ -3,6 +3,7 @@ mod localize; +use cosmic::iced::{Alignment, Length}; use cosmic::{ app, app::Core, @@ -13,14 +14,16 @@ use cosmic::{ iced::{ Rectangle, Task, platform_specific::shell::commands::popup::{destroy_popup, get_popup}, + widget::{column, row}, window::Id, }, - iced::core::window, + iced::{core::window, runtime::Appearance}, prelude::*, surface, theme, widget::{ self, autosize, rectangle_tracker::{RectangleTracker, RectangleUpdate, rectangle_tracker_subscription}, + space, }, }; use cosmic_comp_config::CosmicCompConfig; diff --git a/cosmic-applet-minimize/Cargo.toml b/cosmic-applet-minimize/Cargo.toml index 2ed9a552..783be11b 100644 --- a/cosmic-applet-minimize/Cargo.toml +++ b/cosmic-applet-minimize/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cosmic-applet-minimize" -version = "1.3.0" +version = "1.0.2" edition = "2024" license = "GPL-3.0-only" @@ -9,7 +9,7 @@ anyhow.workspace = true i18n-embed-fl.workspace = true i18n-embed.workspace = true image = { version = "0.25.9", default-features = false } -cosmic.workspace = true +libcosmic.workspace = true memmap2 = "0.9.10" rust-embed.workspace = true rustix.workspace = true diff --git a/cosmic-applet-minimize/src/lib.rs b/cosmic-applet-minimize/src/lib.rs index 307762b5..e40a7551 100644 --- a/cosmic-applet-minimize/src/lib.rs +++ b/cosmic-applet-minimize/src/lib.rs @@ -316,7 +316,7 @@ impl cosmic::Application for Minimize { } else { (cross_padding, major_padding) }; - let _theme = self.core.system_theme().cosmic(); + let theme = self.core.system_theme().cosmic(); let icon_buttons = self.apps[..max_icon_count].iter().map(|app| { self.core .applet @@ -420,7 +420,7 @@ impl cosmic::Application for Minimize { (cross_padding, major_padding) }; let theme = self.core.system_theme().cosmic(); - let _space_xxs = theme.space_xxs(); + let space_xxs = theme.space_xxs(); let icon_buttons = self.apps[max_icon_count..].iter().map(|app| { tooltip( Element::from(crate::window_image::WindowImage::new( diff --git a/cosmic-applet-minimize/src/wayland_handler.rs b/cosmic-applet-minimize/src/wayland_handler.rs index 562693e2..bf2eae30 100644 --- a/cosmic-applet-minimize/src/wayland_handler.rs +++ b/cosmic-applet-minimize/src/wayland_handler.rs @@ -121,7 +121,6 @@ struct AppData { toplevel_info_state: ToplevelInfoState, toplevel_manager_state: ToplevelManagerState, seat_state: SeatState, - captured_toplevels: std::collections::HashSet, } struct CaptureData { @@ -352,36 +351,6 @@ impl AppData { } }); } - - fn handle_toplevel(&mut self, toplevel: &ExtForeignToplevelHandleV1, is_new: bool) { - let Some(info) = self.toplevel_info_state.info(toplevel) else { - return; - }; - if info - .state - .contains(&zcosmic_toplevel_handle_v1::State::Minimized) - { - // Capture a thumbnail once, on the transition into minimized. - if self.captured_toplevels.insert(toplevel.clone()) { - self.send_image(toplevel.clone()); - } - let update = if is_new { - ToplevelUpdate::Add(info.clone()) - } else { - ToplevelUpdate::Update(info.clone()) - }; - let _ = futures::executor::block_on(self.tx.send(WaylandUpdate::Toplevel(update))); - } else { - self.remove_toplevel(toplevel); - } - } - - fn remove_toplevel(&mut self, toplevel: &ExtForeignToplevelHandleV1) { - self.captured_toplevels.remove(toplevel); - let _ = futures::executor::block_on(self.tx.send(WaylandUpdate::Toplevel( - ToplevelUpdate::Remove(toplevel.clone()), - ))); - } } impl ToplevelInfoHandler for AppData { @@ -395,7 +364,23 @@ impl ToplevelInfoHandler for AppData { _qh: &QueueHandle, toplevel: &ExtForeignToplevelHandleV1, ) { - self.handle_toplevel(toplevel, true); + if let Some(info) = self.toplevel_info_state.info(toplevel) { + if info + .state + .contains(&zcosmic_toplevel_handle_v1::State::Minimized) + { + // spawn thread for sending the image + self.send_image(toplevel.clone()); + let _ = futures::executor::block_on( + self.tx + .send(WaylandUpdate::Toplevel(ToplevelUpdate::Add(info.clone()))), + ); + } else { + let _ = futures::executor::block_on(self.tx.send(WaylandUpdate::Toplevel( + ToplevelUpdate::Remove(toplevel.clone()), + ))); + } + } } fn update_toplevel( @@ -404,7 +389,21 @@ impl ToplevelInfoHandler for AppData { _qh: &QueueHandle, toplevel: &ExtForeignToplevelHandleV1, ) { - self.handle_toplevel(toplevel, false); + if let Some(info) = self.toplevel_info_state.info(toplevel) { + if info + .state + .contains(&zcosmic_toplevel_handle_v1::State::Minimized) + { + self.send_image(toplevel.clone()); + let _ = futures::executor::block_on(self.tx.send(WaylandUpdate::Toplevel( + ToplevelUpdate::Update(info.clone()), + ))); + } else { + let _ = futures::executor::block_on(self.tx.send(WaylandUpdate::Toplevel( + ToplevelUpdate::Remove(toplevel.clone()), + ))); + } + } } fn toplevel_closed( @@ -413,7 +412,9 @@ impl ToplevelInfoHandler for AppData { _qh: &QueueHandle, toplevel: &ExtForeignToplevelHandleV1, ) { - self.remove_toplevel(toplevel); + let _ = futures::executor::block_on(self.tx.send(WaylandUpdate::Toplevel( + ToplevelUpdate::Remove(toplevel.clone()), + ))); } } @@ -481,7 +482,6 @@ pub(crate) fn wayland_handler( toplevel_manager_state: ToplevelManagerState::new(®istry_state, &qh), queue_handle: qh, registry_state, - captured_toplevels: std::collections::HashSet::new(), }; loop { diff --git a/cosmic-applet-network/Cargo.toml b/cosmic-applet-network/Cargo.toml index a41dc80f..659dd076 100644 --- a/cosmic-applet-network/Cargo.toml +++ b/cosmic-applet-network/Cargo.toml @@ -1,16 +1,18 @@ [package] name = "cosmic-applet-network" -version = "1.3.0" +version = "1.0.2" edition = "2024" license = "GPL-3.0-or-later" [dependencies] +anyhow.workspace = true async-fn-stream = "0.3" +cosmic-dbus-networkmanager = { git = "https://github.com/pop-os/dbus-settings-bindings" } futures.workspace = true futures-util.workspace = true i18n-embed-fl.workspace = true i18n-embed.workspace = true -cosmic = { workspace = true, features = [ +libcosmic = { workspace = true, features = [ "applet", "applet-token", "tokio", @@ -23,7 +25,17 @@ tokio.workspace = true tracing-log.workspace = true tracing-subscriber.workspace = true tracing.workspace = true +zbus.workspace = true +nm-secret-agent-manager = { git = "https://github.com/pop-os/dbus-settings-bindings/" } indexmap = "2.13.0" secure-string = "0.3.0" uuid = { version = "1.21.0", features = ["v4"] } -nmrs = "3.4.0" + + +[dependencies.cosmic-settings-network-manager-subscription] +git = "https://github.com/pop-os/cosmic-settings/" +# path = "../../cosmic-settings/subscriptions/network-manager" + +[dependencies.cosmic-settings-airplane-mode-subscription] +git = "https://github.com/pop-os/cosmic-settings/" +# path = "../../cosmic-settings/subscriptions/airplane-mode" diff --git a/cosmic-applet-network/data/dbus-1/system.d/com.system76.CosmicSettings.Applet.NetworkManager.SecretAgent.conf b/cosmic-applet-network/data/dbus-1/system.d/com.system76.CosmicSettings.Applet.NetworkManager.SecretAgent.conf deleted file mode 100644 index 8e98cedb..00000000 --- a/cosmic-applet-network/data/dbus-1/system.d/com.system76.CosmicSettings.Applet.NetworkManager.SecretAgent.conf +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - diff --git a/cosmic-applet-network/i18n/ca/cosmic_applet_network.ftl b/cosmic-applet-network/i18n/ca/cosmic_applet_network.ftl index ae3bd5dd..e69de29b 100644 --- a/cosmic-applet-network/i18n/ca/cosmic_applet_network.ftl +++ b/cosmic-applet-network/i18n/ca/cosmic_applet_network.ftl @@ -1,4 +0,0 @@ -cancel = Cancel·lar -connected = Connectat -wifi = Wi-Fi -identity = Identitat diff --git a/cosmic-applet-network/i18n/el/cosmic_applet_network.ftl b/cosmic-applet-network/i18n/el/cosmic_applet_network.ftl index 8eac3a7a..c7ed802d 100644 --- a/cosmic-applet-network/i18n/el/cosmic_applet_network.ftl +++ b/cosmic-applet-network/i18n/el/cosmic_applet_network.ftl @@ -1,24 +1 @@ cancel = Ακύρωση -connect = Σύνδεση -network = Δίκτυο -check-wifi-connection = Βεβαιωθείτε ότι το Wi-Fi είναι συνδεδεμένο στο διαδίκτυο και ότι ο κωδικός πρόσβασης είναι σωστός -reset = Επαναφορά -visible-wireless-networks = Ορατά ασύρματα δίκτυα -enter-password = Εισαγάγετε τον κωδικό πρόσβασης ή το κλειδί κρυπτογράφησης -ipv6 = Διεύθυνση IPv6 -airplane-mode-on = Η λειτουργία πτήσης είναι ενεργή -connecting = Σύνδεση -airplane-mode = Λειτουργία πτήσης -wifi = Wi-Fi -ipv4 = Διεύθυνση IPv4 -identity = Ταυτότητα -unable-to-connect = Δεν είναι δυνατή η σύνδεση στο δίκτυο -turn-off-airplane-mode = Απενεργοποιήστε τη για ενεργοποίηση του Wi-Fi, του Bluetooth και της κινητής ευρυζωνικής σύνδεσης. -connected = Συνδέθηκε -vpn-connections = Συνδέσεις VPN -mac = MAC -router-wps-button = Μπορείτε επίσης να συνδεθείτε πατώντας το κουμπί «WPS» του δρομολογητή -settings = Ρυθμίσεις δικτύου... -megabits-per-second = Mbps -gigabits-per-second = Gbps -terabits-per-second = Tbps diff --git a/cosmic-applet-network/i18n/hu/cosmic_applet_network.ftl b/cosmic-applet-network/i18n/hu/cosmic_applet_network.ftl index 4ad41d3f..e9a61812 100644 --- a/cosmic-applet-network/i18n/hu/cosmic_applet_network.ftl +++ b/cosmic-applet-network/i18n/hu/cosmic_applet_network.ftl @@ -1,16 +1,16 @@ network = Hálózat airplane-mode = Repülőgép-üzemmód airplane-mode-on = A repülőgép-üzemmód be van kapcsolva -turn-off-airplane-mode = Kapcsold ki a Wi-Fi, Bluetooth és mobil széles sáv engedélyezéséhez. +turn-off-airplane-mode = Kapcsold ki a Wi-Fi, Bluetooth és mobil szélessáv engedélyezéséhez. wifi = Wi-Fi identity = Azonosító ipv4 = IPv4-cím ipv6 = IPv6-cím mac = MAC megabits-per-second = Mbps -connected = Kapcsolódva -connecting = Kapcsolódás -connect = Kapcsolódás +connected = Csatlakoztatva +connecting = Csatlakozás… +connect = Csatlakozás cancel = Mégse settings = Hálózati beállítások… visible-wireless-networks = Látható vezeték nélküli hálózatok diff --git a/cosmic-applet-network/i18n/kmr/cosmic_applet_network.ftl b/cosmic-applet-network/i18n/kmr/cosmic_applet_network.ftl index 3dbb0266..e69de29b 100644 --- a/cosmic-applet-network/i18n/kmr/cosmic_applet_network.ftl +++ b/cosmic-applet-network/i18n/kmr/cosmic_applet_network.ftl @@ -1,24 +0,0 @@ -cancel = Têk bibe -connect = Girê bide -connected = Hat girêdan -network = Tor -airplane-mode = Awaya balafirê -airplane-mode-on = Awaya balafirê vekirî ye -turn-off-airplane-mode = Ji bo çalakirina Wi-Fi, Bluetooth û torên mobayîl, awaya balafirê bigire. -wifi = Wi-Fi -identity = Nasname -ipv4 = IPv4 -ipv6 = IPv6 -mac = MAC -megabits-per-second = Mbps -gigabits-per-second = Gbps -terabits-per-second = Tbps -connecting = Tê girêdan -settings = Sazkariyên torê... -visible-wireless-networks = Torên bêtêl ên xuya -vpn-connections = Girêdanên VPN -enter-password = Borînpeyvê yan jî kilîtê şîfrekirinê têxîne -router-wps-button = Tu dikarî bi pêlkirina bişkoka "WPS" ya li ser routerê jî girê bidî -unable-to-connect = Nikare bi torê ve were girêdan -check-wifi-connection = Piştrast bike ku Wi-Fi bi înternetê ve girêdayî ye û borînpeyv rast e -reset = Ji nû ve saz bike diff --git a/cosmic-applet-network/i18n/lo/cosmic_applet_network.ftl b/cosmic-applet-network/i18n/lo/cosmic_applet_network.ftl deleted file mode 100644 index e69de29b..00000000 diff --git a/cosmic-applet-network/i18n/sr/cosmic_applet_network.ftl b/cosmic-applet-network/i18n/sr/cosmic_applet_network.ftl index b79b0ddd..e69de29b 100644 --- a/cosmic-applet-network/i18n/sr/cosmic_applet_network.ftl +++ b/cosmic-applet-network/i18n/sr/cosmic_applet_network.ftl @@ -1,24 +0,0 @@ -cancel = Откажи -connected = Повезано -connect = Повежи -identity = Идентитет -wifi = Бежична -check-wifi-connection = Уверите се да је бежична мрежа повезана са интернетом и да је лозинка тачна -reset = Врати -visible-wireless-networks = Видљиве бежичне мреже -enter-password = Унесите лозинку или кључ за шифровање -ipv6 = ИПв6 адреса -airplane-mode-on = Авионски режим је укључен -connecting = Повезује се -airplane-mode = Авио режим -ipv4 = ИПв4 адреса -unable-to-connect = Немогуће повезати се са мрежом -turn-off-airplane-mode = Искључите да бисте омогућили Блутут, бежичну и мобилни интернет. -network = Мрежа -vpn-connections = ВПН везе -mac = МАК -router-wps-button = Такође се можете повезати притиском на дугме „WPS“ на рутеру -settings = Подешавања мреже... -megabits-per-second = Mbps -gigabits-per-second = Gbps -terabits-per-second = Tbps diff --git a/cosmic-applet-network/i18n/uk/cosmic_applet_network.ftl b/cosmic-applet-network/i18n/uk/cosmic_applet_network.ftl index cebc8087..687539ca 100644 --- a/cosmic-applet-network/i18n/uk/cosmic_applet_network.ftl +++ b/cosmic-applet-network/i18n/uk/cosmic_applet_network.ftl @@ -8,15 +8,15 @@ ipv6 = IPv6-адреса mac = MAC megabits-per-second = Мбіт/с connected = З'єднано -connecting = З’єднання -connect = З’єднати +connecting = Підключення +connect = З'єднати cancel = Скасувати settings = Налаштування мережі... visible-wireless-networks = Видимі бездротові мережі enter-password = Введіть пароль або ключ шифрування -router-wps-button = Також можна під’єднатися, натиснувши кнопку «WPS» на маршрутизаторі -unable-to-connect = Не вдалося під’єднатися до мережі -check-wifi-connection = Переконайтеся, що Wi-Fi під’єднано до Інтернету, а пароль — правильний +router-wps-button = Також можна підключитися, натиснувши кнопку «WPS» на маршрутизаторі +unable-to-connect = Не вдалося підключитися до мережі +check-wifi-connection = Переконайтеся, що Wi-Fi підключено до Інтернету, а пароль — правильний reset = Скинути identity = Ідентичність vpn-connections = VPN-з'єднання diff --git a/cosmic-applet-network/src/app.rs b/cosmic-applet-network/src/app.rs index b5f12bfa..727d9b10 100644 --- a/cosmic-applet-network/src/app.rs +++ b/cosmic-applet-network/src/app.rs @@ -1,21 +1,25 @@ -use indexmap::IndexMap; -use nmrs::{ - ActiveConnection, ActiveConnectionState, ConnectType, ConnectivityState, EapOptions, - NetworkEvent, NetworkManager as NmrsManager, NetworkSnapshot, WifiSecurity, - agent::{SecretAgent, SecretAgentCapabilities, SecretRequest, SecretResponder, SecretSetting}, +use anyhow::Context; +use cosmic_dbus_networkmanager::settings::{NetworkManagerSettings, connection::Settings}; +use cosmic_settings_network_manager_subscription::{ + self as network_manager, NetworkManagerState, UUID, + active_conns::active_conns_subscription, + available_wifi::{AccessPoint, NetworkType}, + current_networks::ActiveConnectionInfo, + hw_address::HwAddress, + nm_secret_agent::{self, PasswordFlag, SecretSender}, }; +use indexmap::IndexMap; use rustc_hash::FxHashSet; use secure_string::SecureString; use std::{ borrow::Cow, - collections::{BTreeMap, HashMap}, - fmt, - str::FromStr, + collections::{BTreeMap, BTreeSet}, sync::{Arc, LazyLock}, + time::Duration, }; use cosmic::{ - Element, Task, app, + Apply, Element, Task, app, applet::{ menu_button, menu_control_padding, padded_control, token::subscription::{TokenRequest, TokenUpdate, activation_token_subscription}, @@ -25,22 +29,23 @@ use cosmic::{ iced::core::window, iced::{ Alignment, Length, Subscription, - platform_specific::shell::wayland::commands::popup::destroy_popup, + platform_specific::shell::wayland::commands::popup::{destroy_popup, get_popup}, }, surface, theme, widget::{ - Id, button, column, container, divider, + Column, Id, Row, button, column, container, divider, icon::{self, from_name}, - indeterminate_circular, row, scrollable, secure_input, text, text_input, toggler, + row, scrollable, secure_input, text, text_input, toggler, }, }; - -use futures::{ - StreamExt, - channel::mpsc::{UnboundedReceiver, UnboundedSender, unbounded}, - lock::Mutex as AsyncMutex, +use cosmic_dbus_networkmanager::interface::{ + access_point, + enums::{ActiveConnectionState, DeviceState, NmConnectivityState, NmState}, }; +use futures::{StreamExt, channel::mpsc::TrySendError}; +use zbus::{Connection, zvariant::ObjectPath}; + use crate::{config, fl}; pub fn run() -> cosmic::iced::Result { @@ -60,6 +65,25 @@ enum NewConnectionState { Failure(AccessPoint), } +impl NewConnectionState { + pub fn ssid(&self) -> &str { + &match self { + Self::EnterPassword { access_point, .. } => access_point, + Self::Waiting(ap) => ap, + Self::Failure(ap) => ap, + } + .ssid + } + pub fn hw_address(&self) -> HwAddress { + match self { + Self::EnterPassword { access_point, .. } => access_point, + Self::Waiting(ap) => ap, + Self::Failure(ap) => ap, + } + .hw_address + } +} + impl From for AccessPoint { fn from(connection_state: NewConnectionState) -> Self { match connection_state { @@ -72,246 +96,56 @@ impl From for AccessPoint { pub static SECURE_INPUT_WIFI: LazyLock = LazyLock::new(Id::unique); -type Uuid = Arc; -type Ssid = Arc; - -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum NetworkType { - Open, - Password, - Eap, -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum DeviceState { - Unknown, - Unmanaged, - Unavailable, - Disconnected, - NeedAuth, - Activated, - Failed, - Other, -} - -impl From<&nmrs::DeviceState> for DeviceState { - fn from(state: &nmrs::DeviceState) -> Self { - match state { - nmrs::DeviceState::Unmanaged => Self::Unmanaged, - nmrs::DeviceState::Unavailable => Self::Unavailable, - nmrs::DeviceState::Disconnected => Self::Disconnected, - nmrs::DeviceState::NeedAuth => Self::NeedAuth, - nmrs::DeviceState::Activated => Self::Activated, - nmrs::DeviceState::Failed => Self::Failed, - nmrs::DeviceState::Other(_) => Self::Unknown, - _ => Self::Other, - } - } -} - -#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub struct HwAddress([u8; 6]); - -impl HwAddress { - fn as_string(self) -> String { - self.0 - .iter() - .map(|byte| format!("{byte:02X}")) - .collect::>() - .join(":") - } -} - -impl FromStr for HwAddress { - type Err = (); - - fn from_str(value: &str) -> Result { - let mut bytes = [0; 6]; - let mut parts = value.split(':'); - for byte in &mut bytes { - let Some(part) = parts.next() else { - return Err(()); - }; - *byte = u8::from_str_radix(part, 16).map_err(|_| ())?; - } - if parts.next().is_some() { - return Err(()); - } - Ok(Self(bytes)) - } -} - -impl fmt::Display for HwAddress { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str(&self.as_string()) - } -} - -fn same_access_point(left: &AccessPoint, right: &AccessPoint) -> bool { - left.ssid == right.ssid - && left.hw_address == right.hw_address - && left.interface == right.interface -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct AccessPoint { - pub ssid: Ssid, - pub network_type: NetworkType, - pub hw_address: HwAddress, - pub strength: u8, - pub state: DeviceState, - pub working: bool, - pub wps_push: bool, - pub interface: Option, -} - -#[derive(Debug, Clone)] -pub enum ActiveConnectionInfo { - Wired { - name: String, - hw_address: String, - speed: u32, - ip4_address: Option, - ip6_address: Option, - }, - WiFi { - name: String, - ip4_address: Option, - ip6_address: Option, - state: ActiveConnectionState, - strength: u8, - hw_address: String, - }, - Vpn { - name: String, - ip4_address: Option, - ip6_address: Option, - }, -} - -impl ActiveConnectionInfo { - fn name(&self) -> &str { - match self { - Self::Wired { name, .. } | Self::WiFi { name, .. } | Self::Vpn { name, .. } => name, - } - } -} - -#[derive(Debug, Clone)] -pub struct NetworkManagerState { - pub wifi_enabled: bool, - pub airplane_mode: bool, - pub connectivity: ConnectivityState, - pub active_conns: Vec, - pub known_access_points: Vec, - pub wireless_access_points: Vec, -} - -impl Default for NetworkManagerState { - fn default() -> Self { - Self { - wifi_enabled: true, - airplane_mode: false, - connectivity: ConnectivityState::Unknown, - active_conns: Vec::new(), - known_access_points: Vec::new(), - wireless_access_points: Vec::new(), - } - } -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum DeviceType { - Wifi, -} - -#[derive(Clone, Debug)] -pub struct DeviceConnection { - pub id: String, -} - -#[derive(Clone, Debug)] -pub struct DeviceInfo { - pub interface: String, - pub device_type: DeviceType, - pub active_connection: Option<(DeviceConnection,)>, - pub known_connections: Vec, -} - -#[derive(Debug, Clone)] -pub struct AppletSnapshot { - pub state: NetworkManagerState, - pub devices: Vec, - pub known_vpns: IndexMap, - pub ssid_to_uuid: BTreeMap, Box>, - pub captive_portal_url: Option, -} - #[derive(Default, Debug, Clone)] pub struct MyNetworkState { - pub known_vpns: IndexMap, + pub known_vpns: IndexMap, pub ssid_to_uuid: BTreeMap, Box>, - pub devices: Vec>, + pub devices: Vec>, + pub password: Option, + pub connecting: BTreeSet, pub nm_state: NetworkManagerState, pub requested_vpn: Option, - pub pending_vpn: Option, } -/// Shared, take-once handle to an `nmrs` [`SecretResponder`]. Cloned freely -/// across `Message` boundaries; the first consumer to `lock().take()` it owns -/// the reply to NetworkManager. -pub type SecretResponderHandle = Arc>>; - #[derive(Debug, Clone)] pub struct RequestedVpn { + name: String, uuid: Arc, description: Option, password: SecureString, password_hidden: bool, - responder: SecretResponderHandle, - /// VPN secret keys NM hinted as needed (e.g. `["password"]`). When empty, - /// `"password"` is used as a fallback. - secret_keys: Vec, -} - -#[derive(Debug, Clone)] -pub struct PendingVpn { - uuid: Arc, - action: PendingVpnAction, -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum PendingVpnAction { - Activate, - Deactivate, + tx: SecretSender, } #[derive(Clone, Debug)] pub enum ConnectionSettings { - Vpn { id: String }, + Vpn(VpnConnectionSettings), Wireguard { id: String }, } -/// Local mirror of the secret-agent events the applet cares about. Sourced -/// from `nmrs::agent` instead of the previous `nm_secret_agent` subscription. -#[derive(Debug, Clone)] -pub enum NmAgentEvent { - RequestSecret { - connection_uuid: String, - connection_id: String, - setting: AgentSetting, - responder: SecretResponderHandle, - }, - CancelGetSecrets, - Failed(String), +#[derive(Clone, Debug, Default)] +pub struct VpnConnectionSettings { + id: String, + username: Option, + connection_type: Option, + password_flag: Option, } -#[derive(Debug, Clone)] -pub enum AgentSetting { - WifiPsk { ssid: String }, - WifiEap, - Vpn { secret_keys: Vec }, - Other, +#[derive(Clone, Debug, Eq, PartialEq)] +enum ConnectionType { + Password, +} + +impl VpnConnectionSettings { + fn password_flag(&self) -> Option { + self.connection_type + .as_ref() + .is_some_and(|ct| match ct { + ConnectionType::Password => true, + }) + .then_some(self.password_flag) + .flatten() + } } #[derive(Default)] @@ -321,19 +155,22 @@ struct CosmicNetworkApplet { popup: Option, // NM state + nm_sender: Option>, + nm_task: Option>, + secret_tx: Option>, nm_state: MyNetworkState, // UI state show_visible_networks: bool, show_available_vpns: bool, new_connection: Option, + conn: Option, toggle_wifi_ctr: u128, token_tx: Option>, - secret_agent_reregister_tx: Option>, failed_known_ssids: FxHashSet>, /// When defined, displays connections for the specific device. - active_device: Option>, + active_device: Option>, } fn wifi_icon(strength: u8) -> &'static str { @@ -427,19 +264,13 @@ fn vpn_section<'a>( if show_available_vpns { for (uuid, connection) in &nm_state.known_vpns { let id = match connection { - ConnectionSettings::Vpn { id } | ConnectionSettings::Wireguard { id } => { - id.as_str() - } + ConnectionSettings::Vpn(connection) => connection.id.as_str(), + ConnectionSettings::Wireguard { id } => id.as_str(), }; // Check if this VPN is currently active let is_active = nm_state.nm_state.active_conns.iter().any( |conn| matches!(conn, ActiveConnectionInfo::Vpn { name, .. } if name == id), ); - let pending_action = nm_state - .pending_vpn - .as_ref() - .filter(|pending| pending.uuid.as_ref() == uuid.as_ref()) - .map(|pending| pending.action); let mut btn_content = vec![ icon::from_name("network-vpn-symbolic") @@ -452,9 +283,6 @@ fn vpn_section<'a>( if is_active { btn_content.push(text::body(fl!("connected")).align_x(Alignment::End).into()); } - if pending_action.is_some() { - btn_content.push(indeterminate_circular().size(24.0).into()); - } let mut btn = menu_button( row::with_children(btn_content) @@ -462,9 +290,7 @@ fn vpn_section<'a>( .spacing(8), ); - btn = if pending_action.is_some() { - btn - } else if is_active { + btn = if is_active { btn.on_press(Message::DeactivateVpn(uuid.clone())) } else { btn.on_press(Message::ActivateVpn(uuid.clone())) @@ -478,341 +304,7 @@ fn vpn_section<'a>( vpn_col } -fn ip_address_elements<'a>( - ip4_address: &Option, - _ip6_address: &Option, -) -> Vec> { - let mut elements = Vec::with_capacity(1); - if let Some(addr) = ip4_address { - // Strip the subnet mask (e.g. "10.0.0.2/24" -> "10.0.0.2"); the drop-down - // only needs the address itself for glancing at the host on a local network. - let addr = addr.split('/').next().unwrap_or(addr.as_str()); - elements.push(text(format!("{}: {}", fl!("ipv4"), addr)).size(12).into()); - } - elements -} - -fn network_type(security: nmrs::SecurityFeatures) -> NetworkType { - match security.preferred_connect_type() { - ConnectType::Open | ConnectType::Owe => NetworkType::Open, - ConnectType::Eap => NetworkType::Eap, - ConnectType::Psk | ConnectType::Sae => NetworkType::Password, - _ => NetworkType::Password, - } -} - -fn wifi_security( - access_point: &AccessPoint, - identity: Option, - password: Option, -) -> WifiSecurity { - match access_point.network_type { - NetworkType::Open => WifiSecurity::Open, - NetworkType::Eap => WifiSecurity::WpaEap { - opts: EapOptions::new( - identity.unwrap_or_default(), - password - .as_ref() - .map(|password| password.unsecure().to_owned()) - .unwrap_or_default(), - ), - }, - NetworkType::Password => WifiSecurity::WpaPsk { - psk: password - .as_ref() - .map(|password| password.unsecure().to_owned()) - .unwrap_or_default(), - }, - } -} - -fn connect_access_point_task( - access_point: AccessPoint, - identity: Option, - password: Option, -) -> Task> { - cosmic::task::future(async move { - let ssid = access_point.ssid.to_string(); - let interface = access_point.interface.clone(); - let bssid = access_point.hw_address.as_string(); - let security = wifi_security(&access_point, identity, password); - match NmrsManager::new().await { - Ok(nm) => { - match nm - .connect_to_bssid(&ssid, Some(&bssid), interface.as_deref(), security) - .await - { - Ok(()) => Message::ConnectionAttemptFinished { - access_point, - success: true, - error: None, - }, - Err(e) => Message::ConnectionAttemptFinished { - access_point, - success: false, - error: Some(e.to_string()), - }, - } - } - Err(e) => Message::Error(format!("nmrs init: {e}")), - } - }) - .map(cosmic::Action::App) -} - -fn snapshot_task() -> Task { - cosmic::task::future(async move { - let nm = match NmrsManager::new().await { - Ok(nm) => nm, - Err(e) => return Message::Error(format!("nmrs init: {e}")), - }; - - match nm.snapshot().await { - Ok(snapshot) => Message::Snapshot(snapshot_to_applet(snapshot)), - Err(e) => Message::Error(format!("snapshot: {e}")), - } - }) -} - -fn network_events_task() -> Task { - cosmic::Task::stream(async_fn_stream::fn_stream(|emitter| async move { - let nm = match NmrsManager::new().await { - Ok(nm) => nm, - Err(e) => { - let _ = emitter - .emit(Message::Error(format!("nmrs init: {e}"))) - .await; - return; - } - }; - let mut events = match nm.network_events().await { - Ok(events) => events, - Err(e) => { - let _ = emitter - .emit(Message::Error(format!("network events: {e}"))) - .await; - return; - } - }; - - while let Some(event) = events.next().await { - match event { - Ok(event) => { - let _ = emitter.emit(Message::NetworkEvent(event)).await; - } - Err(e) => { - let _ = emitter - .emit(Message::Error(format!("network event: {e}"))) - .await; - } - } - } - })) -} - -fn snapshot_to_applet(snapshot: NetworkSnapshot) -> AppletSnapshot { - let summary = snapshot.applet_summary(); - let mut known_vpns = IndexMap::new(); - for vpn in summary.saved_vpns.values() { - let uuid: Uuid = Arc::from(vpn.uuid.as_str()); - let entry = match vpn.kind { - Some(nmrs::VpnKind::WireGuard) => ConnectionSettings::Wireguard { id: vpn.id.clone() }, - _ => ConnectionSettings::Vpn { id: vpn.id.clone() }, - }; - known_vpns.insert(uuid, entry); - } - - let mut ssid_to_uuid = BTreeMap::new(); - for (ssid, profiles) in &summary.known_wifi { - if let Some(profile) = profiles.first() { - ssid_to_uuid.insert( - ssid.clone().into_boxed_str(), - profile.uuid.clone().into_boxed_str(), - ); - } - } - - let active_conns = snapshot - .active_connections - .iter() - .filter_map(|conn| match conn { - ActiveConnection::Wired(wired) => Some(ActiveConnectionInfo::Wired { - name: wired.id.clone(), - hw_address: wired.hw_address.clone().unwrap_or_default(), - speed: wired.speed_mbps.unwrap_or_default(), - ip4_address: wired.ip4_address.clone(), - ip6_address: wired.ip6_address.clone(), - }), - ActiveConnection::Wifi(wifi) - if known_vpns.values().any(|connection| { - matches!( - connection, - ConnectionSettings::Vpn { id } | ConnectionSettings::Wireguard { id } - if id == &wifi.ssid - ) - }) => - { - Some(ActiveConnectionInfo::Vpn { - name: wifi.ssid.clone(), - ip4_address: wifi.ip4_address.clone(), - ip6_address: wifi.ip6_address.clone(), - }) - } - ActiveConnection::Wifi(wifi) => Some(ActiveConnectionInfo::WiFi { - name: wifi.ssid.clone(), - ip4_address: wifi.ip4_address.clone(), - ip6_address: wifi.ip6_address.clone(), - state: wifi.state, - strength: wifi.strength.unwrap_or_default(), - hw_address: wifi.bssid.clone().unwrap_or_default(), - }), - ActiveConnection::Vpn(vpn) => Some(ActiveConnectionInfo::Vpn { - name: vpn.id.clone(), - ip4_address: vpn.ip4_address.clone(), - ip6_address: vpn.ip6_address.clone(), - }), - ActiveConnection::Other(_) | _ => None, - }) - .collect(); - - let mut wireless_access_points = summary - .wifi_groups - .iter() - .filter(|group| !group.ssid.is_empty() && !group.strongest.ssid_bytes.is_empty()) - .map(|group| { - let strongest = &group.strongest; - AccessPoint { - ssid: Arc::from(group.ssid.as_str()), - network_type: network_type(strongest.security), - hw_address: HwAddress::from_str(&strongest.bssid).unwrap_or_default(), - strength: strongest.strength, - state: DeviceState::from(&strongest.device_state), - working: false, - wps_push: strongest.security.wps, - interface: Some(group.interface.clone()), - } - }) - .collect::>(); - wireless_access_points.sort_by(|a, b| b.strength.cmp(&a.strength)); - - let known_access_points = summary - .wifi_groups - .iter() - .filter(|group| group.known || group.active) - .map(|group| { - let strongest = &group.strongest; - let device_state = DeviceState::from(&strongest.device_state); - AccessPoint { - ssid: Arc::from(group.ssid.as_str()), - network_type: network_type(strongest.security), - hw_address: HwAddress::from_str(&strongest.bssid).unwrap_or_default(), - strength: strongest.strength, - state: if group.active { - DeviceState::Activated - } else if matches!(device_state, DeviceState::Activated) { - DeviceState::Disconnected - } else { - device_state - }, - working: false, - wps_push: strongest.security.wps, - interface: Some(group.interface.clone()), - } - }) - .collect::>(); - - let devices = snapshot - .wifi_devices - .iter() - .map(|device| { - let known_connections = summary - .known_wifi - .iter() - .filter(|(ssid, _)| { - summary - .wifi_groups - .iter() - .any(|group| group.interface == device.interface && group.ssid == **ssid) - }) - .map(|(ssid, _)| DeviceConnection { id: ssid.clone() }) - .collect(); - DeviceInfo { - interface: device.interface.clone(), - device_type: DeviceType::Wifi, - active_connection: device - .active_ssid - .as_ref() - .map(|ssid| (DeviceConnection { id: ssid.clone() },)), - known_connections, - } - }) - .collect(); - - AppletSnapshot { - state: NetworkManagerState { - wifi_enabled: snapshot.wifi.enabled, - airplane_mode: summary.airplane_mode.is_airplane_mode(), - connectivity: summary.connectivity.state, - active_conns, - known_access_points, - wireless_access_points, - }, - devices, - known_vpns, - ssid_to_uuid, - captive_portal_url: summary.connectivity.captive_portal_url.clone(), - } -} - impl CosmicNetworkApplet { - fn apply_snapshot(&mut self, snapshot: AppletSnapshot) { - let previous_connectivity = self.nm_state.nm_state.connectivity; - self.nm_state.devices = snapshot.devices.into_iter().map(Arc::new).collect(); - self.nm_state.known_vpns = snapshot.known_vpns; - self.nm_state.ssid_to_uuid = snapshot.ssid_to_uuid; - self.update_nm_state(snapshot.state); - self.clear_completed_pending_vpn(); - - if !previous_connectivity.is_captive() && self.nm_state.nm_state.connectivity.is_captive() { - let mut browser = std::process::Command::new("xdg-open"); - browser.arg( - snapshot - .captive_portal_url - .as_deref() - .unwrap_or("http://204.pop-os.org/"), - ); - tokio::spawn(cosmic::process::spawn(browser)); - } - } - - fn clear_completed_pending_vpn(&mut self) { - let Some(pending) = self.nm_state.pending_vpn.as_ref() else { - return; - }; - let Some(connection) = self.nm_state.known_vpns.get(&pending.uuid) else { - self.nm_state.pending_vpn = None; - self.update_icon_name(); - return; - }; - let id = match connection { - ConnectionSettings::Vpn { id } | ConnectionSettings::Wireguard { id } => id, - }; - let is_active = self - .nm_state - .nm_state - .active_conns - .iter() - .any(|conn| matches!(conn, ActiveConnectionInfo::Vpn { name, .. } if name == id)); - let completed = match pending.action { - PendingVpnAction::Activate => is_active, - PendingVpnAction::Deactivate => !is_active, - }; - if completed { - self.nm_state.pending_vpn = None; - self.update_icon_name(); - } - } - fn update_nm_state(&mut self, mut new_state: NetworkManagerState) { self.update_togglers(&new_state); // check for failed conns that can be reset @@ -822,7 +314,7 @@ impl CosmicNetworkApplet { }; if matches!(state, ActiveConnectionState::Activated) { - self.failed_known_ssids.remove(new_s.name()); + self.failed_known_ssids.remove(new_s.name().as_str()); continue; } if matches!( @@ -849,22 +341,6 @@ impl CosmicNetworkApplet { } fn update_icon_name(&mut self) { - if self - .nm_state - .pending_vpn - .as_ref() - .is_some_and(|pending| pending.action == PendingVpnAction::Activate) - || self - .nm_state - .nm_state - .active_conns - .iter() - .any(|conn| matches!(conn, ActiveConnectionInfo::Vpn { .. })) - { - self.icon_name = "network-vpn-symbolic".to_string(); - return; - } - self.icon_name = self .nm_state .nm_state @@ -877,7 +353,12 @@ impl CosmicNetworkApplet { "network-wired-disconnected-symbolic", ActiveConnectionInfo::WiFi { strength, .. }, ) => wifi_icon(*strength), - (_, ActiveConnectionInfo::Wired { .. }) => "network-wired-symbolic", + (_, ActiveConnectionInfo::Wired { .. }) + if icon_name != "network-vpn-symbolic" => + { + "network-wired-symbolic" + } + (_, ActiveConnectionInfo::Vpn { .. }) => "network-vpn-symbolic", _ => icon_name, }, ) @@ -885,12 +366,15 @@ impl CosmicNetworkApplet { } fn update_togglers(&mut self, state: &NetworkManagerState) { + let mut changed = false; if self.nm_state.nm_state.wifi_enabled != state.wifi_enabled { self.nm_state.nm_state.wifi_enabled = state.wifi_enabled; + changed = true; } if self.nm_state.nm_state.airplane_mode != state.airplane_mode { self.nm_state.nm_state.airplane_mode = state.airplane_mode; + changed = true; } } @@ -913,106 +397,55 @@ impl CosmicNetworkApplet { } fn connect_vpn(&mut self, uuid: Arc) -> Task> { - cosmic::task::future(async move { - let error = match NmrsManager::new().await { - Ok(nm) => nm - .connect_vpn_by_uuid(&uuid) - .await - .err() - .map(|e| format!("activate VPN {uuid}: {e}")), - Err(e) => Some(format!("nmrs init: {e}")), - }; - Message::VpnOperationFinished { - uuid, - action: PendingVpnAction::Activate, - error, - } - }) - .map(cosmic::Action::App) + if let Some((tx, conn)) = self.nm_sender.clone().zip(self.conn.clone()) { + cosmic::task::future(async move { + // Find the connection by UUID + if let Ok(nm_settings) = NetworkManagerSettings::new(&conn).await { + if let Ok(connections) = nm_settings.list_connections().await { + for connection in connections { + if let Ok(settings) = connection.get_settings().await { + let settings = Settings::new(settings); + if let Some(conn_settings) = &settings.connection { + if conn_settings.uuid.as_ref().is_some_and(|conn_uuid| { + conn_uuid.as_str() == uuid.as_ref() + }) { + let path = connection.inner().path().clone().to_owned(); + if let Err(err) = + tx.unbounded_send(network_manager::Request::Activate( + ObjectPath::try_from("/").unwrap(), + path, + )) + { + if err.is_disconnected() { + return zbus::Connection::system() + .await + .context( + "failed to create system dbus connection", + ) + .map_or_else( + |why| Message::Error(why.to_string()), + Message::NetworkManagerConnect, + ); + } + + tracing::error!("{err:?}"); + } + break; + } + } + } + } + } + } + Message::Refresh + }) + } else { + tracing::warn!("No sender available to activate VPN."); + Task::none() + } } } -/// Registers an `nmrs` secret agent on the system bus and yields its -/// requests + cancellations as [`NmAgentEvent`] for the applet to handle. -fn secret_agent_task( - identifier: String, - mut reregister_requests: UnboundedReceiver<()>, -) -> Task { - cosmic::Task::stream(async_fn_stream::fn_stream(move |emitter| async move { - let registration = SecretAgent::builder() - .with_identifier(identifier) - .with_object_path("/org/freedesktop/NetworkManager/SecretAgent") - .with_capabilities(SecretAgentCapabilities::VPN_HINTS) - .register() - .await; - - let (mut handle, mut requests) = match registration { - Ok(pair) => pair, - Err(e) => { - let _ = emitter.emit(NmAgentEvent::Failed(e.to_string())).await; - return; - } - }; - - loop { - tokio::select! { - req = requests.next() => match req { - Some(req) => { - let event = secret_request_to_event(req); - let _ = emitter.emit(event).await; - } - None => break, - }, - cancel = handle.cancellations().next() => match cancel { - Some(_reason) => { - let _ = emitter.emit(NmAgentEvent::CancelGetSecrets).await; - } - None => break, - }, - Some(()) = reregister_requests.next() => { - if let Err(e) = handle.reregister().await { - tracing::warn!("failed to re-register secret agent: {e}"); - } - }, - } - } - - if let Err(e) = handle.unregister().await { - tracing::warn!("failed to unregister secret agent: {e}"); - } - })) -} - -fn secret_request_to_event(req: SecretRequest) -> NmAgentEvent { - let setting = match req.setting { - SecretSetting::WifiPsk { ssid } => AgentSetting::WifiPsk { ssid }, - SecretSetting::WifiEap { .. } => AgentSetting::WifiEap, - SecretSetting::Vpn { .. } => AgentSetting::Vpn { - secret_keys: req.hints.clone(), - }, - _ => AgentSetting::Other, - }; - - NmAgentEvent::RequestSecret { - connection_uuid: req.connection_uuid, - connection_id: req.connection_id, - setting, - responder: Arc::new(AsyncMutex::new(Some(req.responder))), - } -} - -/// Reply with [`NoSecrets`](nmrs::agent::SecretResponder::no_secrets) to free -/// NetworkManager when the applet decides not to use the responder. Dropping -/// it would also auto-reply, but doing it explicitly keeps the log clean. -fn release_responder(responder: SecretResponderHandle) -> Task> { - cosmic::task::future(async move { - if let Some(r) = responder.lock().await.take() { - let _ = r.no_secrets().await; - } - Message::NoOp - }) -} - #[derive(Debug, Clone)] pub(crate) enum Message { TogglePopup, @@ -1027,39 +460,35 @@ pub(crate) enum Message { TogglePasswordVisibility, FocusSecureInput, NoOp, - #[allow(dead_code)] // required by `cosmic::applet` surface path; not always emitted Surface(surface::Action), ActivateVpn(Arc), // UUID of VPN to activate DeactivateVpn(Arc), // UUID of VPN to deactivate - VpnOperationFinished { - uuid: Arc, - action: PendingVpnAction, - error: Option, - }, - ToggleVpnList, // Show/hide available VPNs + ToggleVpnList, // Show/hide available VPNs /// An update from the secret agent - SecretAgent(NmAgentEvent), + SecretAgent(network_manager::nm_secret_agent::Event), /// Connect to a WiFi network access point. - Connect(Ssid, HwAddress), + Connect(network_manager::SSID, HwAddress), /// Connect with a password ConnectWithPassword, + KnownConnections(IndexMap), + /// Settings for known connections. + ConnectionSettings(BTreeMap, Box>), /// Disconnect from an access point. - Disconnect(Ssid, HwAddress), - ConnectionAttemptFinished { - access_point: AccessPoint, - success: bool, - error: Option, - }, + Disconnect(network_manager::SSID, HwAddress), /// An error occurred. Error(String), /// Identity update from the dialog IdentityUpdate(String), - /// An update from NetworkManager. - NetworkEvent(NetworkEvent), + /// An update from the network manager daemon + NetworkManager(network_manager::Event), + /// Successfully connected to the system dbus. + NetworkManagerConnect(zbus::Connection), /// Update the password from the dialog PasswordUpdate(SecureString), - /// Update applet state from NetworkManager. - Snapshot(AppletSnapshot), + /// Update NetworkManagerState + UpdateState(NetworkManagerState), + /// Update the devices lists + UpdateDevices(Vec), /// Toggle WiFi access WiFiEnable(bool), /// Refresh state @@ -1069,7 +498,258 @@ pub(crate) enum Message { VPNPasswordUpdate(SecureString), CancelVPNConnection, /// Selects a device to display connections from - SelectDevice(Option>), + SelectDevice(Option>), +} + +#[derive(Debug, Clone)] +struct Password { + ssid: network_manager::SSID, + hw_address: HwAddress, + identity: Option, + password: SecureString, + password_hidden: bool, + tx: SecretSender, +} + +fn connection_settings(conn: zbus::Connection) -> Task { + let settings = async move { + let settings = network_manager::dbus::settings::NetworkManagerSettings::new(&conn).await?; + + _ = settings.load_connections(&[]).await; + + let settings = settings + // Get a list of known connections. + .list_connections() + .await? + // Prepare for wrapping in a concurrent stream. + .into_iter() + .map(|conn| async move { conn }) + // Create a concurrent stream for each connection. + .apply(futures::stream::FuturesOrdered::from_iter) + // Concurrently fetch settings for each connection. + .filter_map(|conn| async move { + conn.get_settings() + .await + .map(network_manager::Settings::new) + .ok() + }) + // Reduce the settings list into a SSID->UUID map. + .fold(BTreeMap::new(), |mut set, settings| async move { + if let Some(ref wifi) = settings.wifi + && let Some(ssid) = wifi + .ssid + .clone() + .and_then(|ssid| String::from_utf8(ssid).ok()) + && let Some(ref connection) = settings.connection + && let Some(uuid) = connection.uuid.clone() + { + set.insert(ssid.into(), uuid.into()); + return set; + } + + set + }) + .await; + + Ok::<_, zbus::Error>(settings) + }; + + cosmic::task::future(async move { + settings + .await + .context("failed to get connection settings") + .map_or_else( + |why| Message::Error(why.to_string()), + Message::ConnectionSettings, + ) + }) +} + +pub fn update_state(conn: zbus::Connection) -> Task { + cosmic::task::future(async move { + match NetworkManagerState::new(&conn).await { + Ok(state) => Message::UpdateState(state), + Err(why) => Message::Error(why.to_string()), + } + }) +} + +pub fn update_devices(conn: zbus::Connection) -> Task { + cosmic::task::future(async move { + let filter = + |device_type| matches!(device_type, network_manager::devices::DeviceType::Wifi); + match network_manager::devices::list(&conn, filter).await { + Ok(devices) => Message::UpdateDevices(devices), + Err(why) => Message::Error(why.to_string()), + } + }) +} + +impl CosmicNetworkApplet { + fn connect(&mut self, conn: zbus::Connection) -> Task { + if self.nm_task.is_none() { + let popup = self.popup; + let (canceller, task) = crate::utils::forward_event_loop(move |emitter| async move { + let (tx, mut rx) = futures::channel::mpsc::channel(1); + + if popup.is_some() { + let watchers = std::pin::pin!(async move { + futures::join!( + network_manager::watch(conn.clone(), tx.clone()), + network_manager::active_conns::watch(conn.clone(), tx.clone(),), + network_manager::wireless_enabled::watch(conn.clone(), tx.clone()), + network_manager::watch_connections_changed(conn, tx,) + ); + }); + let forwarder = std::pin::pin!(async move { + while let Some(message) = rx.next().await { + _ = emitter.emit(Message::NetworkManager(message)).await; + } + }); + + futures::future::select(watchers, forwarder).await; + } else { + let watchers = std::pin::pin!(async move { + futures::join!( + network_manager::watch(conn.clone(), tx.clone()), + network_manager::active_conns::watch(conn.clone(), tx.clone(),), + network_manager::wireless_enabled::watch(conn.clone(), tx.clone()), + ); + }); + let forwarder = std::pin::pin!(async move { + while let Some(message) = rx.next().await { + _ = emitter.emit(Message::NetworkManager(message)).await; + } + }); + + futures::future::select(watchers, forwarder).await; + }; + }); + + self.nm_task = Some(canceller); + return task.map(Message::from); + } + + Task::none() + } +} + +fn load_vpns(conn: zbus::Connection) -> Task { + let settings = async move { + let settings = network_manager::dbus::settings::NetworkManagerSettings::new(&conn).await?; + + _ = settings.load_connections(&[]).await; + + let settings = settings + // Get a list of known connections. + .list_connections() + .await? + // Prepare for wrapping in a concurrent stream. + .into_iter() + .map(|conn| async move { conn }) + // Create a concurrent stream for each connection. + .apply(futures::stream::FuturesOrdered::from_iter) + // Concurrently fetch settings for each connection, and filter for VPN. + .filter_map(|conn| async move { + let settings = conn.get_settings().await.ok()?; + + let connection = settings.get("connection")?; + + match connection + .get("type")? + .downcast_ref::() + .ok()? + .as_str() + { + "vpn" => (), + + "wireguard" => { + let id = connection.get("id")?.downcast_ref::().ok()?; + let uuid = connection.get("uuid")?.downcast_ref::().ok()?; + return Some((Arc::from(uuid), ConnectionSettings::Wireguard { id })); + } + + _ => return None, + } + + let vpn = settings.get("vpn")?; + let id = connection.get("id")?.downcast_ref::().ok()?; + let uuid = connection.get("uuid")?.downcast_ref::().ok()?; + + let (connection_type, username, password_flag) = vpn + .get("data") + .and_then(|data| data.downcast_ref::().ok()) + .map(|dict| { + let (mut connection_type, mut password_flag) = (None, None); + let mut username = vpn + .get("user-name") + .and_then(|u| u.downcast_ref::().ok()); + if dict + .get::(&String::from("connection-type")) + .ok() + .flatten() + .as_deref() + // may be "password" or "password-tls" + .is_some_and(|p| p.starts_with("password")) + { + connection_type = Some(ConnectionType::Password); + username = Some(username.unwrap_or_default()); + + password_flag = dict + .get::(&String::from("password-flags")) + .ok() + .flatten() + .and_then(|value| match value.as_str() { + "0" => Some(PasswordFlag::None), + "1" => Some(PasswordFlag::AgentOwned), + "2" => Some(PasswordFlag::NotSaved), + "4" => Some(PasswordFlag::NotRequired), + _ => None, + }); + } + + (connection_type, username, password_flag) + }) + .unwrap_or_default(); + + Some(( + Arc::from(uuid), + ConnectionSettings::Vpn(VpnConnectionSettings { + id, + connection_type, + password_flag, + username, + }), + )) + }) + // Reduce the settings list into + .fold(IndexMap::new(), |mut set, (uuid, data)| async move { + set.insert(uuid, data); + set + }) + .await; + + Ok::<_, zbus::Error>(settings) + }; + + cosmic::task::future(async move { + settings.await.map_or_else( + |why| Message::Error(why.to_string()), + Message::KnownConnections, + ) + }) +} + +fn system_conn() -> Task { + cosmic::Task::future(async move { + zbus::Connection::system() + .await + .context("failed to create system dbus connection") + .map_or_else( + |why| Message::Error(why.to_string()), + Message::NetworkManagerConnect, + ) + }) } impl cosmic::Application for CosmicNetworkApplet { @@ -1079,30 +759,14 @@ impl cosmic::Application for CosmicNetworkApplet { const APP_ID: &'static str = config::APP_ID; fn init(core: cosmic::app::Core, _flags: ()) -> (Self, app::Task) { - let applet = Self { + let mut applet = Self { core, icon_name: "network-wired-disconnected-symbolic".to_string(), token_tx: None, ..Default::default() }; - let uuid = uuid::Uuid::new_v4().to_string().replace("-", "_"); - let my_id = - format!("com.system76.CosmicSettings.Applet._{uuid}.NetworkManager.SecretAgent",); - let (secret_agent_reregister_tx, secret_agent_reregister_rx) = unbounded(); - - ( - Self { - secret_agent_reregister_tx: Some(secret_agent_reregister_tx), - ..applet - }, - Task::batch(vec![ - snapshot_task(), - network_events_task(), - secret_agent_task(my_id, secret_agent_reregister_rx).map(Message::SecretAgent), - ]) - .map(cosmic::Action::App), - ) + (applet, system_conn().map(cosmic::Action::App)) } fn core(&self) -> &cosmic::app::Core { @@ -1121,62 +785,98 @@ impl cosmic::Application for CosmicNetworkApplet { return destroy_popup(p); } else { let mut tasks = Vec::with_capacity(2); - tasks.push(snapshot_task().map(cosmic::Action::App)); - tasks.push(cosmic::surface::surface_task( - cosmic::surface::action::app_popup( - |_| Default::default(), - |app: &mut Self| { - let new_id = window::Id::unique(); - app.popup.replace(new_id); + if let Some(conn) = self.conn.clone() { + tasks.push(update_state(conn.clone())); + tasks.push(update_devices(conn.clone())); + tasks.push(load_vpns(conn)); + let (tx, rx) = tokio::sync::mpsc::channel(4); + self.secret_tx = Some(tx); + let my_id = format!( + "com.system76.CosmicSettings.Applet.{}.NetworkManager.SecretAgent", + uuid::Uuid::new_v4() + ); + tasks.push( + cosmic::Task::stream(nm_secret_agent::secret_agent_stream( + my_id.clone(), + rx, + )) + .map(Message::SecretAgent), + ); + } + // TODO request update of state maybe + let new_id = window::Id::unique(); + self.popup.replace(new_id); - let popup_settings = app.core.applet.get_popup_settings( - app.core.main_window_id().unwrap(), - new_id, - None, - None, - None, - ); - popup_settings - }, - None, - ), - )); + let popup_settings = self.core.applet.get_popup_settings( + self.core.main_window_id().unwrap(), + new_id, + None, + None, + None, + ); - return Task::batch(tasks); + tasks.push(system_conn()); + tasks.push(get_popup(popup_settings)); + + return Task::batch(tasks).map(cosmic::Action::App); } } Message::ToggleAirplaneMode(enabled) => { self.toggle_wifi_ctr += 1; - self.nm_state.nm_state.airplane_mode = enabled; - return cosmic::task::future(async move { - match NmrsManager::new().await { - Ok(nm) => match nm.set_airplane_mode(enabled).await { - Ok(()) => Message::Refresh, - Err(e) => { - tracing::warn!("set_airplane_mode partial failure: {e}"); - Message::Refresh - } - }, - Err(e) => Message::Error(format!("nmrs init: {e}")), + if let Some(tx) = self.nm_sender.as_mut() { + if let Err(err) = + tx.unbounded_send(network_manager::Request::SetAirplaneMode(enabled)) + { + if err.is_disconnected() { + return system_conn().map(cosmic::Action::App); + } + + tracing::error!("{err:?}"); } - }) - .map(cosmic::Action::App); + } } Message::SelectWirelessAccessPoint(access_point) => { + let Some(tx) = self.nm_sender.as_ref() else { + return Task::none(); + }; + if matches!(access_point.network_type, NetworkType::Open) { - self.new_connection = Some(NewConnectionState::Waiting(access_point.clone())); - return connect_access_point_task(access_point, None, None); + if let Err(err) = + tx.unbounded_send(network_manager::Request::SelectAccessPoint( + access_point.ssid.clone(), + access_point.network_type, + self.secret_tx.clone(), + self.active_device.as_ref().map(|d| d.interface.clone()), + )) + { + if err.is_disconnected() { + return system_conn().map(cosmic::Action::App); + } + + tracing::error!("{err:?}"); + } + self.new_connection = Some(NewConnectionState::Waiting(access_point)); } else { - let known = self + if self .nm_state .nm_state .known_access_points - .iter() - .any(|known| same_access_point(known, &access_point)); - if known { - self.new_connection = - Some(NewConnectionState::Waiting(access_point.clone())); - return connect_access_point_task(access_point, None, None); + .contains(&access_point) + { + if let Err(err) = + tx.unbounded_send(network_manager::Request::SelectAccessPoint( + access_point.ssid.clone(), + access_point.network_type, + self.secret_tx.clone(), + self.active_device.as_ref().map(|d| d.interface.clone()), + )) + { + if err.is_disconnected() { + return system_conn().map(cosmic::Action::App); + } + + tracing::error!("{err:?}"); + } } self.new_connection = Some(NewConnectionState::EnterPassword { access_point, @@ -1211,6 +911,12 @@ impl cosmic::Application for CosmicNetworkApplet { Message::CloseRequested(id) => { if Some(id) == self.popup { self.popup = None; + if let Some(cancel) = self.nm_task.take() { + _ = cancel.send(()); + } + + self.secret_tx = None; + return system_conn().map(cosmic::Action::App); } } Message::OpenSettings => { @@ -1275,23 +981,19 @@ impl cosmic::Application for CosmicNetworkApplet { tracing::warn!("Failed to find known access point with ssid: {}", ssid); return Task::none(); }; - self.show_visible_networks = true; - let ssid_for_task = ssid.clone(); - let forget_task = cosmic::task::future(async move { - match NmrsManager::new().await { - Ok(nm) => match nm.forget(&ssid_for_task).await { - Ok(()) => Message::Refresh, - Err(e) => { - tracing::warn!("forget {ssid_for_task} failed: {e}"); - Message::Refresh - } - }, - Err(e) => Message::Error(format!("nmrs init: {e}")), + if let Some(tx) = self.nm_sender.as_ref() { + if let Err(err) = + tx.unbounded_send(network_manager::Request::Forget(ssid.into())) + { + if err.is_disconnected() { + return system_conn().map(cosmic::Action::App); + } + + tracing::error!("{err:?}"); } - }) - .map(cosmic::Action::App); - let reconnect_task = connect_access_point_task(ap, None, None); - return Task::batch(vec![forget_task, reconnect_task]); + self.show_visible_networks = true; + return self.update(Message::SelectWirelessAccessPoint(ap)); + } } Message::Surface(a) => { return cosmic::task::message(cosmic::Action::Cosmic( @@ -1299,75 +1001,59 @@ impl cosmic::Application for CosmicNetworkApplet { )); } Message::ActivateVpn(uuid) => { - self.nm_state.pending_vpn = Some(PendingVpn { - uuid: uuid.clone(), - action: PendingVpnAction::Activate, - }); - self.update_icon_name(); - return Task::batch(vec![ - snapshot_task().map(cosmic::Action::App), - self.connect_vpn(uuid.clone()), - ]); + return self.connect_vpn(uuid.clone()); } - Message::DeactivateVpn(uuid) => { - self.nm_state.pending_vpn = Some(PendingVpn { - uuid: uuid.clone(), - action: PendingVpnAction::Deactivate, - }); - self.update_icon_name(); - let disconnect_task = cosmic::task::future(async move { - let error = match NmrsManager::new().await { - Ok(nm) => nm - .disconnect_vpn_by_uuid(&uuid) - .await - .err() - .map(|e| format!("disconnect VPN {uuid}: {e}")), - Err(e) => Some(format!("nmrs init: {e}")), - }; - Message::VpnOperationFinished { - uuid, - action: PendingVpnAction::Deactivate, - error, + Message::DeactivateVpn(name) => { + if let Some(tx) = self.nm_sender.as_ref() { + if let Err(err) = tx.unbounded_send(network_manager::Request::Deactivate(name)) + { + if err.is_disconnected() { + return system_conn().map(cosmic::Action::App); + } + + tracing::error!("{err:?}"); } - }) - .map(cosmic::Action::App); - return Task::batch(vec![ - snapshot_task().map(cosmic::Action::App), - disconnect_task, - ]); - } - Message::VpnOperationFinished { - uuid, - action, - error, - } => { - if self.nm_state.pending_vpn.as_ref().is_some_and(|pending| { - pending.uuid.as_ref() == uuid.as_ref() && pending.action == action - }) { - self.nm_state.pending_vpn = None; - self.update_icon_name(); } - if let Some(error) = error { - tracing::error!("{error}"); - } - return snapshot_task().map(cosmic::Action::App); } Message::ToggleVpnList => { self.show_available_vpns = !self.show_available_vpns; } Message::Connect(ssid, hw_address) => { - if let Some(ap) = self - .nm_state - .nm_state - .known_access_points - .iter_mut() - .find(|c| c.ssid == ssid && c.hw_address == hw_address) - { - ap.working = true; - return connect_access_point_task(ap.clone(), None, None); + let mut network_type = NetworkType::Open; + let tx = if let Some(tx) = self.nm_sender.as_ref() { + if let Some(ap) = self + .nm_state + .nm_state + .known_access_points + .iter_mut() + .find(|c| c.ssid == ssid && c.hw_address == hw_address) + { + network_type = ap.network_type; + ap.working = true; + } + tx + } else { + return Task::none(); + }; + if let Err(err) = tx.unbounded_send(network_manager::Request::SelectAccessPoint( + ssid, + network_type, + self.secret_tx.clone(), + self.active_device.as_ref().map(|d| d.interface.clone()), + )) { + if err.is_disconnected() { + return system_conn().map(cosmic::Action::App); + } + + tracing::error!("{err:?}"); } } Message::ConnectWithPassword => { + // save password + let Some(tx) = self.nm_sender.as_ref() else { + return Task::none(); + }; + if let Some(NewConnectionState::EnterPassword { password, access_point, @@ -1375,44 +1061,55 @@ impl cosmic::Application for CosmicNetworkApplet { .. }) = self.new_connection.take() { + let is_enterprise: bool = matches!(access_point.network_type, NetworkType::EAP); + + if let Err(err) = tx.unbounded_send(network_manager::Request::Authenticate { + ssid: access_point.ssid.to_string(), + identity: is_enterprise.then(|| identity.clone()), + password, + secret_tx: self.secret_tx.clone(), + interface: self.active_device.as_ref().map(|d| d.interface.clone()), + }) { + if err.is_disconnected() { + return system_conn().map(cosmic::Action::App); + } + tracing::error!("Failed to authenticate with network manager"); + } self.new_connection - .replace(NewConnectionState::Waiting(access_point.clone())); - return connect_access_point_task(access_point, Some(identity), Some(password)); + .replace(NewConnectionState::Waiting(access_point)); } } + Message::ConnectionSettings(btree_map) => { + self.nm_state.ssid_to_uuid = btree_map; + } Message::Disconnect(ssid, hw_address) => { self.new_connection = None; - let interface = self - .nm_state - .nm_state - .known_access_points - .iter() - .find(|ap| ap.ssid == ssid && ap.hw_address == hw_address) - .and_then(|ap| ap.interface.clone()); - if let Some(ActiveConnectionInfo::WiFi { state, .. }) = - self.nm_state.nm_state.active_conns.iter_mut().find(|c| { - let c_hw_address = match c { - ActiveConnectionInfo::Wired { hw_address, .. } - | ActiveConnectionInfo::WiFi { hw_address, .. } => { - HwAddress::from_str(hw_address).unwrap_or_default() - } - ActiveConnectionInfo::Vpn { .. } => HwAddress::default(), - }; - c.name() == ssid.as_ref() && c_hw_address == hw_address - }) - { - *state = ActiveConnectionState::Deactivating; - } - return cosmic::task::future(async move { - match NmrsManager::new().await { - Ok(nm) => match nm.disconnect(interface.as_deref()).await { - Ok(()) => Message::Refresh, - Err(e) => Message::Error(format!("disconnect {ssid}: {e}")), - }, - Err(e) => Message::Error(format!("nmrs init: {e}")), + let tx = if let Some(tx) = self.nm_sender.as_ref() { + if let Some(ActiveConnectionInfo::WiFi { state, .. }) = + self.nm_state.nm_state.active_conns.iter_mut().find(|c| { + let c_hw_address = match c { + ActiveConnectionInfo::Wired { hw_address, .. } + | ActiveConnectionInfo::WiFi { hw_address, .. } => { + HwAddress::from_str(hw_address).unwrap() + } + ActiveConnectionInfo::Vpn { .. } => HwAddress::default(), + }; + c.name().as_str() == ssid.as_ref() && c_hw_address == hw_address + }) + { + *state = ActiveConnectionState::Deactivating; } - }) - .map(cosmic::Action::App); + tx + } else { + return Task::none(); + }; + if let Err(err) = tx.unbounded_send(network_manager::Request::Disconnect(ssid)) { + if err.is_disconnected() { + return system_conn().map(cosmic::Action::App); + } + + tracing::error!("{err:?}"); + } } Message::Error(error) => { tracing::error!("error: {error:?}") @@ -1424,37 +1121,141 @@ impl cosmic::Application for CosmicNetworkApplet { *identity = new_identity; } } - Message::ConnectionAttemptFinished { - access_point, - success, - error, - } => { - if success { - self.failed_known_ssids.remove(access_point.ssid.as_ref()); - self.new_connection = None; - self.show_visible_networks = false; - } else { - if let Some(error) = error { - tracing::warn!("connect {} failed: {error}", access_point.ssid); - } - self.failed_known_ssids.insert(access_point.ssid.clone()); - self.new_connection = Some(NewConnectionState::Failure(access_point)); + Message::NetworkManager(event) => match event { + network_manager::Event::Init { + conn, + sender, + state, + } => { + self.nm_sender = Some(sender); + self.update_nm_state(state); + self.conn = Some(conn); } - return snapshot_task().map(cosmic::Action::App); - } - Message::NetworkEvent(event) => { - if matches!(event, NetworkEvent::NetworkManagerRestarted) { - tracing::debug!( - "NetworkManager restarted; refreshing network snapshot and secret agent" - ); - if let Some(mut tx) = self.secret_agent_reregister_tx.clone() { - if let Err(e) = tx.unbounded_send(()) { - tracing::warn!("secret agent re-registration task stopped: {e}"); - self.secret_agent_reregister_tx = None; + network_manager::Event::WiFiEnabled(_) + | network_manager::Event::WirelessAccessPoints + | network_manager::Event::ActiveConns => { + if let Some(conn) = self.conn.clone() { + return Task::future(async move { + let conn = conn.clone(); + NetworkManagerState::new(&conn).await + }) + .map(|res| match res { + Ok(s) => Message::UpdateState(s), + Err(err) => Message::Error(err.to_string()), + }) + .map(cosmic::Action::App); + } + } + network_manager::Event::RequestResponse { + mut state, + success, + req, + } => { + if let network_manager::Request::SelectAccessPoint( + ssid, + hw_address, + _network_type, + secret_tx, + ) = &req + { + let conn_match = self + .new_connection + .as_ref() + .is_some_and(|c| c.ssid() == ssid.as_ref() ); + + if conn_match && success { + if let Some(ActiveConnectionInfo::WiFi { state, .. }) = state + .active_conns + .iter_mut() + .find(|ap| { + let ap_hw_address = match ap { + ActiveConnectionInfo::Wired { hw_address, .. } + | ActiveConnectionInfo::WiFi { hw_address, .. } => { + HwAddress::from_str(&hw_address).unwrap() + } + ActiveConnectionInfo::Vpn { .. } => HwAddress::default(), + }; + ap.name().as_str() == ssid.as_ref()}) + { + *state = ActiveConnectionState::Activated; + } + self.failed_known_ssids.remove(ssid); + self.new_connection = None; + self.show_visible_networks = false; + } else if !matches!( + &self.new_connection, + Some(NewConnectionState::EnterPassword { .. }) + ) && !success { + self.failed_known_ssids.insert(ssid.clone()); } + } else if let network_manager::Request::Authenticate { + ssid, + identity: _, + password: _, + secret_tx, + interface + } = &req + { + if let Some(NewConnectionState::Waiting(access_point)) = + self.new_connection.as_ref() + { + if !success + && ssid.as_str() == access_point.ssid.as_ref() + { + self.new_connection = + Some(NewConnectionState::Failure(access_point.clone())); + } else { + self.show_visible_networks = false; + } + } else if let Some(NewConnectionState::EnterPassword { + access_point, .. + }) = self.new_connection.as_ref() + { + if success && ssid.as_str() == access_point.ssid.as_ref() { + self.new_connection = None; + self.show_visible_networks = false; + } + } + } else if self + .new_connection + .as_ref() + .map(NewConnectionState::ssid).is_some_and(|ssid| { + state.active_conns.iter().any(|c| + matches!(c, ActiveConnectionInfo::WiFi { name, state: ActiveConnectionState::Activated, .. } if ssid == name) + ) + }) { + self.new_connection = None; + self.show_visible_networks = false; + } + + if !matches!(req, network_manager::Request::Reload) + && matches!(state.connectivity, NmConnectivityState::Portal) + { + let mut browser = std::process::Command::new("xdg-open"); + browser.arg("http://204.pop-os.org/"); + + tokio::spawn(cosmic::process::spawn(browser)); + } + + self.update_nm_state(state); + } + + cosmic_settings_network_manager_subscription::Event::Devices => { + if let Some(conn) = self.conn.clone() { + return update_devices(conn).map(cosmic::Action::App); } } - return snapshot_task().map(cosmic::Action::App); + cosmic_settings_network_manager_subscription::Event::WiFiCredentials { + ssid, + password, + security_type, + } => {} + }, + Message::NetworkManagerConnect(connection) => { + return cosmic::task::batch(vec![ + self.connect(connection.clone()), + connection_settings(connection), + ]); } Message::PasswordUpdate(entered_pw) => { if let Some(NewConnectionState::EnterPassword { password, .. }) = @@ -1463,99 +1264,93 @@ impl cosmic::Application for CosmicNetworkApplet { *password = entered_pw; } } - Message::Snapshot(snapshot) => { - self.apply_snapshot(snapshot); + Message::UpdateState(network_manager_state) => { + self.update_nm_state(network_manager_state); + } + Message::UpdateDevices(device_infos) => { + self.nm_state.devices = device_infos.into_iter().map(Arc::new).collect(); } Message::WiFiEnable(enable) => { - self.nm_state.nm_state.wifi_enabled = enable; - return cosmic::task::future(async move { - match NmrsManager::new().await { - Ok(nm) => match nm.set_wireless_enabled(enable).await { - Ok(()) => Message::Refresh, - Err(e) => Message::Error(format!("set_wireless_enabled: {e}")), - }, - Err(e) => Message::Error(format!("nmrs init: {e}")), + if let Some(sender) = self.nm_sender.as_mut() { + if let Err(err) = + sender.unbounded_send(network_manager::Request::SetWiFi(enable)) + { + if err.is_disconnected() { + return system_conn().map(cosmic::Action::App); + } + + tracing::error!("{err:?}"); } - }) - .map(cosmic::Action::App); + if let Err(err) = sender.unbounded_send(network_manager::Request::Reload) { + if err.is_disconnected() { + return system_conn().map(cosmic::Action::App); + } + + tracing::error!("{err:?}"); + } + } } Message::SecretAgent(agent_event) => match agent_event { - NmAgentEvent::RequestSecret { - connection_uuid, - connection_id, - setting, - responder, + nm_secret_agent::Event::RequestSecret { + uuid, + name, + description, + previous, + tx, + .. } => { - let description = (!connection_id.is_empty()).then_some(connection_id); - let known_vpn = self - .nm_state - .known_vpns - .contains_key(connection_uuid.as_str()); - - let mut consumed = false; - if let Some(state) = self.new_connection.as_mut() { match state { NewConnectionState::EnterPassword { access_point, .. } | NewConnectionState::Waiting(access_point) | NewConnectionState::Failure(access_point) => { - let matches_ssid = matches!( - &setting, - AgentSetting::WifiPsk { ssid } - if ssid == access_point.ssid.as_ref() - ); - let matches_uuid = self + if self .nm_state .ssid_to_uuid .get(access_point.ssid.as_ref()) - .is_some_and(|ap_uuid| { - ap_uuid.as_ref() == connection_uuid.as_str() - }); - - if matches_ssid || matches_uuid { + .is_some_and(|ap_uuid| ap_uuid.as_ref() == uuid.as_str()) + { *state = NewConnectionState::EnterPassword { access_point: access_point.clone(), - description: description.clone(), + description, identity: String::new(), password: String::new().into(), password_hidden: true, - }; + } } } } - } else if known_vpn { - let secret_keys = match &setting { - AgentSetting::Vpn { secret_keys } => secret_keys.clone(), - _ => Vec::new(), - }; + } else if self.nm_state.known_vpns.contains_key(uuid.as_str()) { self.nm_state.requested_vpn = Some(RequestedVpn { - uuid: connection_uuid.into(), + name, + uuid: uuid.into(), description, - password: SecureString::from(String::new()), + password: previous, password_hidden: true, - responder: responder.clone(), - secret_keys, + tx, }); - consumed = true; - } - - // The applet's Wi-Fi flow re-issues the password through - // `Authenticate` rather than the agent. Free NM with - // `NoSecrets` so it doesn't sit on a stalled GetSecrets call. - if !consumed { - return release_responder(responder); } } - NmAgentEvent::CancelGetSecrets => { + nm_secret_agent::Event::CancelGetSecrets { .. } => { self.new_connection = None; self.nm_state.requested_vpn = None; } - NmAgentEvent::Failed(error) => { - tracing::error!("Error from secret agent: {error}"); + nm_secret_agent::Event::Failed(error) => { + tracing::error!("Error from secret agent: {error:?}"); } }, + Message::KnownConnections(index_map) => { + self.nm_state.known_vpns = index_map; + } Message::Refresh => { - return snapshot_task().map(cosmic::Action::App); + if let Some(conn) = self.conn.clone() { + return Task::batch(vec![ + update_state(conn.clone()), + update_devices(conn.clone()), + load_vpns(conn), + ]) + .map(cosmic::Action::App); + } } Message::ToggleVPNPasswordVisibility => { if let Some(requested_vpn) = self.nm_state.requested_vpn.as_mut() { @@ -1563,30 +1358,12 @@ impl cosmic::Application for CosmicNetworkApplet { } } Message::ConnectVPNWithPassword => { - if let Some(RequestedVpn { - password, - responder, - secret_keys, - .. - }) = self.nm_state.requested_vpn.take() + if let Some(RequestedVpn { password, tx, .. }) = self.nm_state.requested_vpn.take() { return Task::future(async move { - let Some(responder) = responder.lock().await.take() else { - return Message::Refresh; - }; - - let mut secrets: HashMap = HashMap::new(); - let value = password.unsecure().to_owned(); - if secret_keys.is_empty() { - secrets.insert("password".to_owned(), value); - } else { - for key in secret_keys { - secrets.insert(key, value.clone()); - } - } - - if let Err(e) = responder.vpn_secrets(secrets).await { - tracing::error!("vpn secret reply failed: {e}"); + let mut guard = tx.lock().await; + if let Some(tx) = guard.take() { + let _ = tx.send(password); } Message::Refresh }) @@ -1599,15 +1376,7 @@ impl cosmic::Application for CosmicNetworkApplet { } } Message::CancelVPNConnection => { - if let Some(req) = self.nm_state.requested_vpn.take() { - return Task::future(async move { - if let Some(responder) = req.responder.lock().await.take() { - let _ = responder.cancel().await; - } - Message::NoOp - }) - .map(cosmic::Action::App); - } + self.nm_state.requested_vpn = None; } } Task::none() @@ -1631,20 +1400,16 @@ impl cosmic::Application for CosmicNetworkApplet { let mut known_wifi = Vec::new(); for conn in &self.nm_state.nm_state.active_conns { match conn { - ActiveConnectionInfo::Vpn { - name, - ip4_address, - ip6_address, - } => { + ActiveConnectionInfo::Vpn { name, ip_addresses } => { if self.active_device.as_ref().is_some_and(|d| { d.active_connection.as_ref().is_none_or(|a| a.0.id != *name) }) { continue; } - let mut info_col = Vec::with_capacity(3); - info_col.push(text::body(name).into()); - for elem in ip_address_elements(ip4_address, ip6_address) { - info_col.push(elem); + let mut ipv4 = Vec::with_capacity(ip_addresses.len() + 1); + ipv4.push(text::body(name).into()); + for addr in ip_addresses { + ipv4.push(text::caption(format!("{}: {}", fl!("ipv4"), addr)).into()); } vpn_ethernet_col = vpn_ethernet_col.push( column::with_capacity::(2) @@ -1652,13 +1417,13 @@ impl cosmic::Application for CosmicNetworkApplet { row::with_children([ Element::from( icon::icon( - icon::from_name("network-vpn-symbolic") + icon::from_name(self.icon_name.clone()) .symbolic(true) .into(), ) .size(40), ), - column::with_children(info_col).into(), + column::with_children(ipv4).into(), text::body(fl!("connected")) .width(Length::Fill) .align_x(Alignment::End) @@ -1676,19 +1441,20 @@ impl cosmic::Application for CosmicNetworkApplet { } ActiveConnectionInfo::Wired { name, - hw_address: _, + hw_address, speed, - ip4_address, - ip6_address, + ip_addresses, } => { if self.active_device.as_ref().is_some_and(|d| { d.active_connection.as_ref().is_none_or(|a| a.0.id != *name) }) { continue; } - let mut info_col = Vec::with_capacity(3); - info_col.push(text::body(name).into()); - info_col.extend(ip_address_elements(ip4_address, ip6_address)); + let mut ipv4 = Vec::with_capacity(ip_addresses.len() + 1); + ipv4.push(text::body(name).into()); + for addr in ip_addresses { + ipv4.push(text(format!("{}: {}", fl!("ipv4"), addr)).size(12).into()); + } let mut right_column = vec![text::body(fl!("connected")).into()]; @@ -1720,13 +1486,13 @@ impl cosmic::Application for CosmicNetworkApplet { row::with_children([ Element::from( icon::icon( - icon::from_name("network-wired-symbolic") + icon::from_name(self.icon_name.clone()) .symbolic(true) .into(), ) .size(40), ), - column::with_children(info_col).into(), + column::with_children(ipv4).into(), column::with_children(right_column) .width(Length::Fill) .align_x(Alignment::End) @@ -1744,8 +1510,7 @@ impl cosmic::Application for CosmicNetworkApplet { } ActiveConnectionInfo::WiFi { name, - ip4_address, - ip6_address, + ip_addresses, state, strength, hw_address, @@ -1755,7 +1520,10 @@ impl cosmic::Application for CosmicNetworkApplet { }) { continue; } - let ip_elements = ip_address_elements(ip4_address, ip6_address); + let mut ipv4 = Vec::with_capacity(ip_addresses.len()); + for addr in ip_addresses { + ipv4.push(text(format!("{}: {}", fl!("ipv4"), addr)).size(12).into()); + } let mut btn_content = vec![ icon::from_name(wifi_icon(*strength)) .size(24) @@ -1763,14 +1531,19 @@ impl cosmic::Application for CosmicNetworkApplet { .into(), column::with_children([ text::body(name).into(), - column::with_children(ip_elements).into(), + column::with_children(ipv4).into(), ]) .width(Length::Fill) .into(), ]; match state { ActiveConnectionState::Activating | ActiveConnectionState::Deactivating => { - btn_content.push(indeterminate_circular().size(24.0).into()); + btn_content.push( + icon::from_name("process-working-symbolic") + .size(24) + .symbolic(true) + .into(), + ); } ActiveConnectionState::Activated => btn_content.push( text::body(fl!("connected")) @@ -1788,7 +1561,7 @@ impl cosmic::Application for CosmicNetworkApplet { .icon_size(16) .on_press(Message::ResetFailedKnownSsid( name.clone(), - HwAddress::from_str(hw_address).unwrap_or_default(), + HwAddress::from_str(&hw_address).unwrap(), )) .into(), ); @@ -1803,7 +1576,7 @@ impl cosmic::Application for CosmicNetworkApplet { ) .on_press(Message::Disconnect( Arc::from(name.as_str()), - HwAddress::from_str(hw_address).unwrap_or_default(), + HwAddress::from_str(&hw_address).unwrap(), )), )]) .align_x(Alignment::Center), @@ -1916,7 +1689,7 @@ impl cosmic::Application for CosmicNetworkApplet { .nm_state .devices .iter() - .filter(|d| matches!(d.device_type, DeviceType::Wifi)) + .filter(|d| matches!(d.device_type, network_manager::devices::DeviceType::Wifi)) .collect::>(); if wireless_hw_devices.len() > 1 && self.active_device.is_none() { @@ -1960,16 +1733,14 @@ impl cosmic::Application for CosmicNetworkApplet { } for known in &self.nm_state.nm_state.known_access_points { - if matches!(known.state, DeviceState::Activated) { - continue; - } - if let Some(active_device) = self.active_device.as_ref() - && active_device + if let Some(active_device) = self.active_device.as_ref() { + if active_device .known_connections .iter() - .all(|c| c.id != *known.ssid) - { - continue; + .all(|c| &c.id != known.ssid.as_ref()) + { + continue; + } } let mut btn_content = Vec::with_capacity(2); let ssid = text::body(known.ssid.as_ref()).width(Length::Fill); @@ -1981,7 +1752,12 @@ impl cosmic::Application for CosmicNetworkApplet { .into(), ); btn_content.push(ssid.into()); - btn_content.push(indeterminate_circular().size(24.0).into()); + btn_content.push( + icon::from_name("process-working-symbolic") + .size(24) + .symbolic(true) + .into(), + ); } else if matches!(known.state, DeviceState::Unavailable) { btn_content.push( icon::from_name("network-wireless-disconnected-symbolic") @@ -2006,7 +1782,7 @@ impl cosmic::Application for CosmicNetworkApplet { .icon_size(16) .on_press(Message::ResetFailedKnownSsid( known.ssid.to_string(), - known.hw_address.clone(), + known.hw_address, )) .into(), ); @@ -2018,8 +1794,6 @@ impl cosmic::Application for CosmicNetworkApplet { .spacing(8), ); btn = match known.state { - // Activated entries are skipped above (shown in the active - // connections section instead), so no Disconnect arm is needed here. DeviceState::Failed | DeviceState::Unknown | DeviceState::Unmanaged @@ -2027,6 +1801,9 @@ impl cosmic::Application for CosmicNetworkApplet { | DeviceState::NeedAuth => { btn.on_press(Message::Connect(known.ssid.clone(), known.hw_address)) } + DeviceState::Activated => { + btn.on_press(Message::Disconnect(known.ssid.clone(), known.hw_address)) + } _ => btn, }; known_wifi.push(Element::from( @@ -2094,44 +1871,44 @@ impl cosmic::Application for CosmicNetworkApplet { ); content = content.push(id); - let is_enterprise = matches!(access_point.network_type, NetworkType::Eap); - let enter_password_col = cosmic::widget::column::with_capacity(4) - .push_maybe(is_enterprise.then(|| text::body(fl!("identity")))) - .push_maybe(is_enterprise.then(|| { - text_input::text_input("", identity).on_input(Message::IdentityUpdate) - })) - .push(text::body(fl!("enter-password"))) - .push_maybe(description.as_ref().map(|d| text::body(d.clone()))) - .push( - text_input::secure_input( - "", - password.unsecure(), - Some(Message::TogglePasswordVisibility), - *password_hidden, + let is_enterprise = matches!(access_point.network_type, NetworkType::EAP); + let enter_password_col = + cosmic::widget::column::with_capacity(4) + .push_maybe(is_enterprise.then(|| text::body(fl!("identity")))) + .push_maybe(is_enterprise.then(|| { + text_input::text_input("", identity) + .on_input(|i| Message::IdentityUpdate(i)) + })) + .push(text::body(fl!("enter-password"))) + .push_maybe(description.as_ref().map(|d| text::body(d.clone()))) + .push( + text_input::secure_input( + "", + password.unsecure(), + Some(Message::TogglePasswordVisibility), + *password_hidden, + ) + .id(SECURE_INPUT_WIFI.clone()) + .on_input(|s| Message::PasswordUpdate(SecureString::from(s))) + .on_paste(|s| Message::PasswordUpdate(SecureString::from(s))) + .on_submit(|_| Message::ConnectWithPassword), ) - .id(SECURE_INPUT_WIFI.clone()) - .on_input(|s| Message::PasswordUpdate(SecureString::from(s))) - .on_paste(|s| Message::PasswordUpdate(SecureString::from(s))) - .on_submit(|_| Message::ConnectWithPassword), - ) - .push_maybe( - access_point.wps_push.then(|| { + .push_maybe(access_point.wps_push.then(|| { container(text::body(fl!("router-wps-button"))).padding(8) - }), - ) - .push( - row::with_children([ - Element::from( - button::standard(fl!("cancel")) - .on_press(Message::CancelNewConnection), - ), - Element::from( - button::suggested(fl!("connect")) - .on_press(Message::ConnectWithPassword), - ), - ]) - .spacing(24), - ); + })) + .push( + row::with_children([ + Element::from( + button::standard(fl!("cancel")) + .on_press(Message::CancelNewConnection), + ), + Element::from( + button::suggested(fl!("connect")) + .on_press(Message::ConnectWithPassword), + ), + ]) + .spacing(24), + ); let col = padded_control(enter_password_col.spacing(8).align_x(Alignment::Center)) .align_x(Alignment::Center); @@ -2152,7 +1929,10 @@ impl cosmic::Application for CosmicNetworkApplet { let connecting = padded_control( row::with_children([ Element::from(id), - indeterminate_circular().size(24.0).into(), + icon::from_name("process-working-symbolic") + .size(24) + .symbolic(true) + .into(), ]) .spacing(8), ); @@ -2207,7 +1987,7 @@ impl cosmic::Application for CosmicNetworkApplet { .filter(|ap| { let among_active = self.nm_state.nm_state.active_conns.iter().any(|a| { let hw_address = active_conn_hw_address(a); - ap.ssid.as_ref() == a.name() && ap.hw_address == hw_address + ap.ssid.as_ref() == &a.name() && ap.hw_address == hw_address }); let among_known = self.nm_state @@ -2274,9 +2054,7 @@ impl cosmic::Application for CosmicNetworkApplet { fn active_conn_hw_address(conn: &ActiveConnectionInfo) -> HwAddress { match conn { ActiveConnectionInfo::Wired { hw_address, .. } - | ActiveConnectionInfo::WiFi { hw_address, .. } => { - HwAddress::from_str(hw_address).unwrap_or_default() - } + | ActiveConnectionInfo::WiFi { hw_address, .. } => HwAddress::from_str(hw_address).unwrap(), ActiveConnectionInfo::Vpn { .. } => HwAddress::default(), } } diff --git a/cosmic-applet-network/src/lib.rs b/cosmic-applet-network/src/lib.rs index f1ce1c6e..7d5414a5 100644 --- a/cosmic-applet-network/src/lib.rs +++ b/cosmic-applet-network/src/lib.rs @@ -3,6 +3,7 @@ mod app; mod config; mod localize; +mod utils; use crate::localize::localize; diff --git a/cosmic-applet-network/src/utils.rs b/cosmic-applet-network/src/utils.rs new file mode 100644 index 00000000..f802010f --- /dev/null +++ b/cosmic-applet-network/src/utils.rs @@ -0,0 +1,18 @@ +use futures_util::future::select; + +/// Spawn a background tasks and forward its messages +pub fn forward_event_loop + Send + 'static>( + event_loop: impl FnOnce(async_fn_stream::StreamEmitter) -> T + Send + 'static, +) -> (tokio::sync::oneshot::Sender<()>, cosmic::Task) { + let (cancel_tx, cancel_rx) = tokio::sync::oneshot::channel::<()>(); + + let task = cosmic::Task::stream(async_fn_stream::fn_stream(|emitter| async move { + select( + std::pin::pin!(cancel_rx), + std::pin::pin!(event_loop(emitter)), + ) + .await; + })); + + (cancel_tx, task) +} diff --git a/cosmic-applet-notifications/Cargo.toml b/cosmic-applet-notifications/Cargo.toml index 0d52f0e0..094cfa14 100644 --- a/cosmic-applet-notifications/Cargo.toml +++ b/cosmic-applet-notifications/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cosmic-applet-notifications" -version = "1.3.0" +version = "1.0.2" edition = "2024" license = "GPL-3.0-only" @@ -8,7 +8,7 @@ license = "GPL-3.0-only" cosmic-notifications-util = { git = "https://github.com/pop-os/cosmic-notifications" } cosmic-notifications-config = { git = "https://github.com/pop-os/cosmic-notifications" } anyhow.workspace = true -cosmic.workspace = true +libcosmic.workspace = true tokio.workspace = true # cosmic-notifications-util = { path = "../../cosmic-notifications-daemon/cosmic-notifications-util" } # cosmic-notifications-config = { path = "../../cosmic-notifications-daemon/cosmic-notifications-config" } diff --git a/cosmic-applet-notifications/i18n/el/cosmic_applet_notifications.ftl b/cosmic-applet-notifications/i18n/el/cosmic_applet_notifications.ftl index 56feb284..e69de29b 100644 --- a/cosmic-applet-notifications/i18n/el/cosmic_applet_notifications.ftl +++ b/cosmic-applet-notifications/i18n/el/cosmic_applet_notifications.ftl @@ -1,19 +0,0 @@ -hours-ago = - { $duration -> - [0] Μόλις τώρα - [one] Πριν από 1 ώρα - *[other] Πριν από { $duration } ώρες - } -show-more = Εμφάνιση { $more } ακόμη -clear-all = Απαλοιφή όλων των ειδοποιήσεων -notification-settings = Ρυθμίσεις ειδοποιήσεων... -no-notifications = Καμία ειδοποίηση -do-not-disturb = Μην ενοχλείτε -show-less = Εμφάνιση λιγότερων -clear-group = Απαλοιφή ομάδας -minutes-ago = - { $duration -> - [0] Μόλις τώρα - [one] Πριν από 1 λεπτό - *[other] Πριν από { $duration } λεπτά - } diff --git a/cosmic-applet-notifications/i18n/hu/cosmic_applet_notifications.ftl b/cosmic-applet-notifications/i18n/hu/cosmic_applet_notifications.ftl index 0ff88aa1..ec1f5a91 100644 --- a/cosmic-applet-notifications/i18n/hu/cosmic_applet_notifications.ftl +++ b/cosmic-applet-notifications/i18n/hu/cosmic_applet_notifications.ftl @@ -1,14 +1,14 @@ hours-ago = { $duration -> - [0] Épp most - [one] 1 órája - *[other] { $duration } órája + [0] Most + [one] 1 órával ezelőtt + *[other] { $duration } órával ezelőtt } minutes-ago = { $duration -> - [0] Épp most - [one] 1 perce - *[other] { $duration } perce + [0] Most + [one] 1 perccel ezelőtt + *[other] { $duration } perccel ezelőtt } show-less = Kevesebb megjelenítése show-more = { $more } további megjelenítése diff --git a/cosmic-applet-notifications/i18n/kmr/cosmic_applet_notifications.ftl b/cosmic-applet-notifications/i18n/kmr/cosmic_applet_notifications.ftl index b7a7578a..e69de29b 100644 --- a/cosmic-applet-notifications/i18n/kmr/cosmic_applet_notifications.ftl +++ b/cosmic-applet-notifications/i18n/kmr/cosmic_applet_notifications.ftl @@ -1,19 +0,0 @@ -hours-ago = - { $duration -> - [0] Niha - [one] 1 demjimêr berê - *[other] { $duration } demjimêr berê - } -minutes-ago = - { $duration -> - [0] Niha - [one] 1 xulek berê - *[other] { $duration } xulek berê - } -show-less = Kêmtir nîşan bide -show-more = Bêtir { $more } nîşan bide -clear-group = Komê pak bike -clear-all = Hemû agahdariyan pak bike -do-not-disturb = Rawestandina balkêşiyê -notification-settings = Sazkariyên agahdariyê.... -no-notifications = Agahdarî tune ne diff --git a/cosmic-applet-notifications/i18n/lo/cosmic_applet_notifications.ftl b/cosmic-applet-notifications/i18n/lo/cosmic_applet_notifications.ftl deleted file mode 100644 index e69de29b..00000000 diff --git a/cosmic-applet-notifications/i18n/sr/cosmic_applet_notifications.ftl b/cosmic-applet-notifications/i18n/sr/cosmic_applet_notifications.ftl index 39408d17..e69de29b 100644 --- a/cosmic-applet-notifications/i18n/sr/cosmic_applet_notifications.ftl +++ b/cosmic-applet-notifications/i18n/sr/cosmic_applet_notifications.ftl @@ -1,19 +0,0 @@ -show-more = Прикажи још { $more } -clear-all = Очисти сва обавештења -notification-settings = Подешавања обавештења... -no-notifications = Без обавештења -do-not-disturb = Не узнемиравај -show-less = Прикажи мање -clear-group = Очисти групу -hours-ago = - { $duration -> - [0] Управо сада - [one] Пре 1 сата - *[other] Пре { $duration } сати - } -minutes-ago = - { $duration -> - [0] Управо сада - [one] Пре 1 минута - *[other] Пре { $duration } минута - } diff --git a/cosmic-applet-power/Cargo.toml b/cosmic-applet-power/Cargo.toml index 14c027a2..041c0474 100644 --- a/cosmic-applet-power/Cargo.toml +++ b/cosmic-applet-power/Cargo.toml @@ -1,13 +1,13 @@ [package] name = "cosmic-applet-power" -version = "1.3.0" +version = "1.0.2" edition = "2024" license = "GPL-3.0-only" [dependencies] i18n-embed-fl.workspace = true i18n-embed.workspace = true -cosmic.workspace = true +libcosmic.workspace = true logind-zbus = "5.3.2" rust-embed.workspace = true rustix.workspace = true diff --git a/cosmic-applet-power/i18n/ca/cosmic_applet_power.ftl b/cosmic-applet-power/i18n/ca/cosmic_applet_power.ftl index c1084630..e69de29b 100644 --- a/cosmic-applet-power/i18n/ca/cosmic_applet_power.ftl +++ b/cosmic-applet-power/i18n/ca/cosmic_applet_power.ftl @@ -1,4 +0,0 @@ -cancel = Cancel·lar -confirm = Confirmar -restart = Reinicia -suspend = Suspèn diff --git a/cosmic-applet-power/i18n/el/cosmic_applet_power.ftl b/cosmic-applet-power/i18n/el/cosmic_applet_power.ftl index bc5a945c..41e6b281 100644 --- a/cosmic-applet-power/i18n/el/cosmic_applet_power.ftl +++ b/cosmic-applet-power/i18n/el/cosmic_applet_power.ftl @@ -4,33 +4,3 @@ log-out = Αποσύνδεση restart = Επανεκκίνηση suspend = Αναστολή confirm = Επιβεβαίωση -confirm-button = - { $action -> - [restart] { restart } - [suspend] { suspend } - [shutdown] Τερματισμός - [log-out] { log-out } - *[other] { confirm } - } -power = Ενέργεια -confirm-body = - Θα εκτελεστεί αυτόματα { $action -> - [restart] επανεκκίνηση - [suspend] αναστολή - [shutdown] τερματισμός - [lock-screen] κλείδωμα της οθόνης - [log-out] αποσύνδεση - *[other] η επιλεγμένη ενέργεια - } του συστήματος σε { $countdown } δευτερόλεπτα. -lock-screen = Κλείδωμα οθόνης -log-out-shortcut = Super + Shift + Escape -settings = Ρυθμίσεις... -lock-screen-shortcut = Super + Escape -confirm-title = - { $action -> - [restart] { restart } - [suspend] { suspend } - [shutdown] { shutdown } - [log-out] Έξοδος από όλες τις εφαρμογές και αποσύνδεση - *[other] Εφαρμογή της επιλεγμένης ενέργειας - } τώρα; diff --git a/cosmic-applet-power/i18n/hu/cosmic_applet_power.ftl b/cosmic-applet-power/i18n/hu/cosmic_applet_power.ftl index 9791f43f..9019650a 100644 --- a/cosmic-applet-power/i18n/hu/cosmic_applet_power.ftl +++ b/cosmic-applet-power/i18n/hu/cosmic_applet_power.ftl @@ -33,4 +33,4 @@ confirm-body = [lock-screen] zárolni fogja a képernyőt [log-out] kijelentkezik *[other] alkalmazni fogja a kiválasztott műveletet - } { $countdown } másodperc múlva. + } { $countdown } másodperc múlva diff --git a/cosmic-applet-power/i18n/kmr/cosmic_applet_power.ftl b/cosmic-applet-power/i18n/kmr/cosmic_applet_power.ftl index 003f4a23..e69de29b 100644 --- a/cosmic-applet-power/i18n/kmr/cosmic_applet_power.ftl +++ b/cosmic-applet-power/i18n/kmr/cosmic_applet_power.ftl @@ -1,36 +0,0 @@ -cancel = Têk bibe -confirm = Bipejirîne -power = Hêz -restart = Ji nû ve bide destpêkirin -settings = Sazkarî... -shutdown = Vemrîne -suspend = Rawestîne -log-out = Derkeve -lock-screen = Dîmenderê kilît bike -lock-screen-shortcut = Super + Escape -log-out-shortcut = Super + Shift + Escape -confirm-button = - { $action -> - [restart] { restart } - [suspend] { suspend } - [shutdown] Vemrîne - [log-out] { log-out } - *[other] { confirm } - } -confirm-title = - { $action -> - [restart] { restart } - [suspend] { suspend } - [shutdown] { shutdown } - [log-out] Hemû sepanan bigire û derkeve - *[other] Çalakiyê hilbijartî bisepîne - } niha? -confirm-body = - Pergal wê xweber were { $action -> - [restart] jinûvedestpêkkirin - [suspend] rawestandin - [shutdown] vemirandin - [lock-screen] kilîtkirin - [log-out] derketin - *[other] çalakiyê hilbijartî bisepîne - }di { $countdown } çirke de. diff --git a/cosmic-applet-power/i18n/lo/cosmic_applet_power.ftl b/cosmic-applet-power/i18n/lo/cosmic_applet_power.ftl deleted file mode 100644 index e69de29b..00000000 diff --git a/cosmic-applet-power/i18n/sr/cosmic_applet_power.ftl b/cosmic-applet-power/i18n/sr/cosmic_applet_power.ftl index 800b2900..e69de29b 100644 --- a/cosmic-applet-power/i18n/sr/cosmic_applet_power.ftl +++ b/cosmic-applet-power/i18n/sr/cosmic_applet_power.ftl @@ -1,36 +0,0 @@ -power = Напајање -lock-screen = Закључај екран -shutdown = Угаси -log-out = Одјава -restart = Поново покрени -log-out-shortcut = Супер + Shift + Esc -cancel = Откажи -suspend = Обустави -confirm = Потврди -settings = Подешавања... -lock-screen-shortcut = Супер + Esc -confirm-button = - { $action -> - [restart] { restart } - [suspend] { suspend } - [shutdown] Искључи - [log-out] { log-out } - *[other] { confirm } - } -confirm-body = - Систем ће { $action -> - [restart] поново покренути - [suspend] обуставити - [shutdown] искључити - [lock-screen] закључати екран - [log-out] одјавити се - *[other] применити изабрану радњу - } самостално за { $countdown } секунде. -confirm-title = - { $action -> - [restart] { restart } - [suspend] { suspend } - [shutdown] { shutdown } - [log-out] Затвори све програме и одјави се - *[other] Примени изабрану радњу - } сада? diff --git a/cosmic-applet-power/src/lib.rs b/cosmic-applet-power/src/lib.rs index 4a5829df..67725fbc 100644 --- a/cosmic-applet-power/src/lib.rs +++ b/cosmic-applet-power/src/lib.rs @@ -16,8 +16,9 @@ use cosmic::{ window, }, surface, theme, - widget::{button, divider, icon, space, text}, + widget::{Space, button, divider, icon, space, text}, }; +use std::sync::LazyLock; use logind_zbus::{ manager::ManagerProxy, @@ -34,6 +35,9 @@ pub mod session_manager; use crate::{cosmic_session::CosmicSessionProxy, session_manager::SessionManagerProxy}; +static SUBSURFACE_ID: LazyLock = + LazyLock::new(|| cosmic::widget::Id::new("subsurface")); + pub fn run() -> cosmic::iced::Result { localize::localize(); @@ -45,6 +49,7 @@ struct Power { icon_name: String, popup: Option, token_tx: Option>, + subsurface_id: window::Id, } #[derive(Debug, Clone, Copy)] @@ -70,7 +75,6 @@ impl PowerAction { } #[derive(Debug, Clone)] -#[allow(dead_code)] enum Message { Action(PowerAction), TogglePopup, @@ -100,6 +104,7 @@ impl cosmic::Application for Power { Self { core, icon_name: "system-shutdown-symbolic".to_string(), + subsurface_id: window::Id::unique(), token_tx: None, popup: Option::default(), }, diff --git a/cosmic-applet-status-area/Cargo.toml b/cosmic-applet-status-area/Cargo.toml index d939230c..fe85ad48 100644 --- a/cosmic-applet-status-area/Cargo.toml +++ b/cosmic-applet-status-area/Cargo.toml @@ -1,12 +1,12 @@ [package] name = "cosmic-applet-status-area" -version = "1.3.0" +version = "1.0.2" edition = "2024" license = "GPL-3.0-only" [dependencies] futures.workspace = true -cosmic.workspace = true +libcosmic.workspace = true serde.workspace = true rustc-hash.workspace = true tokio.workspace = true diff --git a/cosmic-applet-status-area/src/components/app.rs b/cosmic-applet-status-area/src/components/app.rs index 7bedc653..1071d7ac 100644 --- a/cosmic-applet-status-area/src/components/app.rs +++ b/cosmic-applet-status-area/src/components/app.rs @@ -9,14 +9,11 @@ use cosmic::{ }, cctk::sctk::reexports::calloop, iced::{ - self, - Event::Mouse, - Length, Subscription, event, - mouse::{self, ScrollDelta}, + self, Length, Subscription, platform_specific::shell::commands::popup::{destroy_popup, get_popup}, + theme::Style, window, }, - scroll::{DiscreteScrollDelta, DiscreteScrollState}, surface, widget::{container, mouse_area}, }; @@ -28,7 +25,6 @@ use crate::{ }; #[derive(Clone, Debug)] -#[allow(dead_code)] pub enum Msg { None, Activate(usize), @@ -37,8 +33,7 @@ pub enum Msg { StatusMenu((usize, status_menu::Msg)), StatusNotifier(status_notifier_watcher::Event), TogglePopup(usize), - Hovered(Option), - WheelScrolled(ScrollDelta), + Hovered(usize), Surface(surface::Action), ToggleOverflow, HoveredOverflow, @@ -54,8 +49,6 @@ pub(crate) struct App { max_menu_id: usize, popup: Option, overflow_popup: Option, - hovered_menu: Option, - scroll_states: BTreeMap, token_tx: Option>, } @@ -110,10 +103,9 @@ impl App { let overflow_index = self.overflow_index().unwrap_or(0); let children = self.menus.iter().skip(overflow_index).map(|(id, menu)| { mouse_area( - menu_icon_button(&self.core.applet, menu).on_press_down(Msg::TogglePopup(*id)), + menu_icon_button(&self.core.applet, &menu).on_press_down(Msg::TogglePopup(*id)), ) - .on_enter(Msg::Hovered(Some(*id))) - .on_exit(Msg::Hovered(None)) + .on_enter(Msg::Hovered(*id)) .into() }); @@ -218,7 +210,6 @@ impl cosmic::Application for App { { let id = *id; self.menus.remove(&id); - self.scroll_states.remove(&id); if self.open_menu == Some(id) { self.open_menu = None; if let Some(popup_id) = self.popup { @@ -321,10 +312,6 @@ impl cosmic::Application for App { } }, Msg::Hovered(id) => { - self.hovered_menu = id; - let Some(id) = id else { - return Task::none(); - }; let mut cmds = Vec::new(); if let Some(old_id) = self.open_menu.take() { if old_id != id { @@ -374,19 +361,6 @@ impl cosmic::Application for App { cmds.push(get_popup(popup_settings)); Task::batch(cmds) } - Msg::WheelScrolled(delta) => { - let Some(id) = self.hovered_menu else { - return Task::none(); - }; - let discrete_delta = self.scroll_states.entry(id).or_default().update(delta); - let Some((delta, orientation)) = discrete_scroll_delta(discrete_delta) else { - return Task::none(); - }; - let Some(menu) = self.menus.get(&id) else { - return Task::none(); - }; - scroll(id, menu.item.item_proxy().clone(), delta, orientation) - } Msg::Surface(a) => { return cosmic::task::message(cosmic::Action::Cosmic( cosmic::app::Action::Surface(a), @@ -431,7 +405,6 @@ impl cosmic::Application for App { } } Msg::HoveredOverflow => { - self.hovered_menu = None; let mut cmds = Vec::new(); if self.overflow_popup.is_some() { // If we already have an overflow popup, do nothing @@ -487,12 +460,6 @@ impl cosmic::Application for App { subscriptions.push(menu.subscription(is_open).with(*id).map(Msg::StatusMenu)); } subscriptions.push(activation_token_subscription(0).map(Msg::Token)); - subscriptions.push(event::listen_with(|e, status, _| match (e, status) { - (Mouse(mouse::Event::WheelScrolled { delta }), event::Status::Ignored) => { - Some(Msg::WheelScrolled(delta)) - } - _ => None, - })); iced::Subscription::batch(subscriptions) } @@ -505,10 +472,9 @@ impl cosmic::Application for App { .iter() .take(overflow_index.unwrap_or(self.menus.len())) .map(|(id, menu)| { - mouse_area(menu_icon_button(&self.core.applet, menu).on_press(Msg::Activate(*id))) + mouse_area(menu_icon_button(&self.core.applet, &menu).on_press(Msg::Activate(*id))) .on_right_press(Msg::TogglePopup(*id)) - .on_enter(Msg::Hovered(Some(*id))) - .on_exit(Msg::Hovered(None)) + .on_enter(Msg::Hovered(*id)) .into() }); @@ -604,33 +570,6 @@ fn activate( }) } -fn scroll( - id: usize, - item_proxy: crate::subscriptions::status_notifier_item::StatusNotifierItemProxy<'static>, - delta: i32, - orientation: &'static str, -) -> Task> { - Task::future(async move { - match item_proxy.scroll(delta, orientation).await { - Ok(_) => cosmic::action::app(Msg::None), - Err(err) => { - tracing::error!("Scroll failed for {}: {}", id, err); - cosmic::action::app(Msg::None) - } - } - }) -} - -fn discrete_scroll_delta(delta: DiscreteScrollDelta) -> Option<(i32, &'static str)> { - if delta.y != 0 { - Some((delta.y as i32, "vertical")) - } else if delta.x != 0 { - Some((delta.x as i32, "horizontal")) - } else { - None - } -} - fn menu_icon_button<'a>( applet: &'a cosmic::applet::Context, menu: &'a status_menu::State, @@ -638,7 +577,7 @@ fn menu_icon_button<'a>( let icon = menu.icon_handle().clone(); let theme = cosmic::theme::active(); - let _theme = theme.cosmic(); + let theme = theme.cosmic(); let suggested = applet.suggested_size(true); let padding = applet.suggested_padding(true).1; @@ -656,7 +595,7 @@ fn menu_icon_button<'a>( .class(if symbolic { cosmic::theme::Svg::Custom(std::rc::Rc::new(|theme| { cosmic::iced::widget::svg::Style { - color: Some(theme.cosmic().background(false).on.into()), + color: Some(theme.cosmic().background.on.into()), } })) } else { @@ -675,43 +614,3 @@ fn menu_icon_button<'a>( pub fn main() -> iced::Result { cosmic::applet::run::(()) } - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn discrete_scroll_prefers_vertical_delta() { - assert_eq!( - discrete_scroll_delta(cosmic::scroll::DiscreteScrollDelta { x: 4, y: -2 }), - Some((-2, "vertical")) - ); - } - - #[test] - fn discrete_scroll_uses_horizontal_delta_when_vertical_is_zero() { - assert_eq!( - discrete_scroll_delta(cosmic::scroll::DiscreteScrollDelta { x: 3, y: 0 }), - Some((3, "horizontal")) - ); - } - - #[test] - fn discrete_scroll_ignores_zero_delta() { - assert_eq!( - discrete_scroll_delta(cosmic::scroll::DiscreteScrollDelta { x: 0, y: 0 }), - None - ); - } - - #[test] - fn pixel_scroll_accumulates_before_emitting_discrete_delta() { - let mut state = cosmic::scroll::DiscreteScrollState::default(); - - let first = state.update(ScrollDelta::Pixels { x: 0.0, y: 12.0 }); - assert_eq!(discrete_scroll_delta(first), None); - - let second = state.update(ScrollDelta::Pixels { x: 0.0, y: 12.0 }); - assert_eq!(discrete_scroll_delta(second), Some((1, "vertical"))); - } -} diff --git a/cosmic-applet-status-area/src/components/status_menu.rs b/cosmic-applet-status-area/src/components/status_menu.rs index e706f37b..fedd79e4 100644 --- a/cosmic-applet-status-area/src/components/status_menu.rs +++ b/cosmic-applet-status-area/src/components/status_menu.rs @@ -26,7 +26,6 @@ pub struct State { expanded: Option, // TODO handle icon with multiple sizes? icon_handle: icon::Handle, - icon_theme_path: Option, click_event: Option<(i32, bool)>, } @@ -40,7 +39,6 @@ impl State { icon_handle: icon::from_name("application-default") .prefer_svg(true) .handle(), - icon_theme_path: None, click_event: None, }, iced::Task::none(), @@ -65,7 +63,6 @@ impl State { } Msg::Icon(update) => { let icon_name = update.name.unwrap_or_default(); - self.icon_theme_path = update.theme_path; // Use the icon pixmap if an icon was not defined by name. if icon_name.is_empty() { @@ -96,18 +93,13 @@ impl State { self.icon_handle = if Path::new(&icon_name).exists() { icon::from_path(Path::new(&icon_name).to_path_buf()).symbolic(true) } else { - let mut builder = icon::from_name(icon_name).prefer_svg(true).fallback(Some( - IconFallback::Names(vec![ + icon::from_name(icon_name) + .prefer_svg(true) + .fallback(Some(IconFallback::Names(vec![ "application-default".into(), "application-x-executable".into(), - ]), - )); - - if let Some(ref theme_path) = self.icon_theme_path { - builder = builder.with_extra_paths(vec![theme_path.clone()]); - } - - builder.handle() + ]))) + .handle() }; iced::Task::none() diff --git a/cosmic-applet-status-area/src/status_notifier_watcher.rs b/cosmic-applet-status-area/src/status_notifier_watcher.rs index 5c8b6175..1bb75d79 100644 --- a/cosmic-applet-status-area/src/status_notifier_watcher.rs +++ b/cosmic-applet-status-area/src/status_notifier_watcher.rs @@ -10,18 +10,21 @@ //! can be socket-activated and not conflict with anything else running as a status notifier //! watcher. //! -//! The daemon runs as long as there is at least one client still connected. +//! The daemon runs as long as as there is at least one client still connected. Which it checks +//! for every `REFRESH_INTERVAL`. use crate::subscriptions::status_notifier_watcher::server::create_service; use crate::unique_names::UniqueNames; use futures::StreamExt; -use std::collections::HashSet; +use std::{collections::HashSet, time::Duration}; use zbus::fdo; use zbus::message::Header; const DBUS_NAME: &str = "com.system76.CosmicStatusNotifierWatcher"; const OBJECT_PATH: &str = "/CosmicStatusNotifierWatcher"; +const REFRESH_INTERVAL: Duration = Duration::from_secs(60); + /// Run daemon pub fn run() -> cosmic::iced::Result { if let Err(err) = run_inner() { @@ -39,7 +42,7 @@ pub async fn cosmic_register(conn: &zbus::Connection) -> zbus::Result<()> { tokio::spawn(async move { while let Some(value) = stream.next().await { if let Some(_unique_name) = value { - // Register with new owner. + /// Register with new owner let _ = cosmic_watcher.register_applet().await; } } diff --git a/cosmic-applet-status-area/src/subscriptions/status_notifier_item.rs b/cosmic-applet-status-area/src/subscriptions/status_notifier_item.rs index 2c3d4904..fd51390e 100644 --- a/cosmic-applet-status-area/src/subscriptions/status_notifier_item.rs +++ b/cosmic-applet-status-area/src/subscriptions/status_notifier_item.rs @@ -2,7 +2,6 @@ // SPDX-License-Identifier: GPL-3.0-only use std::hash::Hash; -use std::path::PathBuf; use cosmic::iced::{self, Subscription}; use futures::{FutureExt, StreamExt}; @@ -28,7 +27,7 @@ pub struct Icon { pub struct IconUpdate { pub name: Option, pub pixmap: Option>, - pub theme_path: Option, + // pub theme_path: Option, } impl StatusNotifierItem { @@ -119,11 +118,11 @@ impl StatusNotifierItem { async fn icon_events(item_proxy: StatusNotifierItemProxy<'static>) -> IconUpdate { let icon_name = item_proxy.icon_name().await; let icon_pixmap = item_proxy.icon_pixmap().await; - let icon_theme_path = item_proxy.icon_theme_path().await.map(PathBuf::from); + // let icon_theme_path = item_proxy.icon_theme_path().await.map(PathBuf::from); IconUpdate { name: icon_name.ok(), pixmap: icon_pixmap.ok(), - theme_path: icon_theme_path.ok().filter(|x| !x.as_os_str().is_empty()), + // theme_path: icon_theme_path.ok().filter(|x| !x.as_os_str().is_empty()), } } @@ -202,8 +201,6 @@ pub trait StatusNotifierItem { fn activate(&self, x: i32, y: i32) -> zbus::Result<()>; fn secondary_activate(&self, x: i32, y: i32) -> zbus::Result<()>; - - fn scroll(&self, delta: i32, orientation: &str) -> zbus::Result<()>; } #[derive(Clone, Debug)] diff --git a/cosmic-applet-tiling/Cargo.toml b/cosmic-applet-tiling/Cargo.toml index d0c08c1f..23e689e0 100644 --- a/cosmic-applet-tiling/Cargo.toml +++ b/cosmic-applet-tiling/Cargo.toml @@ -1,14 +1,14 @@ [package] name = "cosmic-applet-tiling" -version = "1.3.0" +version = "1.0.2" edition = "2024" license = "GPL-3.0-only" [dependencies] -cosmic.workspace = true +libcosmic.workspace = true anyhow.workspace = true cctk.workspace = true -cosmic-comp-config.workspace = true +cosmic-comp-config = { git = "https://github.com/pop-os/cosmic-comp.git", rev = "5eb5af4" } cosmic-protocols.workspace = true i18n-embed-fl.workspace = true i18n-embed.workspace = true diff --git a/cosmic-applet-tiling/i18n/el/cosmic_applet_tiling.ftl b/cosmic-applet-tiling/i18n/el/cosmic_applet_tiling.ftl index 9c9d7e68..e69de29b 100644 --- a/cosmic-applet-tiling/i18n/el/cosmic_applet_tiling.ftl +++ b/cosmic-applet-tiling/i18n/el/cosmic_applet_tiling.ftl @@ -1,20 +0,0 @@ -arrow-keys = βέλη -move-window = Μετακίνηση παραθύρου -shift = Shift -super = Super -new-workspace = Συμπεριφορά νέων χώρων εργασίας -tiled = Παράθεση -active-hint = Ένδειξη ενεργού παραθύρου -navigate-windows = Πλοήγηση στα παράθυρα -toggle-floating-window = Εναλλαγή αιώρησης παραθύρων -all-workspaces = Όλοι οι χώροι εργασίας -per-workspace = Ανά χώρο εργασίας -autotile-behavior = Παράθεση παραθύρων στους χώρους εργασίας -tile-current = Παράθεση στον τρέχοντα χώρο εργασίας -floating = Αιώρηση -gaps = Κενά -tile-windows = Αυτόματη παράθεση παραθύρων -view-all-shortcuts = Προβολή όλων των συντομεύσεων... -shortcuts = Συντομεύσεις -floating-window-exceptions = Εξαιρέσεις αιωρούμενων παραθύρων... -window-management-settings = Ρυθμίσεις διαχείρισης παραθύρων... diff --git a/cosmic-applet-tiling/i18n/hu/cosmic_applet_tiling.ftl b/cosmic-applet-tiling/i18n/hu/cosmic_applet_tiling.ftl index a2d0d4bc..7d622463 100644 --- a/cosmic-applet-tiling/i18n/hu/cosmic_applet_tiling.ftl +++ b/cosmic-applet-tiling/i18n/hu/cosmic_applet_tiling.ftl @@ -3,11 +3,11 @@ tile-current = Jelenlegi munkaterület csempézése shortcuts = Gyorsbillentyűk navigate-windows = Ablakok navigálása move-window = Ablak mozgatása -toggle-floating-window = Lebegő ablak be/ki +toggle-floating-window = Az ablakok lebegtetésének be- és kikapcsolása view-all-shortcuts = Az összes gyorsbillentyű megtekintése… active-hint = Aktív ablak kiemelése gaps = Hézagok -floating-window-exceptions = Lebegőablak-kivételek… +floating-window-exceptions = Lebegő ablak kivételek… window-management-settings = Ablakkezelési beállítások… all-workspaces = Összes munkaterület per-workspace = Munkaterületenként diff --git a/cosmic-applet-tiling/i18n/kmr/cosmic_applet_tiling.ftl b/cosmic-applet-tiling/i18n/kmr/cosmic_applet_tiling.ftl index e3f124ff..e69de29b 100644 --- a/cosmic-applet-tiling/i18n/kmr/cosmic_applet_tiling.ftl +++ b/cosmic-applet-tiling/i18n/kmr/cosmic_applet_tiling.ftl @@ -1,20 +0,0 @@ -tile-windows = Çarçoveyan xweber berhev bike -tile-current = Qada xebatê heyî berhev bike -shortcuts = Kurterê -navigate-windows = Di nav çarçoveyan de bigere -move-window = Çarçoveyê bilivîne -toggle-floating-window = Rewşê çarçoveyê herikbar biguhêrîne -view-all-shortcuts = Hemû kurteriyan nîşan bide... -active-hint = Pêşniyara çalak -gaps = Valahî -floating-window-exceptions = Awarteyên çarçoveyê herikbar... -window-management-settings = Sazkariyên rêveberiya çarçoveyê... -all-workspaces = Hemû qadên xebatê -per-workspace = Li gorî qada xebatê -super = Super -shift = Shift -arrow-keys = tîr -tiled = Berhevkirî -floating = Herikbar -autotile-behavior = Li ser qadên xebatê çarçoveyan berhev bike -new-workspace = Tevgera qada xebatê ya nû diff --git a/cosmic-applet-tiling/i18n/lo/cosmic_applet_tiling.ftl b/cosmic-applet-tiling/i18n/lo/cosmic_applet_tiling.ftl deleted file mode 100644 index e69de29b..00000000 diff --git a/cosmic-applet-tiling/i18n/sr/cosmic_applet_tiling.ftl b/cosmic-applet-tiling/i18n/sr/cosmic_applet_tiling.ftl index 394e8fd9..e69de29b 100644 --- a/cosmic-applet-tiling/i18n/sr/cosmic_applet_tiling.ftl +++ b/cosmic-applet-tiling/i18n/sr/cosmic_applet_tiling.ftl @@ -1,20 +0,0 @@ -arrow-keys = стрелице -tiled = Поплочано -active-hint = Активна помоћ -navigate-windows = Крећите се прозорима -toggle-floating-window = Окини пловни прозор -all-workspaces = Сви радни простори -per-workspace = По радном простору -autotile-behavior = Поплочај прозоре на радним просторима -tile-current = Поплочај тренутни радни простор -floating = Плутајуће -gaps = Размаци -move-window = Премести прозор -tile-windows = Самостално поплочај прозоре -view-all-shortcuts = Прикажи све пречице... -shortcuts = Пречице -shift = Shift -floating-window-exceptions = Изузеци за пловне прозоре... -super = Супер -window-management-settings = Подешавања за управљање прозорима... -new-workspace = Понашање новог радног простора diff --git a/cosmic-applet-tiling/src/window.rs b/cosmic-applet-tiling/src/window.rs index 7da430e3..327d5d75 100644 --- a/cosmic-applet-tiling/src/window.rs +++ b/cosmic-applet-tiling/src/window.rs @@ -26,7 +26,7 @@ use cosmic::{ }; use cosmic_comp_config::{CosmicCompConfig, TileBehavior}; use cosmic_protocols::workspace::v2::client::zcosmic_workspace_handle_v2::TilingState; -use std::thread; +use std::{thread, time::Instant}; use tracing::error; const ID: &str = "com.system76.CosmicAppletTiling"; @@ -46,7 +46,6 @@ pub struct Window { } #[derive(Clone, Debug)] -#[allow(dead_code)] pub enum Message { TogglePopup, PopupClosed(Id), diff --git a/cosmic-applet-time/Cargo.toml b/cosmic-applet-time/Cargo.toml index b2026bac..ff5e6298 100644 --- a/cosmic-applet-time/Cargo.toml +++ b/cosmic-applet-time/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cosmic-applet-time" -version = "1.3.0" +version = "1.0.2" edition = "2024" license = "GPL-3.0-only" @@ -9,7 +9,7 @@ cosmic-applets-config.workspace = true jiff = "0.2" i18n-embed-fl.workspace = true i18n-embed.workspace = true -cosmic.workspace = true +libcosmic.workspace = true rust-embed.workspace = true tokio.workspace = true tracing-log.workspace = true @@ -17,5 +17,5 @@ tracing-subscriber.workspace = true tracing.workspace = true icu = { version = "2.1.1", features = ["compiled_data"] } zbus.workspace = true -timedate-zbus = { path = "../../dbus-settings-bindings/timedate" } +timedate-zbus = { git = "https://github.com/pop-os/dbus-settings-bindings" } logind-zbus = "5.3.2" diff --git a/cosmic-applet-time/i18n/el/cosmic_applet_time.ftl b/cosmic-applet-time/i18n/el/cosmic_applet_time.ftl index 1e7405f3..e69de29b 100644 --- a/cosmic-applet-time/i18n/el/cosmic_applet_time.ftl +++ b/cosmic-applet-time/i18n/el/cosmic_applet_time.ftl @@ -1 +0,0 @@ -datetime-settings = Ρυθμίσεις ημερομηνίας, ώρας και ημερολογίου... diff --git a/cosmic-applet-time/i18n/kmr/cosmic_applet_time.ftl b/cosmic-applet-time/i18n/kmr/cosmic_applet_time.ftl index e6ff41f9..e69de29b 100644 --- a/cosmic-applet-time/i18n/kmr/cosmic_applet_time.ftl +++ b/cosmic-applet-time/i18n/kmr/cosmic_applet_time.ftl @@ -1 +0,0 @@ -datetime-settings = Sazkariyên dîrok, dem û salnameyê... diff --git a/cosmic-applet-time/i18n/lo/cosmic_applet_time.ftl b/cosmic-applet-time/i18n/lo/cosmic_applet_time.ftl deleted file mode 100644 index e69de29b..00000000 diff --git a/cosmic-applet-time/i18n/sr/cosmic_applet_time.ftl b/cosmic-applet-time/i18n/sr/cosmic_applet_time.ftl index 014ee726..e69de29b 100644 --- a/cosmic-applet-time/i18n/sr/cosmic_applet_time.ftl +++ b/cosmic-applet-time/i18n/sr/cosmic_applet_time.ftl @@ -1 +0,0 @@ -datetime-settings = Датум, време и подешавања календара... diff --git a/cosmic-applet-time/src/window.rs b/cosmic-applet-time/src/window.rs index ddd37d31..a44d81ee 100644 --- a/cosmic-applet-time/src/window.rs +++ b/cosmic-applet-time/src/window.rs @@ -94,7 +94,6 @@ pub struct Window { } #[derive(Debug, Clone)] -#[allow(dead_code)] pub enum Message { TogglePopup, CloseRequested(window::Id), @@ -362,7 +361,7 @@ impl cosmic::Application for Window { } fn subscription(&self) -> Subscription { - fn time_subscription(show_seconds: watch::Receiver) -> Subscription { + fn time_subscription(mut show_seconds: watch::Receiver) -> Subscription { struct Wrapper { inner: watch::Receiver, id: &'static str, @@ -377,7 +376,7 @@ impl cosmic::Application for Window { inner: show_seconds, id: "time-sub", }, - |Wrapper { inner, id: _ }| { + |Wrapper { inner, id }| { let mut show_seconds = inner.clone(); stream::channel(1, move |mut output: mpsc::Sender| async move { // Mark this receiver's state as changed so that it always receives an initial diff --git a/cosmic-applet-workspaces/Cargo.toml b/cosmic-applet-workspaces/Cargo.toml index dd2fb8b0..ee9e04db 100644 --- a/cosmic-applet-workspaces/Cargo.toml +++ b/cosmic-applet-workspaces/Cargo.toml @@ -1,12 +1,12 @@ [package] name = "cosmic-applet-workspaces" -version = "1.3.0" +version = "1.0.2" authors = ["Ashley Wulber "] edition = "2024" license = "GPL-3.0-only" [dependencies] -cosmic.workspace = true +libcosmic.workspace = true cctk.workspace = true tracing.workspace = true tracing-subscriber.workspace = true diff --git a/cosmic-applet-workspaces/i18n/el/cosmic_applet_workspaces.ftl b/cosmic-applet-workspaces/i18n/el/cosmic_applet_workspaces.ftl index 84c57d03..e69de29b 100644 --- a/cosmic-applet-workspaces/i18n/el/cosmic_applet_workspaces.ftl +++ b/cosmic-applet-workspaces/i18n/el/cosmic_applet_workspaces.ftl @@ -1 +0,0 @@ -cosmic-applet-workspaces = Χώροι εργασίας COSMIC diff --git a/cosmic-applet-workspaces/i18n/kmr/cosmic_applet_workspaces.ftl b/cosmic-applet-workspaces/i18n/kmr/cosmic_applet_workspaces.ftl index e262ce29..e69de29b 100644 --- a/cosmic-applet-workspaces/i18n/kmr/cosmic_applet_workspaces.ftl +++ b/cosmic-applet-workspaces/i18n/kmr/cosmic_applet_workspaces.ftl @@ -1 +0,0 @@ -cosmic-applet-workspaces = Qadên xebatê yên COSMIC diff --git a/cosmic-applet-workspaces/i18n/lo/cosmic_applet_workspaces.ftl b/cosmic-applet-workspaces/i18n/lo/cosmic_applet_workspaces.ftl deleted file mode 100644 index e69de29b..00000000 diff --git a/cosmic-applet-workspaces/i18n/sr/cosmic_applet_workspaces.ftl b/cosmic-applet-workspaces/i18n/sr/cosmic_applet_workspaces.ftl index 98c21cf2..e69de29b 100644 --- a/cosmic-applet-workspaces/i18n/sr/cosmic_applet_workspaces.ftl +++ b/cosmic-applet-workspaces/i18n/sr/cosmic_applet_workspaces.ftl @@ -1 +0,0 @@ -cosmic-applet-workspaces = КОСМИК радни простори diff --git a/cosmic-applet-workspaces/src/components/app.rs b/cosmic-applet-workspaces/src/components/app.rs index a9bafb79..dc4ccdc4 100644 --- a/cosmic-applet-workspaces/src/components/app.rs +++ b/cosmic-applet-workspaces/src/components/app.rs @@ -84,7 +84,6 @@ impl IcedWorkspacesApplet { } #[derive(Debug, Clone)] -#[allow(dead_code)] enum Message { WorkspaceUpdate(WorkspacesUpdate), WorkspacePressed(ExtWorkspaceHandleV1), diff --git a/cosmic-applets-config/Cargo.toml b/cosmic-applets-config/Cargo.toml index 7ea99e7f..b6999f16 100644 --- a/cosmic-applets-config/Cargo.toml +++ b/cosmic-applets-config/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cosmic-applets-config" -version = "1.3.0" +version = "1.0.2" edition = "2024" [dependencies] diff --git a/cosmic-applets/Cargo.toml b/cosmic-applets/Cargo.toml index d5fe4cc2..2d950afc 100644 --- a/cosmic-applets/Cargo.toml +++ b/cosmic-applets/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cosmic-applets" -version = "1.3.0" +version = "1.0.2" edition = "2024" license = "GPL-3.0-only" @@ -23,7 +23,7 @@ cosmic-applet-time = { path = "../cosmic-applet-time" } cosmic-applet-workspaces = { path = "../cosmic-applet-workspaces" } cosmic-applet-input-sources = { path = "../cosmic-applet-input-sources" } cosmic-panel-button = { path = "../cosmic-panel-button" } -cosmic.workspace = true +libcosmic.workspace = true tracing.workspace = true tracing-subscriber.workspace = true tracing-log.workspace = true diff --git a/cosmic-applets/src/main.rs b/cosmic-applets/src/main.rs index dfd541d1..f62bbf21 100644 --- a/cosmic-applets/src/main.rs +++ b/cosmic-applets/src/main.rs @@ -12,44 +12,26 @@ fn main() -> cosmic::iced::Result { }; let start = applet.rfind('/').map_or(0, |v| v + 1); - let cmd = applet.as_str()[start..].to_string(); + let cmd = &applet.as_str()[start..]; tracing::info!("Starting `{cmd}` with version {VERSION}"); - let cmd_for_run = cmd.clone(); - let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(move || { - match cmd_for_run.as_str() { - "cosmic-app-list" => cosmic_app_list::run(), - "cosmic-applet-a11y" => cosmic_applet_a11y::run(), - "cosmic-applet-audio" => cosmic_applet_audio::run(), - "cosmic-applet-battery" => cosmic_applet_battery::run(), - "cosmic-applet-bluetooth" => cosmic_applet_bluetooth::run(), - "cosmic-applet-minimize" => cosmic_applet_minimize::run(), - "cosmic-applet-network" => cosmic_applet_network::run(), - "cosmic-applet-notifications" => cosmic_applet_notifications::run(), - "cosmic-applet-power" => cosmic_applet_power::run(), - "cosmic-applet-status-area" => cosmic_applet_status_area::run(), - "cosmic-applet-tiling" => cosmic_applet_tiling::run(), - "cosmic-applet-time" => cosmic_applet_time::run(), - "cosmic-applet-workspaces" => cosmic_applet_workspaces::run(), - "cosmic-applet-input-sources" => cosmic_applet_input_sources::run(), - "cosmic-panel-button" => cosmic_panel_button::run(), - _ => Ok(()), - } - })); - - match result { - Ok(r) => r, - Err(payload) => { - let msg = payload - .downcast_ref::<&str>() - .map(|s| s.to_string()) - .or_else(|| payload.downcast_ref::().cloned()) - .unwrap_or_else(|| "".to_string()); - tracing::error!( - "`{cmd}` panicked (likely compositor disconnect), exiting cleanly: {msg}" - ); - Ok(()) - } + match cmd { + "cosmic-app-list" => cosmic_app_list::run(), + "cosmic-applet-a11y" => cosmic_applet_a11y::run(), + "cosmic-applet-audio" => cosmic_applet_audio::run(), + "cosmic-applet-battery" => cosmic_applet_battery::run(), + "cosmic-applet-bluetooth" => cosmic_applet_bluetooth::run(), + "cosmic-applet-minimize" => cosmic_applet_minimize::run(), + "cosmic-applet-network" => cosmic_applet_network::run(), + "cosmic-applet-notifications" => cosmic_applet_notifications::run(), + "cosmic-applet-power" => cosmic_applet_power::run(), + "cosmic-applet-status-area" => cosmic_applet_status_area::run(), + "cosmic-applet-tiling" => cosmic_applet_tiling::run(), + "cosmic-applet-time" => cosmic_applet_time::run(), + "cosmic-applet-workspaces" => cosmic_applet_workspaces::run(), + "cosmic-applet-input-sources" => cosmic_applet_input_sources::run(), + "cosmic-panel-button" => cosmic_panel_button::run(), + _ => Ok(()), } } diff --git a/cosmic-panel-button/Cargo.toml b/cosmic-panel-button/Cargo.toml index cbb8b01a..dc6ffd9a 100644 --- a/cosmic-panel-button/Cargo.toml +++ b/cosmic-panel-button/Cargo.toml @@ -1,11 +1,11 @@ [package] name = "cosmic-panel-button" -version = "1.3.0" +version = "1.0.2" edition = "2024" license = "GPL-3.0-only" [dependencies] -cosmic.workspace = true +libcosmic.workspace = true rustc-hash.workspace = true tracing.workspace = true tracing-subscriber.workspace = true diff --git a/cosmic-panel-button/src/lib.rs b/cosmic-panel-button/src/lib.rs index 5c875719..718744b0 100644 --- a/cosmic-panel-button/src/lib.rs +++ b/cosmic-panel-button/src/lib.rs @@ -48,7 +48,7 @@ impl Button { icon: cosmic::widget::icon::Handle, ) -> cosmic::widget::Button<'a, Message> { let theme = cosmic::theme::active(); - let _theme = theme.cosmic(); + let theme = theme.cosmic(); let suggested = self.core.applet.suggested_size(icon.symbolic); let (major_padding, applet_padding_minor_axis) = @@ -66,7 +66,7 @@ impl Button { .class(if symbolic { cosmic::theme::Svg::Custom(std::rc::Rc::new(|theme| { cosmic::iced::widget::svg::Style { - color: Some(theme.cosmic().background(false).on.into()), + color: Some(theme.cosmic().background.on.into()), } })) } else { @@ -123,15 +123,11 @@ impl cosmic::Application for Button { fn update(&mut self, message: Msg) -> app::Task { match message { Msg::Press => { - if let Ok(mut child) = Command::new("sh") + let _ = Command::new("sh") .arg("-c") - .arg(format!("exec {}", self.desktop.exec)) + .arg(&self.desktop.exec) .spawn() - { - std::thread::spawn(move || { - let _ = child.wait(); - }); - } + .unwrap(); } Msg::ConfigUpdated(conf) => { self.config = conf diff --git a/debian/changelog b/debian/changelog index 3d46c583..e3d8e02e 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,15 +1,3 @@ -cosmic-applets (1.0.15) noble; urgency=medium - - * Update changelog - - -- Michael Murphy Thu, 28 May 2026 19:33:02 +0200 - -cosmic-applets (1.0.12) noble; urgency=medium - - * Update changelog - - -- Michael Murphy Tue, 28 Apr 2026 15:11:17 +0200 - cosmic-applets (1.0.2) noble; urgency=medium [ Ashley Wulber ] diff --git a/i18n/ca/desktop_entries.ftl b/i18n/ca/desktop_entries.ftl index 8e680471..e69de29b 100644 --- a/i18n/ca/desktop_entries.ftl +++ b/i18n/ca/desktop_entries.ftl @@ -1 +0,0 @@ -cosmic-app-list = Safata d'Aplicacions diff --git a/i18n/el/desktop_entries.ftl b/i18n/el/desktop_entries.ftl index d5257105..e69de29b 100644 --- a/i18n/el/desktop_entries.ftl +++ b/i18n/el/desktop_entries.ftl @@ -1,51 +0,0 @@ -cosmic-app-list = Περιοχή εφαρμογών -cosmic-app-list-comment = Εκκίνηση καρφιτσωμένων εφαρμογών και διαχείριση ανοικτών παραθύρων -cosmic-applet-a11y = Προσβασιμότητα -cosmic-applet-audio = Ήχος -cosmic-applet-bluetooth = Bluetooth -cosmic-applet-bluetooth-comment = Διαχείριση συσκευών Bluetooth -cosmic-applet-minimize = Ελαχιστοποιημένα παράθυρα -cosmic-applet-minimize-comment = Διαχείριση ελαχιστοποιημένων παραθύρων -cosmic-applet-network = Δίκτυο -cosmic-applet-network-comment = Διαχείριση συνδέσεων δικτύου -cosmic-applet-time = Ημερομηνία, ώρα και ημερολόγιο -cosmic-applet-workspaces = Αριθμημένοι χώροι εργασίας -cosmic-panel-workspaces-button = Κουμπί χώρων εργασίας -cosmic-applet-audio-comment = Επιλογή συσκευής ήχου, έλεγχος έντασης και στοιχεία ελέγχου πολυμέσων MPRIS -cosmic-applet-audio-keywords = Μικροεφαρμογή;Ήχος;COSMIC;Applet;Sound;Audio;MPRIS; -cosmic-applet-bluetooth-keywords = Μικροεφαρμογή;COSMIC;Applet;Bluetooth; -cosmic-applet-input-sources = Πηγές εισόδου -cosmic-applet-input-sources-comment = Εναλλαγή μεταξύ πηγών εισόδου -cosmic-applet-input-sources-keywords = Μικροεφαρμογή;Πηγή;Είσοδος;COSMIC;Applet;Input;Source; -cosmic-applet-minimize-keywords = Μικροεφαρμογή;Ελαχιστοποίηση;COSMIC;Applet;Minimize; -cosmic-applet-network-keywords = Μικροεφαρμογή;Δίκτυο;COSMIC;Applet;Network; -cosmic-applet-notifications = Κέντρο ειδοποιήσεων -cosmic-applet-notifications-comment = Διαχείριση ειδοποιήσεων και λειτουργία «Μην ενοχλείτε» -cosmic-applet-notifications-keywords = Μικροεφαρμογή;Ειδοποίηση;COSMIC;Applet;Notification; -cosmic-applet-power = Συνεδρία χρήστη -cosmic-applet-power-comment = Κλείδωμα οθόνης, αποσύνδεση, αναστολή, επανεκκίνηση και τερματισμός -cosmic-applet-power-keywords = Μικροεφαρμογή;Χρήστης;Συνεδρία;Κλείδωμα;Αποσύνδεση;Επανεκκίνηση;Τερματισμός;Αναστολή;COSMIC;Applet;User;Session;Lock;Log;Reboot;Shutdown;Suspend; -cosmic-applet-status-area = Περιοχή ειδοποιήσεων -cosmic-applet-workspaces-keywords = Μικροεφαρμογή;Χώρος;Εργασίας;COSMIC;Applet;Workspace; -cosmic-panel-app-button = Κουμπί βιβλιοθήκης εφαρμογών -cosmic-panel-workspaces-button-keywords = Μικροεφαρμογή;Χώρος;Εργασίας;Επισκόπηση;COSMIC;Applet;Workspace;Overview; -cosmic-app-list-keywords = Μικροεφαρμογή;Εφαρμογή;Περιοχή;Λίστα;Εργασία;Γραμμή;COSMIC;Applet;App;Tray;List;Task;Bar; -cosmic-applet-a11y-keywords = Μικροεφαρμογή;Προσβασιμότητα;Προσιτότητα;Οθόνη;Ανάγνωση;Αναγνώστης;Μεγεθυντικός;Φακός;Αντίθεση;Χρώμα;COSMIC;Applet;Accessibility;A11y;Screen;Reader;Magnifier;Contrast;Color; -cosmic-applet-battery = Ενέργεια και μπαταρία -cosmic-applet-battery-comment = Λειτουργίες ενέργειας και επιλογές εξοικονόμησης ενέργειας -cosmic-applet-battery-keywords = Μικροεφαρμογή;Ενέργεια;Ισχύς;Μπαταρία;COSMIC;Applet;Power;Battery; -cosmic-applet-status-area-keywords = Μικροεφαρμογή;Εφαρμογή;Ένδειξη;Ειδοποίηση;Περιοχή;Κατάσταση;COSMIC;Applet;App;Indicator;Notification;Tray;Status; -cosmic-applet-time-keywords = Μικροεφαρμογή;Ημερομηνία;Ώρα;Ημερολόγιο;COSMIC;Applet;Date;Time;Calendar; -cosmic-panel-app-button-comment = Άνοιγμα της βιβλιοθήκης εφαρμογών για εκκίνηση εγκατεστημένων εφαρμογών -cosmic-panel-app-button-keywords = Μικροεφαρμογή;Εφαρμογή;Βιβλιοθήκη;Περιοχή;COSMIC;Applet;App;Library;Tray; -cosmic-applet-a11y-comment = Διαμόρφωση των ρυθμίσεων προσβασιμότητας από τη γραμμή συστήματος -cosmic-applet-status-area-comment = Ενδείξεις εφαρμογών που ενδέχεται να εξάγουν μενού στη γραμμή συστήματος -cosmic-applet-tiling = Παράθεση -cosmic-applet-tiling-comment = Διαχείριση της ένδειξης ενεργού παραθύρου, αυτόματη παράθεση παραθύρων στον τρέχοντα χώρο εργασίας και ανά χώρο εργασίας -cosmic-applet-tiling-keywords = Μικροεφαρμογή;Παράθεση;Ένδειξη;Χώρος;Εργασίας;COSMIC;Applet;Tiling;Hint;Workspace; -cosmic-applet-time-comment = Προβολή της τρέχουσας ώρας στη γραμμή συστήματος με αναδυόμενο ημερολόγιο -cosmic-applet-workspaces-comment = Εναλλαγή μεταξύ των αριθμημένων χώρων εργασίας στη γραμμή συστήματος -cosmic-panel-launcher-button = Κουμπί μενού εκκίνησης -cosmic-panel-launcher-button-comment = Άνοιγμα του μενού εκκίνησης για αναζήτηση εφαρμογών και εκτέλεση εντολών -cosmic-panel-launcher-button-keywords = Μικροεφαρμογή;Μενού;Εκκίνηση;Εκτέλεση;COSMIC;Applet;Launcher;Runner; -cosmic-panel-workspaces-button-comment = Άνοιγμα των χώρων εργασίας για διαχείριση και εναλλαγή χώρων εργασίας diff --git a/i18n/hu/desktop_entries.ftl b/i18n/hu/desktop_entries.ftl index 82b4bfdb..8dd220f7 100644 --- a/i18n/hu/desktop_entries.ftl +++ b/i18n/hu/desktop_entries.ftl @@ -15,20 +15,20 @@ cosmic-applet-workspaces = Számozott Munkaterületek cosmic-panel-app-button = Alkalmazáskönyvtár gomb cosmic-panel-launcher-button = Indító gomb cosmic-panel-workspaces-button = Munkaterületek gomb -cosmic-app-list-keywords = COSMIC;kisalkalmazás;alkalmazás;app;tálca;lista;sáv; -cosmic-applet-a11y-keywords = COSMIC;kisalkalmazás;akadálymentesség;akadálymentesítés;képernyő;olvasó;nagyító;szín; -cosmic-applet-audio-keywords = COSMIC;kisalkalmazás;hang;MPRIS; -cosmic-applet-battery-keywords = COSMIC;kisalkalmazás;energia;akkumulátor; +cosmic-app-list-keywords = COSMIC;Kisalkalmazás;Alkalmazás;App;Tálca;Lista;Sáv; +cosmic-applet-a11y-keywords = COSMIC;Kisalkalmazás;Akadálymentesség;Akadálymentesítés;Képernyő;Olvasó;Nagyító;Szín; +cosmic-applet-audio-keywords = COSMIC;Kisalkalmazás;Hang;MPRIS; +cosmic-applet-battery-keywords = COSMIC;Kisalkalmazás;Energia;Akkumulátor; cosmic-applet-bluetooth-comment = Bluetooth-eszközök kezelése -cosmic-applet-bluetooth-keywords = COSMIC;kisalkalmazás;bluetooth; -cosmic-applet-input-sources-keywords = COSMIC;kisalkalmazás;beviteli forrás; -cosmic-applet-minimize-keywords = COSMIC;kisalkalmazás;minimizálás; -cosmic-applet-network-keywords = COSMIC;kisalkalmazás;hálózat;internet; -cosmic-applet-notifications-keywords = COSMIC;kisalkalmazás;értesítések; -cosmic-applet-power-keywords = COSMIC;kisalkalmazás;felhasználói munkamenet;zárolás;kijelentkezés;újraindítás;leállítás;felfüggesztés; -cosmic-panel-app-button-keywords = COSMIC;kisalkalmazás;alkalmazás;könyvtár;tálca; -cosmic-panel-launcher-button-keywords = COSMIC;kisalkalmazás;indító;futtató; -cosmic-panel-workspaces-button-keywords = COSMIC;kisalkalmazás;munkaterületek;áttekintés; +cosmic-applet-bluetooth-keywords = COSMIC;Kisalkalmazás;Bluetooth; +cosmic-applet-input-sources-keywords = COSMIC;Kisalkalmazás;Beviteli;Forrás; +cosmic-applet-minimize-keywords = COSMIC;Kisalkalmazás;Minimizálás; +cosmic-applet-network-keywords = COSMIC;Kisalkalmazás;Hálózat;Internet; +cosmic-applet-notifications-keywords = COSMIC;Kisalkalmazás;Értesítések; +cosmic-applet-power-keywords = COSMIC;Kisalkalmazás;Felhasználói munkamenet;Zárolás;Kijelentkezés;Újraindítás;Leállítás;Felfüggesztés; +cosmic-panel-app-button-keywords = COSMIC;Kisalkalmazás;Alkalmazás;Könyvtár;Tálca; +cosmic-panel-launcher-button-keywords = COSMIC;Kisalkalmazás;Indító;Futtató; +cosmic-panel-workspaces-button-keywords = COSMIC;Kisalkalmazás;Munkaterületek;Áttekintés; cosmic-applet-a11y-comment = Akadálymentességi beállítások kezelése a panelen cosmic-applet-audio-comment = Hangeszköz kiválasztása, hangerőszabályozás és MPRIS médiavezérlés cosmic-applet-battery-comment = Energiamódok és energiagazdálkodási beállítások @@ -39,13 +39,13 @@ cosmic-applet-notifications-comment = Értesítések és a Ne zavarjanak kezelé cosmic-applet-power-comment = Képernyő zárolása, kijelentkezés, felfüggesztés, újraindítás és leállítás cosmic-app-list-comment = Rögzített alkalmazások indítása és a megnyitott ablakok kezelése cosmic-applet-status-area-comment = Alkalmazásjelzők, melyek menüt is megjeleníthetnek a panelen -cosmic-applet-status-area-keywords = COSMIC;kisalkalmazás;alkalmazás;app;jelző;értesítések;tálca;állapot; -cosmic-applet-tiling-comment = Az aktív ablakkiemelés, valamint a jelenlegi és munkaterületenkénti automatikus csempézés kezelése -cosmic-applet-tiling-keywords = COSMIC;kisalkalmazás;csempézés;kiemelés;munkaterületek; +cosmic-applet-status-area-keywords = COSMIC;Kisalkalmazás;Alkalmazás;App;Jelző;Értesítések;Tálca;Állapot; +cosmic-applet-tiling-comment = Az aktív ablak kiemelésének, valamint az aktuális és munkaterületenkénti automatikus csempézés kezelése +cosmic-applet-tiling-keywords = COSMIC;Kisalkalmazás;Csempézés;Kiemelés;Munkaterületek; cosmic-applet-time-comment = Az aktuális idő megjelenítése a panelen, felugró naptárral -cosmic-applet-time-keywords = COSMIC;kisalkalmazás;dátum;idő;naptár; +cosmic-applet-time-keywords = COSMIC;Kisalkalmazás;Dátum;Idő;Naptár; cosmic-applet-workspaces-comment = Váltás számozott munkaterületek között a panelen -cosmic-applet-workspaces-keywords = COSMIC;kisalkalmazás;munkaterületek; +cosmic-applet-workspaces-keywords = COSMIC;Kisalkalmazás;Munkaterületek; cosmic-panel-app-button-comment = Az alkalmazáskönyvtár megnyitása a telepített alkalmazások indításához cosmic-panel-launcher-button-comment = Az indító megnyitása alkalmazások kereséséhez és parancsok futtatásához cosmic-panel-workspaces-button-comment = A munkaterületek áttekintőjének megnyitása munkaterületek kezeléséhez és váltásához diff --git a/i18n/ja/desktop_entries.ftl b/i18n/ja/desktop_entries.ftl index c5b41c0a..e69de29b 100644 --- a/i18n/ja/desktop_entries.ftl +++ b/i18n/ja/desktop_entries.ftl @@ -1 +0,0 @@ -cosmic-applet-a11y = アクセシビリティ diff --git a/i18n/kmr/desktop_entries.ftl b/i18n/kmr/desktop_entries.ftl index 0a42fc18..e69de29b 100644 --- a/i18n/kmr/desktop_entries.ftl +++ b/i18n/kmr/desktop_entries.ftl @@ -1,51 +0,0 @@ -cosmic-applet-audio = Deng -cosmic-app-list = Destgeha sepanan -cosmic-applet-a11y = Gihîştin -cosmic-applet-bluetooth = Bluetooth -cosmic-applet-bluetooth-comment = Amûrên Bluetooth bi rê ve bibe -cosmic-applet-bluetooth-keywords = COSMIC;Sepanok;Bluetooth; -cosmic-applet-input-sources = Çavkaniyên ketanê -cosmic-applet-input-sources-comment = Di navbera çavkaniyên ketanê de biguhêrîne -cosmic-applet-input-sources-keywords = COSMIC;Sepanok;Ketan;Çavkanî; -cosmic-app-list-comment = Sepanên darxistî veke û çarçoveyên vekirî bi rê ve bibe -cosmic-app-list-keywords = COSMIC;Sepanok;Sepan;Destgeh;Lîste;Erk;Bend; -cosmic-applet-a11y-comment = Sazkariyên gihîştinê ji destgehê saz bike -cosmic-applet-a11y-keywords = COSMIC;Sepanok;Gihîştin;Dîmender;A11y;Xwîner;Dîmender;Mezinker;Bijberî;Reng; -cosmic-applet-audio-comment = Hilbijartina amûra dengê, kontrola dengê, û kontrolên mediyaya MPRIS -cosmic-applet-audio-keywords = COSMIC;Sepanok;Deng;Deng;MPRIS; -cosmic-applet-battery = Hêz & Betarî -cosmic-applet-battery-comment = Awayên hêzê û vebijêrkên parastina hêzê -cosmic-applet-battery-keywords = COSMIC;Sepanok;Hêz;Betarî; -cosmic-applet-minimize = Çarçoveyên biçûk -cosmic-applet-minimize-comment = Çarçoveyên biçûk bi rê ve bibe -cosmic-applet-minimize-keywords = COSMIC;Sepanok;Biçûkkirin; -cosmic-applet-network = Tor -cosmic-applet-network-comment = Girêdanên torê bi rê ve bibe -cosmic-applet-network-keywords = COSMIC;Sepanok;Tor; -cosmic-applet-notifications = Navenda Agahdariyan -cosmic-applet-notifications-comment = Agahdarî û rawestandina balkişiyê bi rê ve bibe -cosmic-applet-notifications-keywords = COSMIC;Sepanok;Agahdarî; -cosmic-applet-power = Danaşîna bikarhêner -cosmic-applet-power-comment = Dîmenderê kilît bike, derkeve, rawestîne, ji nû ve bide destpêkirin, û vemrîne -cosmic-applet-power-keywords = COSMIC;Sepanok;Bikarhêner;Danaşîn;Kilîtkirin;Derketin;Destpêkirin;Vemirandin;Rawestandin; -cosmic-applet-status-area = Desgeha agahdariyan -cosmic-applet-status-area-comment = Nîşanderên sepanan ên ku dibe ku menuyan bo destgehê derxînin -cosmic-applet-status-area-keywords = COSMIC;Sepanok;Sepan;Nîşander;Agahdarî;Destgeh;Rewş; -cosmic-applet-tiling = Berhevkirin -cosmic-applet-tiling-comment = Berhevkirina xweber a çalak, ya niha û ya her qada xebatê bi rê ve bibe -cosmic-applet-tiling-keywords = COSMIC;Sepanok;Berhevkirin;Nîşander;Navenda xebatê; -cosmic-applet-time = Dîrok, dem & Salname -cosmic-applet-time-comment = Demê niha li ser destgehê bi salnameyeke vebûyî nîşan bide -cosmic-applet-time-keywords = COSMIC;Sepanok;Dîrok;Dem;Salname; -cosmic-applet-workspaces = Qada xebatê jimarkirî -cosmic-applet-workspaces-comment = Di navbera qada xebatê jimarkirî de di destgehê de biguherîne -cosmic-applet-workspaces-keywords = COSMIC;Sepanok;Qada xebatê; -cosmic-panel-app-button = Bişkoka pirtûkxaneya sepanan -cosmic-panel-app-button-comment = Pirtûkxaneya sepanan ji bo vekirina sepanên sazkirî veke -cosmic-panel-app-button-keywords = COSMIC;Sepanok;Sepan;Pirtûkxane;Destgeh; -cosmic-panel-launcher-button = Bişkoka destpêker -cosmic-panel-launcher-button-comment = Destpêkerê ji bo lêgerîna sepanan û xebitandina fermanan veke -cosmic-panel-launcher-button-keywords = COSMIC;Sepanok;Destpêker;Rêveber; -cosmic-panel-workspaces-button = Bişkoka qadên xebatê -cosmic-panel-workspaces-button-comment = Pêşdîtina qadên xebatê veke ji bo bi rê ve bibî û biguhêrînî -cosmic-panel-workspaces-button-keywords = COSMIC;Sepan;Qada xebatê;Pêşdîtin; diff --git a/i18n/ko/desktop_entries.ftl b/i18n/ko/desktop_entries.ftl index 17b758c9..7a9abf42 100644 --- a/i18n/ko/desktop_entries.ftl +++ b/i18n/ko/desktop_entries.ftl @@ -46,6 +46,3 @@ cosmic-panel-launcher-button-keywords = COSMIC;애플릿;런처;실행기; cosmic-panel-workspaces-button = 작업 공간 버튼 cosmic-panel-workspaces-button-comment = 작업 공간 관리 및 전환을 위해 작업 공간 개요 열기 cosmic-panel-workspaces-button-keywords = COSMIC;애플릿;작업 공간;개요; -cosmic-applet-workspaces-comment = 패널에서 번호가 지정된 작업 공간 간 전환 -cosmic-applet-tiling-comment = 활성 창 강조, 현재 및 작업 공간별 자동 타일링 관리 -cosmic-applet-workspaces = 번호가 지정된 작업 공간 diff --git a/i18n/lo/desktop_entries.ftl b/i18n/lo/desktop_entries.ftl deleted file mode 100644 index e69de29b..00000000 diff --git a/i18n/pt/desktop_entries.ftl b/i18n/pt/desktop_entries.ftl index 1fe912c3..de93fad1 100644 --- a/i18n/pt/desktop_entries.ftl +++ b/i18n/pt/desktop_entries.ftl @@ -20,8 +20,3 @@ cosmic-applet-workspaces-comment = Alternar entre áreas de trabalho numeradas n cosmic-applet-workspaces-keywords = COSMIC;Miniaplicação;Applet;Área;Trabalho; cosmic-panel-workspaces-button-comment = Abrir a visão geral das áreas de trabalho para gerir e alternar entre elas cosmic-panel-workspaces-button-keywords = COSMIC;Miniaplicação;Applet;Área;Trabalho;Visão;Geral; -cosmic-applet-a11y-comment = Configurar definições de acessibilidade a partir do painel -cosmic-applet-battery = Energia e Bateria -cosmic-app-list-keywords = COSMIC;Miniaplicação;Applet;Aplicação;Bandeja;Lista;Tarefa;Barra; -cosmic-applet-a11y-keywords = COSMIC;Miniaplicação;Applet;Acessibilidade;A11y;Ecrã;Leitor;Lupa;Ampliador;Contraste;Cor; -cosmic-applet-audio-keywords = COSMIC;Miniaplicação;Applet;Som;Áudio;MPRIS; diff --git a/i18n/sr/desktop_entries.ftl b/i18n/sr/desktop_entries.ftl index 10c8d1eb..e69de29b 100644 --- a/i18n/sr/desktop_entries.ftl +++ b/i18n/sr/desktop_entries.ftl @@ -1,51 +0,0 @@ -cosmic-app-list = Системска касета -cosmic-app-list-comment = Покрени закачене програме и управљај отвореним прозорима -cosmic-app-list-keywords = COSMIC;Applet;App;Tray;List;Task;Bar;Космик;Програмчић;Програм;Касета;Списак;Задатак;Трака;kosmik;programčić;program;kaseta;spisak;zadatak;traka; -cosmic-applet-a11y = Приступачност -cosmic-applet-a11y-comment = Подеси подешавања приступачности из траке -cosmic-applet-a11y-keywords = COSMIC;Applet;Accessibility;A11y;Screen;Reader;Magnifier;Contrast;Color;Космик;Програмчић;Приступачност;А11ј;Екран;Читач;Лупа;Контраст;Боја;kosmik;programčić;pristupačnost;a11j;ekran;čitač;lupa;kontrast;boja; -cosmic-applet-audio = Звук -cosmic-applet-audio-comment = Избор уређаја звука, управљање јачином звука и MPRIS контроле медија -cosmic-applet-audio-keywords = COSMIC;Applet;Sound;Audio;MPRIS;Космик;Програмчић;Аудио;Звук;Јачина звука;МПРИС;Медија;kosmik;programčić;audio;zvuk;jačina zvuka;medija; -cosmic-applet-battery = Напајање и батерија -cosmic-applet-battery-comment = Режими напајања и могућности уштеде енергије -cosmic-applet-battery-keywords = COSMIC;Applet;Power;Battery;Космик;Програмчић;Напајање;Батерија;kosmik;programčić;napajanje;baterija; -cosmic-applet-bluetooth = Блутут -cosmic-applet-bluetooth-comment = Управљај Блутут уређајима -cosmic-applet-bluetooth-keywords = COSMIC;Applet;Bluetooth;Космик;Програмчић;Блутут;kosmik;programčić;blutut; -cosmic-applet-input-sources = Извори уноса -cosmic-applet-input-sources-comment = Пребацивање између извора уноса -cosmic-applet-input-sources-keywords = COSMIC;Applet;Input;Source;Космик;Програмчић;Унос;Извор;kosmik;programčić;unos;izvor; -cosmic-applet-minimize = Умањени прозори -cosmic-applet-minimize-comment = Управљај умањеним прозорима -cosmic-applet-minimize-keywords = COSMIC;Applet;Minimize;Космик;Програмчић;Умањи;kosmik;programčić;umanji; -cosmic-applet-network = Мрежа -cosmic-applet-network-comment = Управљај мрежним везама -cosmic-applet-network-keywords = COSMIC;Applet;Network;Космик;Програмчић;Мрежа;kosmik;programčić;mreža; -cosmic-applet-notifications = Центар за обавештења -cosmic-applet-notifications-comment = Управљај обавештењима и режимом „Не узнемиравај“ -cosmic-applet-notifications-keywords = COSMIC;Applet;Notification;Космик;Програмчић;Обавештење;kosmik;programčić;obaveštenje; -cosmic-applet-power = Корисничка сесија -cosmic-applet-power-comment = Закључај екран, одјави се, обустави, поново покрени и искључи -cosmic-applet-power-keywords = COSMIC;Applet;User;Session;Lock;Log;Reboot;Shutdown;Suspend;Космик;Програмчић;Корисник;Сесија;Закључавање;Одјава;Поновно покретање;Искључивање;Обустава;kosmik;programčić;korisnik;sesija;zaključavanje;odjava;ponovno pokretanje;isključivanje;obustava; -cosmic-applet-status-area = Обавештајна касета -cosmic-applet-status-area-comment = Показивачи програма који могу извести изборнике у површ -cosmic-applet-status-area-keywords = COSMIC;Applet;App;Indicator;Notification;Tray;Status;Космик;Програмчић;Програм;Показатељ;Обавештење;Пољце;Статус;kosmik;programčić;program;pokazatelj;obaveštenje;poljce;status; -cosmic-applet-tiling = Поплочавање -cosmic-applet-tiling-comment = Управљај активним саветом, тренутним и самопоплочавањем по радном простору -cosmic-applet-tiling-keywords = COSMIC;Applet;Tiling;Hint;Workspace;Космик;Програмчић;Поплочавање;Помоћ;Радни простор;kosmik;programčić;popločavanje;pomoć;radni prostor; -cosmic-applet-time = Датум, време и календар -cosmic-applet-time-comment = Приказује тренутно време у површи са искакућим календаром -cosmic-applet-time-keywords = COSMIC;Applet;Date;Time;Calendar;Космик;Програмчић;Датум;Време;Календар;kosmik;programčić;datum;vreme;kalendar; -cosmic-applet-workspaces = Радни простори обележени бројевима -cosmic-applet-workspaces-comment = Пребаците између радних простора обележених бројевима у траци -cosmic-applet-workspaces-keywords = COSMIC;Applet;Workspace;Космик;Програмчић;радни простор;kosmik;programčić;radni prostor; -cosmic-panel-app-button = Дугме за библиотеку програма -cosmic-panel-app-button-comment = Отвори библиотеку програма за покретање инсталираних програма -cosmic-panel-app-button-keywords = COSMIC;Applet;App;Library;Tray;Космик;Програмчић;Програм;Библиотека;Касета;kosmik;programčić;program;biblioteka;kaseta; -cosmic-panel-launcher-button = Дугме покретача -cosmic-panel-launcher-button-comment = Отвори покретач да бисте претражили програме и покренули наредбе -cosmic-panel-launcher-button-keywords = COSMIC;Applet;Launcher;Runner;Космик;Програмчић;Покретач;Покретач;kosmik;programčić;pokretač;pokretač; -cosmic-panel-workspaces-button = Дугме радних простора -cosmic-panel-workspaces-button-comment = Отвори преглед радних простора за управљање и пребацивање радних простора -cosmic-panel-workspaces-button-keywords = COSMIC;Applet;Workspace;Overview;Космик;Програмчић;радни простор;Преглед;kosmik;programčić;radni prostor;pregled; diff --git a/i18n/uk/desktop_entries.ftl b/i18n/uk/desktop_entries.ftl index 72bfb5ba..b6b177b5 100644 --- a/i18n/uk/desktop_entries.ftl +++ b/i18n/uk/desktop_entries.ftl @@ -30,7 +30,7 @@ cosmic-applet-input-sources-comment = Перемикання джерел вве cosmic-applet-input-sources-keywords = COSMIC;КОСМІК;КОСМОС;КОСМІЧНЕ;Віджет;Віджети;Джерело;Введення;Розкладка;Клавіатура; cosmic-applet-minimize-comment = Керування згорнутими вікнами cosmic-applet-minimize-keywords = COSMIC;КОСМІК;КОСМОС;КОСМІЧНЕ;Віджет;Віджети;Згорнути;Згорнуті;Вікна;Вікно; -cosmic-applet-network-comment = Керування мережевими з’єднаннями +cosmic-applet-network-comment = Керування мережевими підключеннями cosmic-applet-network-keywords = COSMIC;КОСМІК;КОСМОС;КОСМІЧНЕ;Віджет;Віджети;Мережа;Інтернет; cosmic-applet-notifications-comment = Керування сповіщеннями та режимом Не турбувати cosmic-applet-notifications-keywords = COSMIC;КОСМІК;КОСМОС;КОСМІЧНЕ;Віджет;Віджети;Сповіщення; diff --git a/justfile b/justfile index 99c53104..83a9f819 100644 --- a/justfile +++ b/justfile @@ -59,16 +59,13 @@ _install_status_notifier_watcher: sed "s|@bindir@|{{ prefixdir }}|" cosmic-applet-status-area/data/com.system76.CosmicStatusNotifierWatcher.service.in > cosmic-applet-status-area/data/com.system76.CosmicStatusNotifierWatcher.service install -Dm0644 cosmic-applet-status-area/data/com.system76.CosmicStatusNotifierWatcher.service {{ libdir }}/systemd/user/com.system76.CosmicStatusNotifierWatcher.service -_install_secret_agent_policy: - install -Dm0644 cosmic-applet-network/data/dbus-1/system.d/com.system76.CosmicSettings.Applet.NetworkManager.SecretAgent.conf {{ sharedir }}/dbus-1/system.d/com.system76.CosmicSettings.Applet.NetworkManager.SecretAgent.conf - # Installs files into the system -install: (_install_bin 'cosmic-applets') (_link_applet 'cosmic-panel-button') (_install_applet 'com.system76.CosmicAppList' 'cosmic-app-list') (_install_default_schema 'cosmic-app-list') (_install_applet 'com.system76.CosmicAppletA11y' 'cosmic-applet-a11y') (_install_applet 'com.system76.CosmicAppletAudio' 'cosmic-applet-audio') (_install_applet 'com.system76.CosmicAppletInputSources' 'cosmic-applet-input-sources') (_install_applet 'com.system76.CosmicAppletBattery' 'cosmic-applet-battery') (_install_applet 'com.system76.CosmicAppletBluetooth' 'cosmic-applet-bluetooth') (_install_applet 'com.system76.CosmicAppletMinimize' 'cosmic-applet-minimize') (_install_applet 'com.system76.CosmicAppletNetwork' 'cosmic-applet-network') (_install_applet 'com.system76.CosmicAppletNotifications' 'cosmic-applet-notifications') (_install_applet 'com.system76.CosmicAppletPower' 'cosmic-applet-power') (_install_applet 'com.system76.CosmicAppletStatusArea' 'cosmic-applet-status-area') (_install_applet 'com.system76.CosmicAppletTiling' 'cosmic-applet-tiling') (_install_applet 'com.system76.CosmicAppletTime' 'cosmic-applet-time') (_install_applet 'com.system76.CosmicAppletWorkspaces' 'cosmic-applet-workspaces') (_install_button 'com.system76.CosmicPanelAppButton' 'cosmic-panel-app-button') (_install_button 'com.system76.CosmicPanelLauncherButton' 'cosmic-panel-launcher-button') (_install_button 'com.system76.CosmicPanelWorkspacesButton' 'cosmic-panel-workspaces-button') _install_metainfo _install_status_notifier_watcher _install_secret_agent_policy +install: (_install_bin 'cosmic-applets') (_link_applet 'cosmic-panel-button') (_install_applet 'com.system76.CosmicAppList' 'cosmic-app-list') (_install_default_schema 'cosmic-app-list') (_install_applet 'com.system76.CosmicAppletA11y' 'cosmic-applet-a11y') (_install_applet 'com.system76.CosmicAppletAudio' 'cosmic-applet-audio') (_install_applet 'com.system76.CosmicAppletInputSources' 'cosmic-applet-input-sources') (_install_applet 'com.system76.CosmicAppletBattery' 'cosmic-applet-battery') (_install_applet 'com.system76.CosmicAppletBluetooth' 'cosmic-applet-bluetooth') (_install_applet 'com.system76.CosmicAppletMinimize' 'cosmic-applet-minimize') (_install_applet 'com.system76.CosmicAppletNetwork' 'cosmic-applet-network') (_install_applet 'com.system76.CosmicAppletNotifications' 'cosmic-applet-notifications') (_install_applet 'com.system76.CosmicAppletPower' 'cosmic-applet-power') (_install_applet 'com.system76.CosmicAppletStatusArea' 'cosmic-applet-status-area') (_install_applet 'com.system76.CosmicAppletTiling' 'cosmic-applet-tiling') (_install_applet 'com.system76.CosmicAppletTime' 'cosmic-applet-time') (_install_applet 'com.system76.CosmicAppletWorkspaces' 'cosmic-applet-workspaces') (_install_button 'com.system76.CosmicPanelAppButton' 'cosmic-panel-app-button') (_install_button 'com.system76.CosmicPanelLauncherButton' 'cosmic-panel-launcher-button') (_install_button 'com.system76.CosmicPanelWorkspacesButton' 'cosmic-panel-workspaces-button') _install_metainfo _install_status_notifier_watcher # Vendor Cargo dependencies locally vendor: mkdir -p .cargo - cargo vendor --locked | head -n -1 > .cargo/config + cargo vendor | head -n -1 > .cargo/config echo 'directory = "vendor"' >> .cargo/config tar pcf vendor.tar vendor rm -rf vendor diff --git a/redeploy.sh b/redeploy.sh deleted file mode 100755 index 686bf3cb..00000000 --- a/redeploy.sh +++ /dev/null @@ -1,56 +0,0 @@ -#!/usr/bin/env bash -# Recompile et déploie le fork local cosmic-applets (multiplexor cosmic-applets + -# binaire séparé cosmic-app-list). -# -# Cibles : /usr/local/bin/cosmic-applets et /usr/local/bin/cosmic-app-list -# (binaires compilés manuellement, hors pacman, qui priment sur ceux du paquet -# via $PATH /usr/local/bin avant /usr/bin). -# -# Branche de déploiement : yoda-dock-magnification (contient les commits -# magnification + le fix Wayland error handling). - -set -euo pipefail - -REPO="/home/lionel/Projets/COSMIC/cosmic-applets" -TARGETS=( - "cosmic-applets" - "cosmic-app-list" - "cosmic-panel-button" -) -BIN_DIR="/usr/local/bin" - -cd "$REPO" - -echo "==> Branche actuelle : $(git branch --show-current)" -echo "==> Build release (workspace)..." -cargo build --release --workspace - -STAMP=$(date +%Y%m%d-%H%M%S) -for bin in "${TARGETS[@]}"; do - src="$REPO/target/release/$bin" - dst="$BIN_DIR/$bin" - - if [[ ! -f "$src" ]]; then - echo "!! Binaire absent après build : $src" >&2 - exit 1 - fi - - if [[ -f "$dst" ]]; then - echo "==> Backup $dst" - sudo cp -a "$dst" "${dst}.bak.${STAMP}" - fi - - echo "==> Install $src -> $dst" - sudo install -m755 "$src" "$dst" -done - -echo "==> Kill des process applet en cours (cosmic-panel relancera à la demande)" -pkill -u "$USER" -f "/usr/local/bin/cosmic-applet" || true -pkill -u "$USER" -f "/usr/local/bin/cosmic-app-list" || true -pkill -u "$USER" -f "/usr/local/bin/cosmic-panel-button" || true - -echo "==> Vérif" -for bin in "${TARGETS[@]}"; do - file "$BIN_DIR/$bin" -done -echo "OK — relogin recommandé pour repartir sur des process applet propres." diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 075062e5..ff100edc 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,2 +1,2 @@ [toolchain] -channel = "1.93.0" +channel = "1.90.0"