From 9ef80984986d5544632c46adaa5dea98d97c001a Mon Sep 17 00:00:00 2001 From: Ashley Wulber <48420062+wash2@users.noreply.github.com> Date: Fri, 9 Dec 2022 15:31:19 -0500 Subject: [PATCH] Iced network applet (#31) * builds and shows basic applet * feat: connect up subscriptions * feat: network applet mostly working --- Cargo.lock | 277 +-------- Cargo.toml | 2 +- applets/cosmic-applet-network/Cargo.lock | 574 +++++++++++++----- applets/cosmic-applet-network/Cargo.toml | 27 +- applets/cosmic-applet-network/i18n.toml | 4 + .../i18n/en/cosmic_applet_network.ftl | 7 + applets/cosmic-applet-network/src/app.rs | 344 +++++++++++ applets/cosmic-applet-network/src/config.rs | 3 + applets/cosmic-applet-network/src/localize.rs | 47 ++ applets/cosmic-applet-network/src/main.rs | 56 +- .../src/network_manager/available_wifi.rs | 43 ++ .../src/network_manager/current_networks.rs | 108 ++++ .../src/network_manager/mod.rs | 242 ++++++++ applets/cosmic-applet-network/src/task.rs | 35 -- applets/cosmic-applet-network/src/ui.rs | 5 - .../src/ui/available_wifi.rs | 128 ---- .../src/ui/current_networks.rs | 248 -------- .../cosmic-applet-network/src/ui/toggles.rs | 71 --- applets/cosmic-applet-network/src/widgets.rs | 5 - .../src/widgets/settings_entry.rs | 211 ------- debian/rules | 6 + justfile | 6 +- 22 files changed, 1277 insertions(+), 1172 deletions(-) create mode 100644 applets/cosmic-applet-network/i18n.toml create mode 100644 applets/cosmic-applet-network/i18n/en/cosmic_applet_network.ftl create mode 100644 applets/cosmic-applet-network/src/app.rs create mode 100644 applets/cosmic-applet-network/src/config.rs create mode 100644 applets/cosmic-applet-network/src/localize.rs create mode 100644 applets/cosmic-applet-network/src/network_manager/available_wifi.rs create mode 100644 applets/cosmic-applet-network/src/network_manager/current_networks.rs create mode 100644 applets/cosmic-applet-network/src/network_manager/mod.rs delete mode 100644 applets/cosmic-applet-network/src/task.rs delete mode 100644 applets/cosmic-applet-network/src/ui.rs delete mode 100644 applets/cosmic-applet-network/src/ui/available_wifi.rs delete mode 100644 applets/cosmic-applet-network/src/ui/current_networks.rs delete mode 100644 applets/cosmic-applet-network/src/ui/toggles.rs delete mode 100644 applets/cosmic-applet-network/src/widgets.rs delete mode 100644 applets/cosmic-applet-network/src/widgets/settings_entry.rs diff --git a/Cargo.lock b/Cargo.lock index 0bbe16a4..a464eaea 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -117,15 +117,6 @@ dependencies = [ "event-listener", ] -[[package]] -name = "async-oneshot" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ec7c75bcbcb0139e9177f30692fd617405ca4e0c27802e128d53171f7042e2c" -dependencies = [ - "futures-micro", -] - [[package]] name = "async-recursion" version = "0.3.2" @@ -295,7 +286,7 @@ dependencies = [ "js-sys", "num-integer", "num-traits", - "time 0.1.44", + "time", "wasm-bindgen", "winapi", ] @@ -348,24 +339,6 @@ dependencies = [ "xdg", ] -[[package]] -name = "cosmic-applet-network" -version = "0.1.0" -dependencies = [ - "cosmic-dbus-networkmanager", - "futures-util", - "gtk4", - "itertools", - "libadwaita", - "libcosmic", - "libcosmic-applet", - "once_cell", - "relm4-macros", - "slotmap", - "tokio", - "zbus 2.3.2", -] - [[package]] name = "cosmic-applet-notifications" version = "0.1.0" @@ -436,19 +409,6 @@ dependencies = [ "zvariant", ] -[[package]] -name = "cosmic-dbus-networkmanager" -version = "0.1.0" -source = "git+https://github.com/pop-os/dbus-settings-bindings#d3b16fda4deba1ed4b95ce77943fb4c0db7a5773" -dependencies = [ - "bitflags", - "derive_builder", - "procfs", - "time 0.3.13", - "zbus 2.3.2", - "zvariant", -] - [[package]] name = "cosmic-panel-button" version = "0.1.0" @@ -504,41 +464,6 @@ dependencies = [ "libc", ] -[[package]] -name = "darling" -version = "0.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4529658bdda7fd6769b8614be250cdcfc3aeb0ee72fe66f9e41e5e5eb73eac02" -dependencies = [ - "darling_core", - "darling_macro", -] - -[[package]] -name = "darling_core" -version = "0.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "649c91bc01e8b1eac09fb91e8dbc7d517684ca6be8ebc75bb9cafc894f9fdb6f" -dependencies = [ - "fnv", - "ident_case", - "proc-macro2", - "quote", - "strsim", - "syn", -] - -[[package]] -name = "darling_macro" -version = "0.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddfc69c5bfcbd2fc09a0f38451d2daf0e372e367986a83906d1b0dbc88134fb5" -dependencies = [ - "darling_core", - "quote", - "syn", -] - [[package]] name = "dashmap" version = "5.3.4" @@ -562,37 +487,6 @@ dependencies = [ "syn", ] -[[package]] -name = "derive_builder" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d07adf7be193b71cc36b193d0f5fe60b918a3a9db4dad0449f57bcfd519704a3" -dependencies = [ - "derive_builder_macro", -] - -[[package]] -name = "derive_builder_core" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f91d4cfa921f1c05904dc3c57b4a32c38aed3340cce209f3a6fd1478babafc4" -dependencies = [ - "darling", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "derive_builder_macro" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f0314b72bed045f3a68671b3c86328386762c93f82d98c65c3cb5e5f573dd68" -dependencies = [ - "derive_builder_core", - "syn", -] - [[package]] name = "digest" version = "0.9.0" @@ -637,12 +531,6 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" -[[package]] -name = "either" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" - [[package]] name = "enumflags2" version = "0.7.5" @@ -755,25 +643,6 @@ dependencies = [ "thiserror", ] -[[package]] -name = "flume" -version = "0.10.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1657b4441c3403d9f7b3409e47575237dac27b1b5726df654a6ecbf92f0f7577" -dependencies = [ - "futures-core", - "futures-sink", - "nanorand", - "pin-project", - "spin", -] - -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - [[package]] name = "futures" version = "0.3.23" @@ -848,15 +717,6 @@ dependencies = [ "syn", ] -[[package]] -name = "futures-micro" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b460264b3593d68b16a7bc35f7bc226ddfebdf9a1c8db1ed95d5cc6b7168c826" -dependencies = [ - "pin-project-lite", -] - [[package]] name = "futures-sink" version = "0.3.23" @@ -959,10 +819,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6" dependencies = [ "cfg-if", - "js-sys", "libc", "wasi 0.11.0+wasi-snapshot-preview1", - "wasm-bindgen", ] [[package]] @@ -1274,12 +1132,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "ident_case" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" - [[package]] name = "instant" version = "0.1.12" @@ -1319,15 +1171,6 @@ dependencies = [ "windows-sys", ] -[[package]] -name = "itertools" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3" -dependencies = [ - "either", -] - [[package]] name = "itoa" version = "1.0.3" @@ -1395,7 +1238,6 @@ dependencies = [ "cascade", "gtk4", "libadwaita", - "libcosmic-widgets", "once_cell", "xdg", ] @@ -1410,16 +1252,6 @@ dependencies = [ "relm4-macros", ] -[[package]] -name = "libcosmic-widgets" -version = "0.1.0" -source = "git+https://github.com/pop-os/libcosmic#aaffa76b982c1c92c79317a6813bfe45bc9c885d" -dependencies = [ - "relm4", - "relm4-macros", - "tracker", -] - [[package]] name = "libloading" version = "0.7.3" @@ -1509,15 +1341,6 @@ dependencies = [ "windows-sys", ] -[[package]] -name = "nanorand" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3" -dependencies = [ - "getrandom", -] - [[package]] name = "nix" version = "0.23.1" @@ -1586,15 +1409,6 @@ dependencies = [ "libc", ] -[[package]] -name = "num_threads" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44" -dependencies = [ - "libc", -] - [[package]] name = "objc" version = "0.2.7" @@ -1776,26 +1590,6 @@ dependencies = [ "siphasher", ] -[[package]] -name = "pin-project" -version = "1.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad29a609b6bcd67fee905812e544992d216af9d755757c05ed2d0e15a74c6ecc" -dependencies = [ - "pin-project-internal", -] - -[[package]] -name = "pin-project-internal" -version = "1.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "pin-project-lite" version = "0.2.9" @@ -1887,19 +1681,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "procfs" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0941606b9934e2d98a3677759a971756eb821f75764d0e0d26946d08e74d9104" -dependencies = [ - "bitflags", - "byteorder", - "hex", - "lazy_static", - "libc", -] - [[package]] name = "quick-error" version = "1.2.3" @@ -1991,22 +1772,6 @@ version = "0.6.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" -[[package]] -name = "relm4" -version = "0.5.0-beta.1" -source = "git+https://github.com/Relm4/Relm4.git?branch=next#55231e300ce9e2b51d66f078b7759eba0759537c" -dependencies = [ - "async-broadcast", - "async-oneshot", - "flume", - "futures", - "gtk4", - "log", - "once_cell", - "tokio", - "tracing", -] - [[package]] name = "relm4-macros" version = "0.5.0-beta.1" @@ -2273,15 +2038,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "spin" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f6002a767bff9e83f8eeecf883ecb8011875a21ae8da43bffb817a57e78cc09" -dependencies = [ - "lock_api", -] - [[package]] name = "static_assertions" version = "1.1.0" @@ -2372,16 +2128,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "time" -version = "0.3.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db76ff9fa4b1458b3c7f077f3ff9887394058460d21e634355b273aaf11eea45" -dependencies = [ - "libc", - "num_threads", -] - [[package]] name = "tinystr" version = "0.3.4" @@ -2436,7 +2182,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2fce9567bd60a67d08a16488756721ba392f24f29006402881e43b19aac64307" dependencies = [ "cfg-if", - "log", "pin-project-lite", "tracing-attributes", "tracing-core", @@ -2462,26 +2207,6 @@ dependencies = [ "once_cell", ] -[[package]] -name = "tracker" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e66d89d37f24af7a53e394a412441c803df73f1a5adfcb3b9c37a2e0a75392eb" -dependencies = [ - "tracker-macros", -] - -[[package]] -name = "tracker-macros" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ca57dc00ed70e0acce16b1a4994ba9caf7718b9247382285d5e5192d3f6cd8d" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "type-map" version = "0.4.0" diff --git a/Cargo.toml b/Cargo.toml index a0ece26b..9131cd67 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,5 @@ [workspace] members = [ - "applets/cosmic-applet-network", "applets/cosmic-applet-notifications", "applets/cosmic-applet-power", "applets/cosmic-applet-status-area", @@ -10,6 +9,7 @@ members = [ "libcosmic-applet", ] exclude = [ + "applets/cosmic-applet-network", "applets/cosmic-applet-graphics", "applets/cosmic-applet-workspaces", "applets/cosmic-applet-battery", diff --git a/applets/cosmic-applet-network/Cargo.lock b/applets/cosmic-applet-network/Cargo.lock index ceab965a..19602b24 100644 --- a/applets/cosmic-applet-network/Cargo.lock +++ b/applets/cosmic-applet-network/Cargo.lock @@ -179,17 +179,6 @@ dependencies = [ "futures-lite", ] -[[package]] -name = "async-recursion" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7d78656ba01f1b93024b7c3a0467f1608e4be67d725749fdcd7d2c7678fd7a2" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "async-recursion" version = "1.0.0" @@ -218,6 +207,17 @@ dependencies = [ "syn", ] +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + [[package]] name = "autocfg" version = "1.1.0" @@ -317,7 +317,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19457a0da465234abd76134a5c2a910c14bd3c5558463e4396ab9a37a328e465" dependencies = [ "log", - "nix 0.25.1", + "nix", "slotmap", "thiserror", "vec_map", @@ -459,28 +459,34 @@ version = "0.1.0" dependencies = [ "cosmic-dbus-networkmanager", "cosmic-panel-config", + "futures", "futures-util", + "i18n-embed", + "i18n-embed-fl", + "iced", "iced_sctk", "itertools", "libcosmic", + "log", "once_cell", - "relm4-macros", + "pretty_env_logger", + "rust-embed", "slotmap", "smithay-client-toolkit", "tokio", - "zbus 3.6.1", + "zbus", ] [[package]] name = "cosmic-dbus-networkmanager" version = "0.1.0" -source = "git+https://github.com/pop-os/dbus-settings-bindings#d3b16fda4deba1ed4b95ce77943fb4c0db7a5773" +source = "git+https://github.com/pop-os/dbus-settings-bindings#958f163ed9f3922c347e9fd91edd2cd3e22043ac" dependencies = [ "bitflags", "derive_builder", "procfs", "time", - "zbus 2.3.2", + "zbus", "zvariant", ] @@ -684,6 +690,19 @@ dependencies = [ "syn", ] +[[package]] +name = "dashmap" +version = "5.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "907076dfda823b0b36d2a1bb5f90c96660a5bbcd7729e10727f07858f22c4edc" +dependencies = [ + "cfg-if", + "hashbrown", + "lock_api", + "once_cell", + "parking_lot_core 0.9.5", +] + [[package]] name = "data-url" version = "0.1.1" @@ -706,18 +725,18 @@ dependencies = [ [[package]] name = "derive_builder" -version = "0.11.2" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d07adf7be193b71cc36b193d0f5fe60b918a3a9db4dad0449f57bcfd519704a3" +checksum = "8d67778784b508018359cbc8696edb3db78160bab2c2a28ba7f56ef6932997f8" dependencies = [ "derive_builder_macro", ] [[package]] name = "derive_builder_core" -version = "0.11.2" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f91d4cfa921f1c05904dc3c57b4a32c38aed3340cce209f3a6fd1478babafc4" +checksum = "c11bdc11a0c47bc7d37d582b5285da6849c96681023680b906673c5707af7b0f" dependencies = [ "darling 0.14.2", "proc-macro2", @@ -727,9 +746,9 @@ dependencies = [ [[package]] name = "derive_builder_macro" -version = "0.11.2" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f0314b72bed045f3a68671b3c86328386762c93f82d98c65c3cb5e5f573dd68" +checksum = "ebcda35c7a396850a55ffeac740804b40ffec779b98fffbb1738f4033f0ee79e" dependencies = [ "derive_builder_core", "syn", @@ -777,6 +796,17 @@ dependencies = [ "winapi", ] +[[package]] +name = "displaydoc" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3bf95dc3f046b9da4f2d51833c0d3547d8564ef6910f5c1ed130306a75b92886" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "dlib" version = "0.5.0" @@ -868,6 +898,40 @@ dependencies = [ "syn", ] +[[package]] +name = "env_logger" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36" +dependencies = [ + "atty", + "humantime", + "log", + "regex", + "termcolor", +] + +[[package]] +name = "errno" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" +dependencies = [ + "errno-dragonfly", + "libc", + "winapi", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] + [[package]] name = "euclid" version = "0.22.7" @@ -932,6 +996,50 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" +[[package]] +name = "fluent" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61f69378194459db76abd2ce3952b790db103ceb003008d3d50d97c41ff847a7" +dependencies = [ + "fluent-bundle", + "unic-langid", +] + +[[package]] +name = "fluent-bundle" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e242c601dec9711505f6d5bbff5bedd4b61b2469f2e8bb8e57ee7c9747a87ffd" +dependencies = [ + "fluent-langneg", + "fluent-syntax", + "intl-memoizer", + "intl_pluralrules", + "rustc-hash", + "self_cell", + "smallvec", + "unic-langid", +] + +[[package]] +name = "fluent-langneg" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c4ad0989667548f06ccd0e306ed56b61bd4d35458d54df5ec7587c0e8ed5e94" +dependencies = [ + "unic-langid", +] + +[[package]] +name = "fluent-syntax" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0abed97648395c902868fee9026de96483933faa54ea3b40d652f7dfe61ca78" +dependencies = [ + "thiserror", +] + [[package]] name = "flume" version = "0.10.14" @@ -1356,10 +1464,88 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df" +[[package]] +name = "humantime" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f" +dependencies = [ + "quick-error", +] + +[[package]] +name = "i18n-config" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b62affcd43abfb51f3cbd8736f9407908dc5b44fc558a9be07460bbfd104d983" +dependencies = [ + "log", + "serde", + "serde_derive", + "thiserror", + "toml", + "unic-langid", +] + +[[package]] +name = "i18n-embed" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7f21ed76e44de8ac3dfa36bb37ab2e6480be0dc75c612474949be1f3cb2c253" +dependencies = [ + "fluent", + "fluent-langneg", + "fluent-syntax", + "i18n-embed-impl", + "intl-memoizer", + "lazy_static", + "locale_config", + "log", + "parking_lot 0.12.1", + "rust-embed", + "thiserror", + "unic-langid", + "walkdir", +] + +[[package]] +name = "i18n-embed-fl" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9420a9718ef9d0ab727840a398e25408ea0daff9ba3c681707ba05485face98e" +dependencies = [ + "dashmap", + "find-crate", + "fluent", + "fluent-syntax", + "i18n-config", + "i18n-embed", + "lazy_static", + "proc-macro-error", + "proc-macro2", + "quote", + "strsim 0.10.0", + "syn", + "unic-langid", +] + +[[package]] +name = "i18n-embed-impl" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0db2330e035808eb064afb67e6743ddce353763af3e0f2bdfc2476e00ce76136" +dependencies = [ + "find-crate", + "i18n-config", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "iced" version = "0.5.2" -source = "git+https://github.com/pop-os/iced?branch=sctk-cosmic#d6cd9a8f106dc4b68595c540ca4ffa466aa15a60" +source = "git+https://github.com/pop-os/iced.git?branch=sctk-cosmic#d6cd9a8f106dc4b68595c540ca4ffa466aa15a60" dependencies = [ "iced_core", "iced_futures", @@ -1375,7 +1561,7 @@ dependencies = [ [[package]] name = "iced_core" version = "0.6.1" -source = "git+https://github.com/pop-os/iced?branch=sctk-cosmic#d6cd9a8f106dc4b68595c540ca4ffa466aa15a60" +source = "git+https://github.com/pop-os/iced.git?branch=sctk-cosmic#d6cd9a8f106dc4b68595c540ca4ffa466aa15a60" dependencies = [ "bitflags", "palette", @@ -1385,10 +1571,11 @@ dependencies = [ [[package]] name = "iced_futures" version = "0.5.1" -source = "git+https://github.com/pop-os/iced?branch=sctk-cosmic#d6cd9a8f106dc4b68595c540ca4ffa466aa15a60" +source = "git+https://github.com/pop-os/iced.git?branch=sctk-cosmic#d6cd9a8f106dc4b68595c540ca4ffa466aa15a60" dependencies = [ "futures", "log", + "tokio", "wasm-bindgen-futures", "wasm-timer", ] @@ -1396,7 +1583,7 @@ dependencies = [ [[package]] name = "iced_glow" version = "0.4.1" -source = "git+https://github.com/pop-os/iced?branch=sctk-cosmic#d6cd9a8f106dc4b68595c540ca4ffa466aa15a60" +source = "git+https://github.com/pop-os/iced.git?branch=sctk-cosmic#d6cd9a8f106dc4b68595c540ca4ffa466aa15a60" dependencies = [ "bytemuck", "euclid", @@ -1411,7 +1598,7 @@ dependencies = [ [[package]] name = "iced_graphics" version = "0.4.0" -source = "git+https://github.com/pop-os/iced?branch=sctk-cosmic#d6cd9a8f106dc4b68595c540ca4ffa466aa15a60" +source = "git+https://github.com/pop-os/iced.git?branch=sctk-cosmic#d6cd9a8f106dc4b68595c540ca4ffa466aa15a60" dependencies = [ "bitflags", "bytemuck", @@ -1431,7 +1618,7 @@ dependencies = [ [[package]] name = "iced_lazy" version = "0.2.0" -source = "git+https://github.com/pop-os/iced?branch=sctk-cosmic#d6cd9a8f106dc4b68595c540ca4ffa466aa15a60" +source = "git+https://github.com/pop-os/iced.git?branch=sctk-cosmic#d6cd9a8f106dc4b68595c540ca4ffa466aa15a60" dependencies = [ "iced_native", "ouroboros", @@ -1440,7 +1627,7 @@ dependencies = [ [[package]] name = "iced_native" version = "0.6.1" -source = "git+https://github.com/pop-os/iced?branch=sctk-cosmic#d6cd9a8f106dc4b68595c540ca4ffa466aa15a60" +source = "git+https://github.com/pop-os/iced.git?branch=sctk-cosmic#d6cd9a8f106dc4b68595c540ca4ffa466aa15a60" dependencies = [ "iced_core", "iced_futures", @@ -1473,7 +1660,7 @@ dependencies = [ [[package]] name = "iced_style" version = "0.5.0" -source = "git+https://github.com/pop-os/iced?branch=sctk-cosmic#d6cd9a8f106dc4b68595c540ca4ffa466aa15a60" +source = "git+https://github.com/pop-os/iced.git?branch=sctk-cosmic#d6cd9a8f106dc4b68595c540ca4ffa466aa15a60" dependencies = [ "iced_core", "once_cell", @@ -1483,7 +1670,7 @@ dependencies = [ [[package]] name = "iced_wgpu" version = "0.6.1" -source = "git+https://github.com/pop-os/iced?branch=sctk-cosmic#d6cd9a8f106dc4b68595c540ca4ffa466aa15a60" +source = "git+https://github.com/pop-os/iced.git?branch=sctk-cosmic#d6cd9a8f106dc4b68595c540ca4ffa466aa15a60" dependencies = [ "bitflags", "bytemuck", @@ -1544,6 +1731,31 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "intl-memoizer" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c310433e4a310918d6ed9243542a6b83ec1183df95dff8f23f87bb88a264a66f" +dependencies = [ + "type-map", + "unic-langid", +] + +[[package]] +name = "intl_pluralrules" +version = "7.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "078ea7b7c29a2b4df841a7f6ac8775ff6074020c6776d48491ce2268e068f972" +dependencies = [ + "unic-langid", +] + +[[package]] +name = "io-lifetimes" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ce5ef949d49ee85593fc4d3f3f95ad61657076395cbbce23e2121fc5542074" + [[package]] name = "io-lifetimes" version = "1.0.0-rc1" @@ -1643,7 +1855,7 @@ checksum = "db6d7e329c562c5dfab7a46a2afabc8b987ab9a4834c9d1ca04dc54c1546cef8" [[package]] name = "libcosmic" version = "0.1.0" -source = "git+https://github.com/pop-os/libcosmic/?branch=sctk-cosmic-design-system#0f857b18ea0987a192b99a2dc34d29b9e41e5bf8" +source = "git+https://github.com/pop-os/libcosmic/?branch=master#91e826d8eacd03d8e7ef5b1a767b2164f8719ed0" dependencies = [ "apply", "cosmic-panel-config", @@ -1677,6 +1889,25 @@ version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" +[[package]] +name = "linux-raw-sys" +version = "0.0.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4d2456c373231a208ad294c33dc5bff30051eafd954cd4caae83a712b12854d" + +[[package]] +name = "locale_config" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d2c35b16f4483f6c26f0e4e9550717a2f6575bcd6f12a53ff0c490a94a6934" +dependencies = [ + "lazy_static", + "objc", + "objc-foundation", + "regex", + "winapi", +] + [[package]] name = "lock_api" version = "0.4.9" @@ -1829,19 +2060,6 @@ dependencies = [ "getrandom", ] -[[package]] -name = "nix" -version = "0.23.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f3790c00a0150112de0f4cd161e3d7fc4b2d8a5542ffc35f099a2562aecb35c" -dependencies = [ - "bitflags", - "cc", - "cfg-if", - "libc", - "memoffset 0.6.5", -] - [[package]] name = "nix" version = "0.25.1" @@ -1916,6 +2134,17 @@ dependencies = [ "objc_exception", ] +[[package]] +name = "objc-foundation" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1add1b659e36c9607c7aab864a76c7a4c2760cd0cd2e120f3fb8b952c7e22bf9" +dependencies = [ + "block", + "objc", + "objc_id", +] + [[package]] name = "objc_exception" version = "0.1.2" @@ -1925,6 +2154,15 @@ dependencies = [ "cc", ] +[[package]] +name = "objc_id" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c92d4ddb4bd7b50d730c215ff871754d0da6b2178849f8a2a2ab69712d0c073b" +dependencies = [ + "objc", +] + [[package]] name = "once_cell" version = "1.16.0" @@ -1950,16 +2188,6 @@ dependencies = [ "hashbrown", ] -[[package]] -name = "ordered-stream" -version = "0.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44630c059eacfd6e08bdaa51b1db2ce33119caa4ddc1235e923109aa5f25ccb1" -dependencies = [ - "futures-core", - "pin-project-lite", -] - [[package]] name = "ordered-stream" version = "0.1.2" @@ -2200,6 +2428,16 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +[[package]] +name = "pretty_env_logger" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "926d36b9553851b8b0005f1275891b392ee4d2d833852c417ed025477350fb9d" +dependencies = [ + "env_logger", + "log", +] + [[package]] name = "proc-macro-crate" version = "1.2.1" @@ -2246,15 +2484,15 @@ dependencies = [ [[package]] name = "procfs" -version = "0.12.0" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0941606b9934e2d98a3677759a971756eb821f75764d0e0d26946d08e74d9104" +checksum = "2dfb6451c91904606a1abe93e83a8ec851f45827fa84273f256ade45dc095818" dependencies = [ "bitflags", "byteorder", "hex", "lazy_static", - "libc", + "rustix", ] [[package]] @@ -2263,6 +2501,12 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74605f360ce573babfe43964cbe520294dcb081afbf8c108fc6e23036b4da2df" +[[package]] +name = "quick-error" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" + [[package]] name = "quick-xml" version = "0.23.1" @@ -2392,16 +2636,6 @@ version = "0.6.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" -[[package]] -name = "relm4-macros" -version = "0.5.0-beta.1" -source = "git+https://github.com/Relm4/Relm4.git?branch=next#55231e300ce9e2b51d66f078b7759eba0759537c" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "remove_dir_all" version = "0.5.3" @@ -2462,6 +2696,40 @@ dependencies = [ "xmlparser", ] +[[package]] +name = "rust-embed" +version = "6.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "283ffe2f866869428c92e0d61c2f35dfb4355293cdfdc48f49e895c15f1333d1" +dependencies = [ + "rust-embed-impl", + "rust-embed-utils", + "walkdir", +] + +[[package]] +name = "rust-embed-impl" +version = "6.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31ab23d42d71fb9be1b643fe6765d292c5e14d46912d13f3ae2815ca048ea04d" +dependencies = [ + "proc-macro2", + "quote", + "rust-embed-utils", + "syn", + "walkdir", +] + +[[package]] +name = "rust-embed-utils" +version = "7.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1669d81dfabd1b5f8e2856b8bbe146c6192b0ba22162edc738ac0a5de18f054" +dependencies = [ + "sha2", + "walkdir", +] + [[package]] name = "rust-ini" version = "0.18.0" @@ -2478,6 +2746,20 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" +[[package]] +name = "rustix" +version = "0.35.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "727a1a6d65f786ec22df8a81ca3121107f235970dc1705ed681d3e6e8b9cd5f9" +dependencies = [ + "bitflags", + "errno", + "io-lifetimes 0.7.5", + "libc", + "linux-raw-sys", + "windows-sys 0.42.0", +] + [[package]] name = "rustybuzz" version = "0.4.0" @@ -2503,6 +2785,15 @@ dependencies = [ "bytemuck", ] +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + [[package]] name = "scoped-tls" version = "1.0.1" @@ -2521,6 +2812,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +[[package]] +name = "self_cell" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ef965a420fe14fdac7dd018862966a4c14094f900e1650bbc71ddd7d580c8af" + [[package]] name = "serde" version = "1.0.149" @@ -2552,15 +2849,6 @@ dependencies = [ "syn", ] -[[package]] -name = "sha1" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1da05c97445caa12d05e848c4a4fcbbea29e748ac28f7e80e9b010392063770" -dependencies = [ - "sha1_smol", -] - [[package]] name = "sha1" version = "0.10.5" @@ -2573,10 +2861,15 @@ dependencies = [ ] [[package]] -name = "sha1_smol" -version = "1.0.0" +name = "sha2" +version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012" +checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] [[package]] name = "signal-hook-registry" @@ -2643,7 +2936,7 @@ dependencies = [ "lazy_static", "log", "memmap2 0.5.8", - "nix 0.25.1", + "nix", "pkg-config", "thiserror", "wayland-backend", @@ -2837,6 +3130,15 @@ dependencies = [ "safe_arch", ] +[[package]] +name = "tinystr" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8aeafdfd935e4a7fe16a91ab711fa52d54df84f9c8f7ca5837a9d1d902ef4c2" +dependencies = [ + "displaydoc", +] + [[package]] name = "tokio" version = "1.23.0" @@ -2932,6 +3234,15 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "type-map" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6d3364c5e96cb2ad1603037ab253ddd34d7fb72a58bdddf4b7350760fc69a46" +dependencies = [ + "rustc-hash", +] + [[package]] name = "typenum" version = "1.16.0" @@ -2948,6 +3259,25 @@ dependencies = [ "winapi", ] +[[package]] +name = "unic-langid" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "398f9ad7239db44fd0f80fe068d12ff22d78354080332a5077dc6f52f14dcf2f" +dependencies = [ + "unic-langid-impl", +] + +[[package]] +name = "unic-langid-impl" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e35bfd2f2b8796545b55d7d3fd3e89a0613f68a0d1c8bc28cb7ff96b411a35ff" +dependencies = [ + "serde", + "tinystr", +] + [[package]] name = "unicode-bidi" version = "0.3.8" @@ -3053,6 +3383,17 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca" +[[package]] +name = "walkdir" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" +dependencies = [ + "same-file", + "winapi", + "winapi-util", +] + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -3148,8 +3489,8 @@ checksum = "f3f96c52ca34b33e6bb55497327428be54dad648973aa5d3d1e02982d2fcc298" dependencies = [ "cc", "downcast-rs", - "io-lifetimes", - "nix 0.25.1", + "io-lifetimes 1.0.0-rc1", + "nix", "scoped-tls", "smallvec", "wayland-sys", @@ -3164,7 +3505,7 @@ dependencies = [ "bitflags", "futures-channel", "futures-core", - "nix 0.25.1", + "nix", "thiserror", "wayland-backend", "wayland-scanner", @@ -3176,7 +3517,7 @@ version = "0.30.0-beta.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa5ce1bbc13a3d806648974cba1d53481fea2ac29483eb147e4cce1250fc5263" dependencies = [ - "nix 0.25.1", + "nix", "wayland-client", "xcursor", ] @@ -3555,46 +3896,6 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec7a2a501ed189703dba8b08142f057e887dfc4b2cc4db2d343ac6376ba3e0b9" -[[package]] -name = "zbus" -version = "2.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d8f1a037b2c4a67d9654dc7bdfa8ff2e80555bbefdd3c1833c1d1b27c963a6b" -dependencies = [ - "async-broadcast", - "async-channel", - "async-executor", - "async-io", - "async-lock", - "async-recursion 0.3.2", - "async-task", - "async-trait", - "byteorder", - "derivative", - "dirs", - "enumflags2", - "event-listener", - "futures-core", - "futures-sink", - "futures-util", - "hex", - "lazy_static", - "nix 0.23.2", - "once_cell", - "ordered-stream 0.0.1", - "rand", - "serde", - "serde_repr", - "sha1 0.6.1", - "static_assertions", - "tracing", - "uds_windows", - "winapi", - "zbus_macros 2.3.2", - "zbus_names", - "zvariant", -] - [[package]] name = "zbus" version = "3.6.1" @@ -3606,7 +3907,7 @@ dependencies = [ "async-executor", "async-io", "async-lock", - "async-recursion 1.0.0", + "async-recursion", "async-task", "async-trait", "byteorder", @@ -3618,35 +3919,22 @@ dependencies = [ "futures-sink", "futures-util", "hex", - "nix 0.25.1", + "nix", "once_cell", - "ordered-stream 0.1.2", + "ordered-stream", "rand", "serde", "serde_repr", - "sha1 0.10.5", + "sha1", "static_assertions", "tracing", "uds_windows", "winapi", - "zbus_macros 3.6.1", + "zbus_macros", "zbus_names", "zvariant", ] -[[package]] -name = "zbus_macros" -version = "2.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f8fb5186d1c87ae88cf234974c240671238b4a679158ad3b94ec465237349a6" -dependencies = [ - "proc-macro-crate", - "proc-macro2", - "quote", - "regex", - "syn", -] - [[package]] name = "zbus_macros" version = "3.6.1" diff --git a/applets/cosmic-applet-network/Cargo.toml b/applets/cosmic-applet-network/Cargo.toml index c10f090e..0c665b56 100644 --- a/applets/cosmic-applet-network/Cargo.toml +++ b/applets/cosmic-applet-network/Cargo.toml @@ -5,15 +5,28 @@ edition = "2021" license = "GPL-3.0-or-later" [dependencies] +once_cell = "1.16.0" cosmic-dbus-networkmanager = { git = "https://github.com/pop-os/dbus-settings-bindings" } futures-util = "0.3.21" -gtk4 = { git = "https://github.com/gtk-rs/gtk4-rs" } -adw = { git = "https://gitlab.gnome.org/World/Rust/libadwaita-rs", package = "libadwaita"} -libcosmic = { git = "https://github.com/pop-os/libcosmic", default-features = false, features = ["widgets"] } +libcosmic = { git = "https://github.com/pop-os/libcosmic/", branch = "master", default-features = false, features = ["wayland", "applet"] } +cosmic-panel-config = {git = "https://github.com/pop-os/cosmic-panel", default-features = false } +iced_sctk = { git = "https://github.com/pop-os/iced-sctk" } +sctk = { package = "smithay-client-toolkit", git = "https://github.com/Smithay/client-toolkit", version = "0.16" } +futures = "0.3" +zbus = { version = "3.5", no-default-features = true } +log = "0.4" +pretty_env_logger = "0.4" +# Application i18n +i18n-embed = { version = "0.13.4", features = ["fluent-system", "desktop-requester"] } +i18n-embed-fl = "0.6.4" +rust-embed = "6.3.0" itertools = "0.10.3" -once_cell = "1.9.0" -relm4-macros = { git = "https://github.com/Relm4/Relm4.git", branch = "next" } slotmap = "1.0.6" tokio = { version = "1.15.0", features = ["full"] } -zbus = "2.0.1" -libcosmic-applet = { path = "../../libcosmic-applet" } + +[dependencies.iced] +git = "https://github.com/pop-os/iced.git" +branch = "sctk-cosmic" +# path = "../iced" +default-features = false +features = ["image", "svg", "tokio"] \ No newline at end of file diff --git a/applets/cosmic-applet-network/i18n.toml b/applets/cosmic-applet-network/i18n.toml new file mode 100644 index 00000000..05c50ba2 --- /dev/null +++ b/applets/cosmic-applet-network/i18n.toml @@ -0,0 +1,4 @@ +fallback_language = "en" + +[fluent] +assets_dir = "i18n" \ No newline at end of file diff --git a/applets/cosmic-applet-network/i18n/en/cosmic_applet_network.ftl b/applets/cosmic-applet-network/i18n/en/cosmic_applet_network.ftl new file mode 100644 index 00000000..e19faeed --- /dev/null +++ b/applets/cosmic-applet-network/i18n/en/cosmic_applet_network.ftl @@ -0,0 +1,7 @@ +network = Network +airplane-mode = Airplane mode +wifi = Wi-Fi +ipv4 = IPv4 Address +ipv6 = IPv6 Address +mac = MAC +megabits-per-second = Mbps diff --git a/applets/cosmic-applet-network/src/app.rs b/applets/cosmic-applet-network/src/app.rs new file mode 100644 index 00000000..23d9724c --- /dev/null +++ b/applets/cosmic-applet-network/src/app.rs @@ -0,0 +1,344 @@ +use cosmic::{ + applet::CosmicAppletHelper, + iced::{ + executor, + widget::{column, container, row, scrollable, text}, + Alignment, Application, Color, Command, Length, Subscription, + }, + iced_native::window, + iced_style::{application, svg}, + theme::{Button, Svg}, + widget::{button, horizontal_rule, icon, list_column, toggler}, + Element, Theme, +}; +use futures::channel::mpsc::UnboundedSender; +use iced_sctk::{ + application::SurfaceIdWrapper, + commands::popup::{destroy_popup, get_popup}, +}; + +use crate::{ + config, fl, + network_manager::{ + available_wifi::AccessPoint, current_networks::ActiveConnectionInfo, + network_manager_subscription, NetworkManagerEvent, NetworkManagerRequest, + }, +}; + +pub fn run() -> cosmic::iced::Result { + let helper = CosmicAppletHelper::default(); + CosmicNetworkApplet::run(helper.window_settings()) +} + +#[derive(Clone, Default)] +struct CosmicNetworkApplet { + icon_name: String, + theme: Theme, + popup: Option, + id_ctr: u32, + applet_helper: CosmicAppletHelper, + // STATE + airplane_mode: bool, + wifi: bool, + wireless_access_points: Vec, + active_conns: Vec, + nm_sender: Option>, +} + +impl CosmicNetworkApplet { + fn update_icon_name(&mut self) { + self.icon_name = self + .active_conns + .iter() + .fold("network-offline-symbolic", |icon_name, conn| { + match (icon_name, conn) { + ("network-offline-symbolic", ActiveConnectionInfo::WiFi { .. }) => { + "network-wireless-symbolic" + } + ( + "network-offline-symbolic", + ActiveConnectionInfo::Wired { .. }, + ) + | ( + "network-wireless-symbolic", + ActiveConnectionInfo::Wired { .. }, + ) => "network-wired-symbolic", + (_, ActiveConnectionInfo::Vpn { .. }) => "network-vpn-symbolic", + _ => icon_name, + } + }) + .to_string() + } +} + +#[derive(Debug, Clone)] +enum Message { + TogglePopup, + ToggleAirplaneMode(bool), + ToggleWiFi(bool), + Errored(String), + Ignore, + NetworkManagerEvent(NetworkManagerEvent), + SelectWirelessAccessPoint(String), +} + +impl Application for CosmicNetworkApplet { + type Message = Message; + type Theme = Theme; + type Executor = executor::Default; + type Flags = (); + + fn new(_flags: ()) -> (Self, Command) { + ( + CosmicNetworkApplet { + icon_name: "network-offline-symbolic".to_string(), + ..Default::default() + }, + Command::none(), + ) + } + + fn title(&self) -> String { + config::APP_ID.to_string() + } + + fn update(&mut self, message: Message) -> Command { + match message { + Message::TogglePopup => { + if let Some(p) = self.popup.take() { + return destroy_popup(p); + } else { + // TODO request update of state maybe + self.id_ctr += 1; + let new_id = window::Id::new(self.id_ctr); + self.popup.replace(new_id); + + let popup_settings = self.applet_helper.get_popup_settings( + window::Id::new(0), + new_id, + (420, 600), + None, + None, + ); + return get_popup(popup_settings); + } + } + Message::Errored(_) => todo!(), + Message::Ignore => {} + Message::ToggleAirplaneMode(enabled) => { + self.airplane_mode = enabled; + // TODO apply changes + } + Message::ToggleWiFi(enabled) => { + self.wifi = enabled; + if let Some(tx) = self.nm_sender.as_mut() { + let _ = tx.unbounded_send(NetworkManagerRequest::SetWiFi(enabled)); + } + } + Message::NetworkManagerEvent(event) => match event { + NetworkManagerEvent::Init { + sender, + wireless_access_points, + active_conns, + wifi_enabled, + airplane_mode, + } => { + self.nm_sender.replace(sender); + self.wireless_access_points = wireless_access_points; + self.active_conns = active_conns; + self.wifi = wifi_enabled; + self.airplane_mode = airplane_mode; + self.update_icon_name(); + } + NetworkManagerEvent::WiFiEnabled(enabled) => { + self.wifi = enabled; + } + NetworkManagerEvent::WirelessAccessPoints(access_points) => { + self.wireless_access_points = access_points; + } + NetworkManagerEvent::ActiveConns(conns) => { + self.active_conns = conns; + self.update_icon_name(); + } + NetworkManagerEvent::RequestResponse { wireless_access_points, active_conns, wifi_enabled, success, ..} => { + if success { + self.wireless_access_points = wireless_access_points; + self.active_conns = active_conns; + self.wifi = wifi_enabled; + self.update_icon_name(); + } + }, + }, + Message::SelectWirelessAccessPoint(ssid) => { + if let Some(tx) = self.nm_sender.as_ref() { + let _ = tx.unbounded_send(NetworkManagerRequest::SelectAccessPoint(ssid)); + } + } + } + Command::none() + } + fn view(&self, id: SurfaceIdWrapper) -> Element { + match id { + SurfaceIdWrapper::LayerSurface(_) => unimplemented!(), + SurfaceIdWrapper::Window(_) => self + .applet_helper + .icon_button(&self.icon_name) + .on_press(Message::TogglePopup) + .into(), + SurfaceIdWrapper::Popup(_) => { + let name = text(fl!("network")).size(18); + let icon = icon(&self.icon_name, 24) + .style(Svg::Custom(|theme| svg::Appearance { + fill: Some(theme.palette().text), + })) + .width(Length::Units(24)) + .height(Length::Units(24)); + let mut list_col = list_column(); + + for conn in &self.active_conns { + let el = match conn { + ActiveConnectionInfo::Vpn { name, ip_addresses } => { + let mut ipv4 = column![]; + let mut ipv6 = column![]; + for addr in ip_addresses { + match addr { + std::net::IpAddr::V4(a) => { + ipv4 = ipv4.push(text(format!( + "{}: {}", + fl!("ipv4"), + a.to_string() + ))); + } + std::net::IpAddr::V6(a) => { + ipv6 = ipv6.push(text(format!( + "{}: {}", + fl!("ipv6"), + a.to_string() + ))); + } + } + } + column![text(name), ipv4, ipv6].spacing(4) + } + ActiveConnectionInfo::Wired { + name, + hw_address, + speed, + ip_addresses, + } => { + let mut ipv4 = column![]; + let mut ipv6 = column![]; + for addr in ip_addresses { + match addr { + std::net::IpAddr::V4(a) => { + ipv4 = ipv4.push(text(format!( + "{}: {}", + fl!("ipv4"), + a.to_string() + ))); + } + std::net::IpAddr::V6(a) => { + ipv6 = ipv6.push(text(format!( + "{}: {}", + fl!("ipv6"), + a.to_string() + ))); + } + } + } + column![ + row![ + text(name), + text(format!("{speed} {}", fl!("megabits-per-second"))) + ] + .spacing(16), + ipv4, + ipv6, + text(format!("{}: {hw_address}", fl!("mac"))), + ] + .spacing(4) + } + ActiveConnectionInfo::WiFi { + name, hw_address, .. + } => column![row![ + text(name), + text(format!("{}: {hw_address}", fl!("mac"))) + ] + .spacing(12)] + .spacing(4), + }; + list_col = list_col.add(el); + } + + let mut content = column![ + row![icon, name].spacing(8).width(Length::Fill), + list_col, + horizontal_rule(1), + container( + toggler(fl!("airplane-mode"), self.airplane_mode, |m| { + Message::ToggleAirplaneMode(m) + }) + .width(Length::Fill) + ) + .padding([0, 12]), + horizontal_rule(1), + container( + toggler(fl!("wifi"), self.wifi, |m| { Message::ToggleWiFi(m) }) + .width(Length::Fill) + ) + .padding([0, 12]), + ] + .align_items(Alignment::Center) + .spacing(8) + .padding(8); + if self.wifi { + let mut list_col = list_column(); + for ap in &self.wireless_access_points { + let button = self + .active_conns + .iter() + .find_map(|conn| match conn { + ActiveConnectionInfo::WiFi { name, .. } if name == &ap.ssid => { + Some( + button(Button::Primary) + .text(&ap.ssid) + .on_press(Message::Ignore) + .width(Length::Fill), + ) + } + _ => None, + }) + .unwrap_or_else(|| { + button(Button::Text) + .text(&ap.ssid) + .on_press(Message::SelectWirelessAccessPoint(ap.ssid.clone())) + .width(Length::Fill) + }); + list_col = list_col.add(button); + } + content = content.push(scrollable(list_col).height(Length::Fill)); + } + self.applet_helper.popup_container(content).into() + } + } + } + + fn subscription(&self) -> Subscription { + network_manager_subscription(0).map(|(_, event)| Message::NetworkManagerEvent(event)) + } + + fn theme(&self) -> Theme { + self.theme + } + + fn close_requested(&self, _id: iced_sctk::application::SurfaceIdWrapper) -> Self::Message { + Message::Ignore + } + + fn style(&self) -> ::Style { + ::Style::Custom(|theme| application::Appearance { + background_color: Color::from_rgba(0.0, 0.0, 0.0, 0.0), + text_color: theme.cosmic().on_bg_color().into(), + }) + } +} diff --git a/applets/cosmic-applet-network/src/config.rs b/applets/cosmic-applet-network/src/config.rs new file mode 100644 index 00000000..42153094 --- /dev/null +++ b/applets/cosmic-applet-network/src/config.rs @@ -0,0 +1,3 @@ +pub const APP_ID: &str = "com.system76.CosmicAppletNetwork"; +pub const PROFILE: &str = ""; +pub const VERSION: &str = "0.1.0"; diff --git a/applets/cosmic-applet-network/src/localize.rs b/applets/cosmic-applet-network/src/localize.rs new file mode 100644 index 00000000..baa05d0d --- /dev/null +++ b/applets/cosmic-applet-network/src/localize.rs @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: MPL-2.0-only + +use i18n_embed::{ + fluent::{fluent_language_loader, FluentLanguageLoader}, + DefaultLocalizer, LanguageLoader, Localizer, +}; +use once_cell::sync::Lazy; +use rust_embed::RustEmbed; + +#[derive(RustEmbed)] +#[folder = "i18n/"] +struct Localizations; + +pub static LANGUAGE_LOADER: Lazy = Lazy::new(|| { + let loader: FluentLanguageLoader = fluent_language_loader!(); + + loader + .load_fallback_language(&Localizations) + .expect("Error while loading fallback language"); + + loader +}); + +#[macro_export] +macro_rules! fl { + ($message_id:literal) => {{ + i18n_embed_fl::fl!($crate::localize::LANGUAGE_LOADER, $message_id) + }}; + + ($message_id:literal, $($args:expr),*) => {{ + i18n_embed_fl::fl!($crate::localize::LANGUAGE_LOADER, $message_id, $($args), *) + }}; +} + +// Get the `Localizer` to be used for localizing this library. +pub fn localizer() -> Box { + Box::from(DefaultLocalizer::new(&*LANGUAGE_LOADER, &Localizations)) +} + +pub fn localize() { + let localizer = localizer(); + let requested_languages = i18n_embed::DesktopLanguageRequester::requested_languages(); + + if let Err(error) = localizer.select(&requested_languages) { + eprintln!("Error while loading language for App List {}", error); + } +} diff --git a/applets/cosmic-applet-network/src/main.rs b/applets/cosmic-applet-network/src/main.rs index ccceef1b..b08a9466 100644 --- a/applets/cosmic-applet-network/src/main.rs +++ b/applets/cosmic-applet-network/src/main.rs @@ -1,49 +1,23 @@ // SPDX-License-Identifier: GPL-3.0-or-later -#[macro_use] -extern crate relm4_macros; +mod app; +mod config; +mod localize; +mod network_manager; -pub mod task; -pub mod ui; -pub mod widgets; +use log::info; -use gtk4::{glib, prelude::*, Orientation, Separator}; -use once_cell::sync::Lazy; -use tokio::runtime::Runtime; +use crate::config::{APP_ID, PROFILE, VERSION}; +use crate::localize::localize; -static RT: Lazy = Lazy::new(|| Runtime::new().expect("failed to build tokio runtime")); +fn main() -> cosmic::iced::Result { + // Initialize logger + pretty_env_logger::init(); + info!("Iced Workspaces Applet ({})", APP_ID); + info!("Version: {} ({})", VERSION, PROFILE); -fn main() { - let _monitors = libcosmic::init(); + // Prepare i18n + localize(); - view! { - window = libcosmic_applet::AppletWindow { - set_title: Some("COSMIC Network Applet"), - #[wrap(Some)] - set_child: button = &libcosmic_applet::AppletButton { - set_button_icon_name: "network-workgroup-symbolic", - #[wrap(Some)] - set_popover_child: main_box = >k4::Box { - set_orientation: Orientation::Vertical, - set_spacing: 10, - set_margin_top: 20, - set_margin_bottom: 20, - set_margin_start: 24, - set_margin_end: 24 - } - } - } - } - - ui::current_networks::add_current_networks(&main_box, &button); - main_box.append(&Separator::new(Orientation::Horizontal)); - ui::toggles::add_toggles(&main_box); - let available_wifi_separator = Separator::new(Orientation::Horizontal); - main_box.append(&available_wifi_separator); - available_wifi_separator.hide(); - ui::available_wifi::add_available_wifi(&main_box, available_wifi_separator); - window.show(); - - let main_loop = glib::MainLoop::new(None, false); - main_loop.run(); + app::run() } diff --git a/applets/cosmic-applet-network/src/network_manager/available_wifi.rs b/applets/cosmic-applet-network/src/network_manager/available_wifi.rs new file mode 100644 index 00000000..d1018822 --- /dev/null +++ b/applets/cosmic-applet-network/src/network_manager/available_wifi.rs @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +use cosmic_dbus_networkmanager::device::wireless::WirelessDevice; + +use futures_util::StreamExt; +use itertools::Itertools; +use std::collections::HashMap; + +pub async fn handle_wireless_device(device: WirelessDevice<'_>) -> zbus::Result> { + device.request_scan(HashMap::new()).await?; + let mut scan_changed = device.receive_last_scan_changed().await; + if let Some(t) = scan_changed.next().await { + if let Ok(-1) = t.get().await { + eprintln!("scan errored"); + return Ok(Default::default()); + } + } + let access_points = device.get_access_points().await?; + // Sort by strength and remove duplicates + let mut aps = HashMap::::new(); + for ap in access_points { + let ssid = String::from_utf8_lossy(&ap.ssid().await?.clone()).into_owned(); + let strength = ap.strength().await?; + if let Some(access_point) = aps.get(&ssid) { + if access_point.strength > strength { + continue; + } + } + aps.insert(ssid.clone(), AccessPoint { ssid, strength }); + } + let aps = aps + .into_iter() + .map(|(_, x)| x) + .sorted_by(|a, b| b.strength.cmp(&a.strength)) + .collect(); + Ok(aps) +} + +#[derive(Debug, Clone)] +pub struct AccessPoint { + pub ssid: String, + pub strength: u8, +} diff --git a/applets/cosmic-applet-network/src/network_manager/current_networks.rs b/applets/cosmic-applet-network/src/network_manager/current_networks.rs new file mode 100644 index 00000000..8e701602 --- /dev/null +++ b/applets/cosmic-applet-network/src/network_manager/current_networks.rs @@ -0,0 +1,108 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +use cosmic_dbus_networkmanager::{ + active_connection::ActiveConnection, + device::SpecificDevice, + interface::enums::{ApFlags, ApSecurityFlags}, +}; +use std::net::IpAddr; + +pub async fn active_connections( + active_connections: Vec>, +) -> zbus::Result> { + let mut info = Vec::::with_capacity(active_connections.len()); + for connection in active_connections { + if connection.vpn().await.unwrap_or_default() { + let mut ip_addresses = Vec::new(); + for address_data in connection.ip4_config().await?.address_data().await.unwrap_or_default() { + ip_addresses.push(IpAddr::V4(address_data.address)); + } + for address_data in connection.ip6_config().await?.address_data().await.unwrap_or_default() { + ip_addresses.push(IpAddr::V6(address_data.address)); + } + info.push(ActiveConnectionInfo::Vpn { + name: connection.id().await?, + ip_addresses, + }); + continue; + } + for device in connection.devices().await.unwrap_or_default() { + match device.downcast_to_device().await.ok().and_then(|inner| inner) { + Some(SpecificDevice::Wired(wired_device)) => { + let mut ip_addresses = Vec::new(); + for address_data in device.ip4_config().await?.address_data().await.unwrap_or_default() { + ip_addresses.push(IpAddr::V4(address_data.address)); + } + for address_data in device.ip6_config().await?.address_data().await.unwrap_or_default() { + ip_addresses.push(IpAddr::V6(address_data.address)); + } + info.push(ActiveConnectionInfo::Wired { + name: connection.id().await?, + hw_address: wired_device.hw_address().await?, + speed: wired_device.speed().await?, + ip_addresses, + }); + } + Some(SpecificDevice::Wireless(wireless_device)) => { + if let Ok(access_point) = wireless_device.active_access_point().await { + info.push(ActiveConnectionInfo::WiFi { + name: String::from_utf8_lossy(&access_point.ssid().await?).into_owned(), + hw_address: wireless_device.hw_address().await?, + flags: access_point.flags().await?, + rsn_flags: access_point.rsn_flags().await?, + wpa_flags: access_point.wpa_flags().await?, + }); + } + } + Some(SpecificDevice::WireGuard(_)) => { + let mut ip_addresses = Vec::new(); + for address_data in connection.ip4_config().await?.address_data().await.unwrap_or_default() { + ip_addresses.push(IpAddr::V4(address_data.address)); + } + for address_data in connection.ip6_config().await?.address_data().await.unwrap_or_default() { + ip_addresses.push(IpAddr::V6(address_data.address)); + } + info.push(ActiveConnectionInfo::Vpn { + name: connection.id().await?, + ip_addresses, + }); + } + _ => {} + } + } + } + + info.sort_by(|a, b| { + let helper = |conn: &ActiveConnectionInfo| { + match conn { + ActiveConnectionInfo::Vpn { name, .. } => format!("0{name}"), + ActiveConnectionInfo::Wired { name, .. } => format!("1{name}"), + ActiveConnectionInfo::WiFi { name, .. } => format!("2{name}"), + } + }; + helper(a).cmp(&helper(b)) + }); + + Ok(info) +} + +#[derive(Debug, Clone)] +pub enum ActiveConnectionInfo { + Wired { + name: String, + hw_address: String, + speed: u32, + ip_addresses: Vec, + }, + WiFi { + name: String, + hw_address: String, + flags: ApFlags, + rsn_flags: ApSecurityFlags, + wpa_flags: ApSecurityFlags, + }, + Vpn { + name: String, + ip_addresses: Vec, + }, +} diff --git a/applets/cosmic-applet-network/src/network_manager/mod.rs b/applets/cosmic-applet-network/src/network_manager/mod.rs new file mode 100644 index 00000000..615467f3 --- /dev/null +++ b/applets/cosmic-applet-network/src/network_manager/mod.rs @@ -0,0 +1,242 @@ +pub mod available_wifi; +pub mod current_networks; + +use std::{fmt::Debug, hash::Hash, time::Duration}; + +use cosmic::iced::{self, subscription}; +use cosmic_dbus_networkmanager::{ + device::SpecificDevice, interface::enums::DeviceType, nm::NetworkManager, +}; +use futures::{ + channel::mpsc::{unbounded, UnboundedReceiver, UnboundedSender}, + FutureExt, StreamExt, +}; +use zbus::Connection; + +use self::{ + available_wifi::{handle_wireless_device, AccessPoint}, + current_networks::{active_connections, ActiveConnectionInfo}, +}; + +// TODO subscription for wifi list & selection of wifi +// TODO subscription & channel for enabling / disabling wifi +// TODO subscription for displaying active connections & devices + +pub fn network_manager_subscription( + id: I, +) -> iced::Subscription<(I, NetworkManagerEvent)> { + subscription::unfold(id, State::Ready, move |state| start_listening(id, state)) +} + +#[derive(Debug)] +pub enum State { + Ready, + Waiting(Connection, UnboundedReceiver), + Finished, +} + +async fn start_listening( + id: I, + state: State, +) -> (Option<(I, NetworkManagerEvent)>, State) { + match state { + State::Ready => { + let conn = match Connection::system().await { + Ok(c) => c, + Err(_) => return (None, State::Finished), + }; + let network_manager = match NetworkManager::new(&conn).await { + Ok(n) => n, + Err(_) => return (None, State::Finished), + }; + let (tx, rx) = unbounded(); + let mut active_conns = active_connections( + network_manager + .active_connections() + .await + .unwrap_or_default(), + ) + .await + .unwrap_or_default(); + active_conns.sort_by(|a, b| { + let helper = |conn: &ActiveConnectionInfo| match conn { + ActiveConnectionInfo::Vpn { name, .. } => format!("0{name}"), + ActiveConnectionInfo::Wired { name, .. } => format!("1{name}"), + ActiveConnectionInfo::WiFi { name, .. } => format!("2{name}"), + }; + helper(a).cmp(&helper(b)) + }); + let wifi_enabled = network_manager.wireless_enabled().await.unwrap_or_default(); + let devices = network_manager.devices().await.ok().unwrap_or_default(); + let wireless_access_point_futures: Vec<_> = devices + .into_iter() + .map(|device| async move { + if let Ok(Some(SpecificDevice::Wireless(wireless_device))) = + device.downcast_to_device().await + { + handle_wireless_device(wireless_device) + .await + .unwrap_or_default() + } else { + Vec::new() + } + }) + .collect(); + let mut wireless_access_points = + Vec::with_capacity(wireless_access_point_futures.len()); + for f in wireless_access_point_futures { + wireless_access_points.append(&mut f.await); + } + wireless_access_points.sort_by(|a, b| b.strength.cmp(&a.strength)); + drop(network_manager); + return ( + Some(( + id, + NetworkManagerEvent::Init { + sender: tx, + wireless_access_points, + wifi_enabled, + airplane_mode: false, + active_conns, + }, + )), + State::Waiting(conn, rx), + ); + } + State::Waiting(conn, mut rx) => { + let network_manager = match NetworkManager::new(&conn).await { + Ok(n) => n, + Err(_) => return (None, State::Finished), + }; + let mut active_conns_changed = tokio::time::sleep(Duration::from_secs(5)) + .then(|_| async { network_manager.receive_active_connections_changed().await }) + .await; + let mut devices_changed = network_manager.receive_devices_changed().await; + let mut wireless_enabled_changed = + network_manager.receive_wireless_enabled_changed().await; + let mut req = rx.next().boxed().fuse(); + + let (update, should_exit) = futures::select! { + req = req => { + match req { + Some(NetworkManagerRequest::SetAirplaneMode(state)) => { + // TODO set airplane mode + let _ = network_manager.set_wireless_enabled(state).await; + (None, false) + } + Some(NetworkManagerRequest::SetWiFi(enabled)) => { + let success = network_manager.set_wireless_enabled(enabled).await.is_ok(); + let active_conns = active_connections(network_manager.active_connections().await.unwrap_or_default()).await.unwrap_or_default(); + let devices = network_manager.devices().await.ok().unwrap_or_default(); + let wireless_access_point_futures: Vec<_> = devices.into_iter().map(|device| async move { + if let Ok(Some(SpecificDevice::Wireless(wireless_device))) = + device.downcast_to_device().await + { + handle_wireless_device(wireless_device).await.unwrap_or_default() + } else { + Vec::new() + } + }).collect(); + let mut wireless_access_points = Vec::with_capacity(wireless_access_point_futures.len()); + for f in wireless_access_point_futures { + wireless_access_points.append(&mut f.await); + } + (Some((id, NetworkManagerEvent::RequestResponse { + req: NetworkManagerRequest::SetWiFi(enabled), + success, + active_conns, + wireless_access_points, + wifi_enabled: enabled, + airplane_mode: false, + })), false) + } + Some(NetworkManagerRequest::SelectAccessPoint(ssid)) => { + 'device_loop: for device in network_manager.devices().await.ok().unwrap_or_default() { + if matches!(device.device_type().await.unwrap_or(DeviceType::Other), DeviceType::Wifi) { + for conn in device.available_connections().await.unwrap_or_default() { + // dbg!(&conn.path()); + // TODO activate connection + } + } + } + (None, false) + } + None => { + (None, true) + } + }} + _ = active_conns_changed.next().boxed().fuse() => { + let active_conns = active_connections(network_manager.active_connections().await.unwrap_or_default()).await.unwrap_or_default(); + + (Some((id, NetworkManagerEvent::ActiveConns(active_conns))), false) + } + _ = devices_changed.next().boxed().fuse() => { + let devices = network_manager.devices().await.ok().unwrap_or_default(); + let wireless_access_point_futures: Vec<_> = devices.into_iter().map(|device| async move { + if let Ok(Some(SpecificDevice::Wireless(wireless_device))) = + device.downcast_to_device().await + { + handle_wireless_device(wireless_device).await.unwrap_or_default() + } else { + Vec::new() + } + }).collect(); + let mut wireless_access_points = Vec::with_capacity(wireless_access_point_futures.len()); + for f in wireless_access_point_futures { + wireless_access_points.append(&mut f.await); + } + (Some((id, NetworkManagerEvent::WirelessAccessPoints(wireless_access_points))), false) + } + enabled = wireless_enabled_changed.next().boxed().fuse() => { + let update = if let Some(update) = enabled { + update.get().await.ok().map(|update| (id, NetworkManagerEvent::WiFiEnabled(update))) + } else { + None + }; + (update, false) + } + }; + drop(active_conns_changed); + drop(wireless_enabled_changed); + drop(req); + ( + update, + if should_exit { + State::Finished + } else { + State::Waiting(conn, rx) + }, + ) + } + State::Finished => iced::futures::future::pending().await, + } +} + +#[derive(Debug, Clone)] +pub enum NetworkManagerRequest { + SetAirplaneMode(bool), + SetWiFi(bool), + SelectAccessPoint(String), +} + +#[derive(Debug, Clone)] +pub enum NetworkManagerEvent { + Init { + sender: UnboundedSender, + wireless_access_points: Vec, + active_conns: Vec, + wifi_enabled: bool, + airplane_mode: bool, + }, + RequestResponse { + req: NetworkManagerRequest, + wireless_access_points: Vec, + active_conns: Vec, + wifi_enabled: bool, + airplane_mode: bool, + success: bool, + }, + WiFiEnabled(bool), + WirelessAccessPoints(Vec), + ActiveConns(Vec), +} diff --git a/applets/cosmic-applet-network/src/task.rs b/applets/cosmic-applet-network/src/task.rs deleted file mode 100644 index fe6ab067..00000000 --- a/applets/cosmic-applet-network/src/task.rs +++ /dev/null @@ -1,35 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later -use std::future::Future; -use tokio::sync::oneshot; - -pub fn spawn(future: F) -> tokio::task::JoinHandle -where - F: Future + Send + 'static, - O: Send + 'static, -{ - crate::RT.spawn(future) -} - -pub fn block_on(future: F) -> O -where - F: Future + Send + 'static, - O: Send + 'static, -{ - crate::RT.block_on(future) -} - -pub fn spawn_local + 'static>(future: F) { - gtk4::glib::MainContext::default().spawn_local(future); -} - -pub async fn wait_for_local(future: F) -> Option -where - O: Send + 'static, - F: Future + Send + 'static, -{ - let (tx, rx) = oneshot::channel::(); - gtk4::glib::MainContext::default().spawn_local(async move { - std::mem::drop(tx.send(future.await)); - }); - rx.await.ok() -} diff --git a/applets/cosmic-applet-network/src/ui.rs b/applets/cosmic-applet-network/src/ui.rs deleted file mode 100644 index cc0c6244..00000000 --- a/applets/cosmic-applet-network/src/ui.rs +++ /dev/null @@ -1,5 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later - -pub mod available_wifi; -pub mod current_networks; -pub mod toggles; diff --git a/applets/cosmic-applet-network/src/ui/available_wifi.rs b/applets/cosmic-applet-network/src/ui/available_wifi.rs deleted file mode 100644 index 7b9b247d..00000000 --- a/applets/cosmic-applet-network/src/ui/available_wifi.rs +++ /dev/null @@ -1,128 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later - -use crate::task; -use cosmic_dbus_networkmanager::{ - device::{wireless::WirelessDevice, SpecificDevice}, - nm::NetworkManager, -}; -use futures_util::StreamExt; -use gtk4::{ - glib::{self, clone, source::PRIORITY_DEFAULT, MainContext, Sender}, - prelude::*, - Image, ListBox, ListBoxRow, Separator, -}; -use libcosmic::widgets::{relm4::RelmContainerExt, LabeledItem}; -use std::{ - cell::RefCell, - collections::{BTreeMap, HashMap}, - rc::Rc, -}; -use zbus::Connection; - -pub fn add_available_wifi(target: >k4::Box, separator: Separator) { - let ap_entries = Rc::>>::default(); - let (tx, rx) = MainContext::channel::>(PRIORITY_DEFAULT); - task::spawn(async move { - if let Err(err) = scan_for_wifi(tx).await { - eprintln!("scan_for_wifi failed: {}", err); - } - }); - - let scrolled_window = gtk4::ScrolledWindow::new(); - scrolled_window.set_hscrollbar_policy(gtk4::PolicyType::Never); - scrolled_window.set_vscrollbar_policy(gtk4::PolicyType::Automatic); - scrolled_window.set_propagate_natural_height(true); - scrolled_window.set_max_content_height(300); - - let wifi_list = ListBox::new(); - rx.attach( - None, - clone!(@strong ap_entries, @weak wifi_list, @weak separator, => @default-return Continue(true), move |aps| { - build_aps_list(ap_entries.clone(), &wifi_list, aps); - separator.set_visible(!ap_entries.borrow().is_empty()); - Continue(true) - }), - ); - scrolled_window.set_child(Some(&wifi_list)); - target.append(&scrolled_window); -} - -fn build_aps_list( - ap_entries: Rc>>, - target: &ListBox, - aps: Vec, -) { - let mut ap_entries = ap_entries.borrow_mut(); - for old_ap_box in ap_entries.drain(..) { - target.remove(&old_ap_box); - } - for ap in aps { - view! { - entry = ListBoxRow { - #[wrap(Some)] - set_child: entry_box = >k4::Box { - container_add: labeled_item = &LabeledItem { - set_title: &ap.ssid, - set_child: icon = &Image { - set_icon_name: Some("network-wireless-symbolic") - } - } - } - } - } - target.append(&entry); - ap_entries.push(entry); - } -} - -async fn scan_for_wifi(tx: Sender>) -> zbus::Result<()> { - let conn = Connection::system().await?; - let network_manager = NetworkManager::new(&conn).await?; - loop { - let devices = network_manager.devices().await?; - for device in devices { - if let Ok(Some(SpecificDevice::Wireless(wireless_device))) = - device.downcast_to_device().await - { - handle_wireless_device(wireless_device, tx.clone()).await?; - } - } - tokio::time::sleep(std::time::Duration::from_secs(5)).await; - } -} - -async fn handle_wireless_device( - device: WirelessDevice<'_>, - tx: Sender>, -) -> zbus::Result<()> { - device.request_scan(HashMap::new()).await?; - let mut scan_changed = device.receive_last_scan_changed().await; - if let Some(t) = scan_changed.next().await { - if let Ok(-1) = t.get().await { - eprintln!("scan errored"); - return Ok(()); - } - } - let access_points = device.get_access_points().await?; - // Sort by SSID and remove duplicates - let mut aps = BTreeMap::::new(); - for ap in access_points { - let ssid = String::from_utf8_lossy(&ap.ssid().await?.clone()).into_owned(); - let strength = ap.strength().await?; - if let Some(access_point) = aps.get(&ssid) { - if access_point.strength > strength { - continue; - } - } - aps.insert(ssid.clone(), AccessPoint { ssid, strength }); - } - let aps = aps.into_iter().map(|(_, x)| x).collect(); - tx.send(aps).expect("failed to send back to main thread"); - Ok(()) -} - -#[derive(Debug)] -struct AccessPoint { - ssid: String, - strength: u8, -} diff --git a/applets/cosmic-applet-network/src/ui/current_networks.rs b/applets/cosmic-applet-network/src/ui/current_networks.rs deleted file mode 100644 index 5c1b85f1..00000000 --- a/applets/cosmic-applet-network/src/ui/current_networks.rs +++ /dev/null @@ -1,248 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later - -use cosmic_dbus_networkmanager::{ - active_connection::ActiveConnection, - device::SpecificDevice, - interface::{ - active_connection::ActiveConnectionProxy, - enums::{ApFlags, ApSecurityFlags}, - }, - nm::NetworkManager, -}; -use futures_util::StreamExt; -use gtk4::{ - glib::{self, clone, source::PRIORITY_DEFAULT, MainContext, Sender}, - prelude::*, - IconSize, Image, ListBox, ListBoxRow, Orientation, -}; -use std::{cell::RefCell, net::IpAddr, rc::Rc}; -use zbus::Connection; - -pub fn add_current_networks(target: >k4::Box, icon_image: &libcosmic_applet::AppletButton) { - let networks_list = ListBox::builder().show_separators(true).build(); - let entries = Rc::>>::default(); - let (tx, rx) = MainContext::channel::>(PRIORITY_DEFAULT); - crate::task::spawn(handle_devices(tx)); - rx.attach( - None, - clone!(@weak networks_list, @weak icon_image, @strong entries => @default-return Continue(true), move |connections| { - let mut entries = entries.borrow_mut(); - display_active_connections(connections, &networks_list, &mut *entries, &icon_image); - Continue(true) - }), - ); - target.append(&networks_list); -} - -fn display_active_connections( - connections: Vec, - target: &ListBox, - entries: &mut Vec, - icon_image: &libcosmic_applet::AppletButton, -) { - for old_entry in entries.drain(..) { - target.remove(&old_entry); - } - for connection in connections { - let entry = match connection { - ActiveConnectionInfo::Wired { - name, - hw_address, - speed, - ip_addresses, - } => { - icon_image.set_button_icon_name("network-wired-symbolic"); - render_wired_connection(name, speed, ip_addresses) - } - ActiveConnectionInfo::WiFi { - name, - hw_address, - flags, - rsn_flags, - wpa_flags, - } => continue, - ActiveConnectionInfo::Vpn { name, ip_addresses } => { - icon_image.set_button_icon_name("network-vpn-symbolic"); - render_vpn(name, ip_addresses) - } - }; - let entry = ListBoxRow::builder().child(&entry).build(); - target.append(&entry); - entries.push(entry); - } -} - -fn render_wired_connection(name: String, speed: u32, ip_addresses: Vec) -> gtk4::Box { - view! { - entry = gtk4::Box { - set_orientation: Orientation::Horizontal, - set_spacing: 8, - append: wired_icon = &Image { - set_icon_name: Some("network-wired-symbolic"), - set_icon_size: IconSize::Large - }, - append: wired_label_box = >k4::Box { - set_orientation: Orientation::Vertical, - append: wired_label = >k4::Label { - set_label: &name, - set_halign: gtk4::Align::Start, - } - }, - append: wired_speed = >k4::Label { - set_label: &format!("Connected - {} Mbps", speed), - set_valign: gtk4::Align::Center, - }, - } - } - for address in ip_addresses { - view! { - wired_ip = gtk4::Label { - set_label: &format!("IP Address: {}", address), - set_halign: gtk4::Align::Start, - } - } - wired_label_box.append(&wired_ip); - } - entry -} - -fn render_vpn(name: String, ip_addresses: Vec) -> gtk4::Box { - view! { - entry = gtk4::Box { - set_orientation: Orientation::Horizontal, - set_spacing: 8, - append: wired_icon = &Image { - set_icon_name: Some("network-vpn-symbolic"), - set_icon_size: IconSize::Large - }, - append: wired_label_box = >k4::Box { - set_orientation: Orientation::Vertical, - append: wired_label = >k4::Label { - set_label: &name, - set_halign: gtk4::Align::Start, - } - } - } - } - for address in ip_addresses { - view! { - wired_ip = gtk4::Label { - set_label: &format!("IP Address: {}", address), - set_halign: gtk4::Align::Start, - } - } - wired_label_box.append(&wired_ip); - } - entry -} - -async fn handle_devices(tx: Sender>) -> zbus::Result<()> { - let conn = Connection::system().await?; - let network_manager = NetworkManager::new(&conn).await?; - handle_active_connections(tx.clone(), network_manager.active_connections().await?).await?; - let mut active_connections_changed = network_manager.receive_active_connections_changed().await; - while let Some(active_connection_objects) = active_connections_changed.next().await { - let active_connection_objects = active_connection_objects.get().await?; - let mut active_connections = Vec::with_capacity(active_connection_objects.len()); - for object in active_connection_objects { - active_connections.push( - ActiveConnectionProxy::builder(&conn) - .path(object)? - .build() - .await - .map(ActiveConnection::from)?, - ); - } - handle_active_connections(tx.clone(), active_connections).await?; - } - Ok(()) -} - -async fn handle_active_connections( - tx: Sender>, - active_connections: Vec>, -) -> zbus::Result<()> { - let mut info = Vec::::with_capacity(active_connections.len()); - for connection in active_connections { - if connection.vpn().await? { - let mut ip_addresses = Vec::new(); - for address_data in connection.ip4_config().await?.address_data().await? { - ip_addresses.push(IpAddr::V4(address_data.address)); - } - for address_data in connection.ip6_config().await?.address_data().await? { - ip_addresses.push(IpAddr::V6(address_data.address)); - } - info.push(ActiveConnectionInfo::Vpn { - name: connection.id().await?, - ip_addresses, - }); - continue; - } - for device in connection.devices().await? { - match device.downcast_to_device().await? { - Some(SpecificDevice::Wired(wired_device)) => { - let mut ip_addresses = Vec::new(); - for address_data in device.ip4_config().await?.address_data().await? { - ip_addresses.push(IpAddr::V4(address_data.address)); - } - for address_data in device.ip6_config().await?.address_data().await? { - ip_addresses.push(IpAddr::V6(address_data.address)); - } - info.push(ActiveConnectionInfo::Wired { - name: connection.id().await?, - hw_address: wired_device.hw_address().await?, - speed: wired_device.speed().await?, - ip_addresses, - }); - } - Some(SpecificDevice::Wireless(wireless_device)) => { - let access_point = wireless_device.active_access_point().await?; - info.push(ActiveConnectionInfo::WiFi { - name: String::from_utf8_lossy(&access_point.ssid().await?).into_owned(), - hw_address: wireless_device.hw_address().await?, - flags: access_point.flags().await?, - rsn_flags: access_point.rsn_flags().await?, - wpa_flags: access_point.wpa_flags().await?, - }); - } - Some(SpecificDevice::WireGuard(_)) => { - let mut ip_addresses = Vec::new(); - for address_data in connection.ip4_config().await?.address_data().await? { - ip_addresses.push(IpAddr::V4(address_data.address)); - } - for address_data in connection.ip6_config().await?.address_data().await? { - ip_addresses.push(IpAddr::V6(address_data.address)); - } - info.push(ActiveConnectionInfo::Vpn { - name: connection.id().await?, - ip_addresses, - }); - } - _ => {} - } - } - } - tx.send(info) - .expect("failed to send active connections back to main thread"); - Ok(()) -} - -enum ActiveConnectionInfo { - Wired { - name: String, - hw_address: String, - speed: u32, - ip_addresses: Vec, - }, - WiFi { - name: String, - hw_address: String, - flags: ApFlags, - rsn_flags: ApSecurityFlags, - wpa_flags: ApSecurityFlags, - }, - Vpn { - name: String, - ip_addresses: Vec, - }, -} diff --git a/applets/cosmic-applet-network/src/ui/toggles.rs b/applets/cosmic-applet-network/src/ui/toggles.rs deleted file mode 100644 index 6de8316c..00000000 --- a/applets/cosmic-applet-network/src/ui/toggles.rs +++ /dev/null @@ -1,71 +0,0 @@ -use crate::{task, widgets::SettingsEntry}; -use cosmic_dbus_networkmanager::nm::NetworkManager; -use futures_util::StreamExt; -use gtk4::{ - glib::{self, clone, source::PRIORITY_DEFAULT, MainContext, Sender}, - prelude::*, - Inhibit, Orientation, Separator, Switch, -}; -use zbus::Connection; - -pub fn add_toggles(target: >k4::Box) { - view! { - airplane_mode = SettingsEntry { - set_title_markup: "Airplane Mode", - set_child: airplane_mode_switch = &Switch {} - } - } - view! { - wifi = SettingsEntry { - set_title_markup: "WiFi", - set_child: wifi_switch = &Switch {} - } - } - target.append(&airplane_mode); - target.append(&Separator::new(Orientation::Horizontal)); - target.append(&wifi); - target.append(&Separator::new(Orientation::Horizontal)); - - let (wifi_tx, wifi_rx) = MainContext::channel::(PRIORITY_DEFAULT); - wifi_switch.connect_state_set( - clone!(@strong wifi_tx => @default-return Inhibit(false), move |_switch, state| { - match task::block_on(set_wifi_mode(state)) { - Ok(()) => Inhibit(false), - Err(err) => { - eprintln!("set_wifi_mode failed: {}", err); - Inhibit(true) - } - } - }), - ); - wifi_rx.attach( - None, - clone!(@weak wifi_switch => @default-return Continue(true), move |wifi| { - wifi_switch.set_active(wifi); - Continue(true) - }), - ); - task::spawn(get_wifi_mode(wifi_tx)); -} - -async fn get_wifi_mode(tx: Sender) -> zbus::Result<()> { - let connection = Connection::system().await?; - let network_manager = NetworkManager::new(&connection).await?; - let wireless_enabled = network_manager.wireless_enabled().await?; - tx.send(wireless_enabled) - .expect("Failed to send wifi enablement back to main thread"); - let mut stream = network_manager.receive_wireless_enabled_changed().await; - while let Some(wireless_enabled) = stream.next().await { - if let Ok(wireless_enabled) = wireless_enabled.get().await { - tx.send(wireless_enabled) - .expect("Failed to send wifi enablement back to main thread"); - } - } - Ok(()) -} - -async fn set_wifi_mode(state: bool) -> zbus::Result<()> { - let connection = Connection::system().await?; - let network_manager = NetworkManager::new(&connection).await?; - network_manager.set_wireless_enabled(state).await -} diff --git a/applets/cosmic-applet-network/src/widgets.rs b/applets/cosmic-applet-network/src/widgets.rs deleted file mode 100644 index 81d096cc..00000000 --- a/applets/cosmic-applet-network/src/widgets.rs +++ /dev/null @@ -1,5 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later - -mod settings_entry; - -pub use settings_entry::SettingsEntry; diff --git a/applets/cosmic-applet-network/src/widgets/settings_entry.rs b/applets/cosmic-applet-network/src/widgets/settings_entry.rs deleted file mode 100644 index 9d56e4e6..00000000 --- a/applets/cosmic-applet-network/src/widgets/settings_entry.rs +++ /dev/null @@ -1,211 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later - -use gtk4::{ - glib::{self, Object}, - prelude::*, - subclass::prelude::*, - Label, -}; -use std::cell::RefCell; - -glib::wrapper! { - pub struct SettingsEntry(ObjectSubclass) - @extends gtk4::Widget, - @implements gtk4::Accessible; -} - -impl SettingsEntry { - pub fn new() -> Self { - Self::default() - } - - pub fn set_child<'a, Widget, IntoWidget>(&self, child: IntoWidget) - where - Widget: IsA, - IntoWidget: Into>, - { - let imp = self.inner(); - let child = child.into().map(AsRef::as_ref); - let child_box_ref = imp.child_box.borrow(); - let child_box: >k4::Box = child_box_ref.as_ref().expect("child_box not created??"); - if let Some(new_child) = child { - new_child.set_halign(gtk4::Align::End); - child_box.append(new_child); - } - if let Some(old_child) = imp.child.replace(child.cloned()) { - child_box.remove(&old_child); - } - } - - pub fn set_child_label>(&self, label: A) { - let label = label.as_ref(); - let child = Label::builder() - .label(label) - .css_classes(vec!["settings-entry-text".into()]) - .build(); - self.set_child(&child); - } - - pub fn align_child(&self, alignment: gtk4::Align) { - let imp = self.inner(); - let child_box = imp.child_box.borrow(); - let child_box = child_box.as_ref().expect("child_box not created??"); - let child = imp.child.borrow(); - let child = child.as_ref().expect("child not set"); - let title_desc_box = imp.title_desc_box.borrow(); - let title_desc_box = title_desc_box - .as_ref() - .expect("title_desc_box not created?"); - match alignment { - gtk4::Align::Start => { - child_box.reorder_child_after(title_desc_box, Some(child)); - } - gtk4::Align::End => { - child_box.reorder_child_after(child, Some(title_desc_box)); - } - _ => unimplemented!(), - } - } - - pub fn set_title(&self, title: &str) { - let inner = self.inner(); - let title_ref = inner.title.borrow_mut(); - match &*title_ref { - Some(label) => label.set_label(title), - None => { - let title = gtk4::Label::builder() - .label(title) - .css_classes(vec!["settings-entry-title".into()]) - .halign(gtk4::Align::Start) - .build(); - let title_desc_box = inner.title_desc_box.borrow(); - let title_desc_box = title_desc_box - .as_ref() - .expect("title_desc_box not created?"); - if inner.desc.borrow().is_some() { - title_desc_box.prepend(&title); - } else { - title_desc_box.append(&title); - } - } - } - } - - pub fn set_title_markup(&self, title: &str) { - let inner = self.inner(); - let title_ref = inner.title.borrow_mut(); - match &*title_ref { - Some(label) => label.set_markup(title), - None => { - let title = gtk4::Label::builder() - .label(title) - .use_markup(true) - .css_classes(vec!["settings-entry-title".into()]) - .halign(gtk4::Align::Start) - .build(); - let title_desc_box = inner.title_desc_box.borrow(); - let title_desc_box = title_desc_box - .as_ref() - .expect("title_desc_box not created?"); - if inner.desc.borrow().is_some() { - title_desc_box.prepend(&title); - } else { - title_desc_box.append(&title); - } - } - } - } - - pub fn set_description(&self, description: &str) { - let inner = self.inner(); - let desc_ref = inner.desc.borrow_mut(); - match &*desc_ref { - Some(label) => label.set_label(description), - None => { - let desc = gtk4::Label::builder() - .label(description) - .css_classes(vec!["settings-entry-desc".into()]) - .halign(gtk4::Align::Start) - .build(); - let title_desc_box = inner.title_desc_box.borrow(); - let title_desc_box = title_desc_box - .as_ref() - .expect("title_desc_box not created?"); - title_desc_box.append(&desc); - } - } - } - - fn inner(&self) -> &SettingsEntryImp { - SettingsEntryImp::from_instance(self) - } -} - -impl Default for SettingsEntry { - fn default() -> Self { - Object::new(&[]).expect("Failed to create `SettingsEntry`.") - } -} - -#[derive(Debug, Default)] -pub struct SettingsEntryImp { - title: RefCell>, - desc: RefCell>, - title_desc_box: RefCell>, - child_box: RefCell>, - child: RefCell>, -} - -#[glib::object_subclass] -impl ObjectSubclass for SettingsEntryImp { - const NAME: &'static str = "SettingsEntry"; - type Type = SettingsEntry; - type ParentType = gtk4::Widget; - - fn class_init(klass: &mut Self::Class) { - // The layout manager determines how child widgets are laid out. - klass.set_layout_manager_type::(); - } -} - -impl ObjectImpl for SettingsEntryImp { - fn constructed(&self, obj: &Self::Type) { - self.parent_constructed(obj); - let child = gtk4::Box::builder() - .css_classes(vec!["settings-entry".into()]) - .orientation(gtk4::Orientation::Horizontal) - .hexpand(true) - .margin_start(24) - .margin_end(24) - .margin_top(8) - .margin_bottom(8) - .spacing(16) - .build(); - - let title_and_desc = gtk4::Box::builder() - .css_classes(vec!["settings-entry-info".into()]) - .orientation(gtk4::Orientation::Vertical) - .spacing(4) - .hexpand(true) - .valign(gtk4::Align::Center) - .build(); - child.append(&title_and_desc); - *self.title_desc_box.borrow_mut() = Some(title_and_desc); - if let Some(entry_child) = self.child.borrow().as_ref() { - child.append(entry_child); - } - child.set_parent(obj); - *self.child_box.borrow_mut() = Some(child); - } - - fn dispose(&self, _obj: &Self::Type) { - if let Some(child) = self.child.borrow_mut().take() { - child.unparent(); - } - if let Some(child_box) = self.child_box.borrow_mut().take() { - child_box.unparent(); - } - } -} - -impl WidgetImpl for SettingsEntryImp {} diff --git a/debian/rules b/debian/rules index 96f020ee..3699e5a3 100755 --- a/debian/rules +++ b/debian/rules @@ -50,6 +50,12 @@ override_dh_auto_clean: echo 'directory = "vendor"' >> .cargo/config; \ tar pcf vendor.tar vendor; \ rm -rf vendor; \ + cd applets/cosmic-applet-network/; \ + mkdir -p .cargo; \ + cargo vendor --sync Cargo.toml | head -n -1 > .cargo/config; \ + echo 'directory = "vendor"' >> .cargo/config; \ + tar pcf vendor.tar vendor; \ + rm -rf vendor; \ fi override_dh_auto_build: diff --git a/justfile b/justfile index ba33311e..a67daaf8 100644 --- a/justfile +++ b/justfile @@ -31,6 +31,9 @@ build: _extract_vendor pushd applets/cosmic-applet-audio/ cargo build {{cargo_args}} popd + pushd applets/cosmic-applet-network/ + cargo build {{cargo_args}} + popd pushd applets/cosmic-applet-graphics/ cargo build {{cargo_args}} popd @@ -59,7 +62,7 @@ install: # network install -Dm0644 applets/cosmic-applet-network/data/icons/{{network_id}}.svg {{iconsdir}}/{{network_id}}.svg install -Dm0644 applets/cosmic-applet-network/data/{{network_id}}.desktop {{sharedir}}/applications/{{network_id}}.desktop - install -Dm0755 target/release/cosmic-applet-network {{bindir}}/cosmic-applet-network + install -Dm0755 applets/cosmic-applet-network/target/release/cosmic-applet-network {{bindir}}/cosmic-applet-network # notifications install -Dm0644 applets/cosmic-applet-notifications/data/icons/{{notifications_id}}.svg {{iconsdir}}/{{notifications_id}}.svg @@ -116,4 +119,5 @@ _extract_vendor: rm -rf applets/cosmic-applet-workspaces/vendor; tar xf applets/cosmic-applet-workspaces/vendor.tar --directory applets/cosmic-applet-workspaces rm -rf applets/cosmic-applet-battery/vendor; tar xf applets/cosmic-applet-battery/vendor.tar --directory applets/cosmic-applet-battery rm -rf applets/cosmic-applet-audio/vendor; tar xf applets/cosmic-applet-audio/vendor.tar --directory applets/cosmic-applet-audio + rm -rf applets/cosmic-applet-network/vendor; tar xf applets/cosmic-applet-battery/vendor.tar --directory applets/cosmic-applet-battery fi