diff --git a/Cargo.lock b/Cargo.lock index 128e67ad..54abb816 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -10,9 +10,9 @@ checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3" [[package]] name = "ab_glyph" -version = "0.2.18" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dcdbc68024b653943864d436fe8a24b028095bc1cf91a8926f8241e4aaffe59" +checksum = "e5568a4aa5ba8adf5175c5c460b030e27d8893412976cc37bef0e4fbc16cfbba" dependencies = [ "ab_glyph_rasterizer", "owned_ttf_parser", @@ -208,9 +208,9 @@ checksum = "7a40729d2133846d9ed0ea60a8b9541bccddab49cd30f0715a1da672fe9a2524" [[package]] name = "async-trait" -version = "0.1.60" +version = "0.1.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d1d8ab452a3936018a687b20e6f7cf5363d713b732b8884001317b0e48aa3" +checksum = "705339e0e4a9690e2908d2b3d049d85682cf19fbd5782494498fbf7003a6a282" dependencies = [ "proc-macro2", "quote", @@ -567,6 +567,7 @@ dependencies = [ name = "cosmic-applet-network" version = "0.1.0" dependencies = [ + "anyhow", "cosmic-dbus-networkmanager", "futures", "futures-util", @@ -655,20 +656,20 @@ dependencies = [ [[package]] name = "cosmic-dbus-networkmanager" version = "0.1.0" -source = "git+https://github.com/pop-os/dbus-settings-bindings?branch=deps#af1cc089ec08f4cb37d7d9448523963bf05995c6" +source = "git+https://github.com/pop-os/dbus-settings-bindings?branch=main#95f6d5e9ac86dfa5902ac74dee85b1a8cfc035fd" dependencies = [ "bitflags", "derive_builder", "procfs", "time 0.3.17", "zbus", - "zvariant 3.9.0", + "zvariant", ] [[package]] name = "cosmic-panel-config" version = "0.1.0" -source = "git+https://github.com/pop-os/cosmic-panel#9502913468dc7bf1cc17da6c1fcf1e274f43a769" +source = "git+https://github.com/pop-os/cosmic-panel#b4e60dc30bb798d59cc57957291609e794d38821" dependencies = [ "anyhow", "ron", @@ -821,9 +822,9 @@ checksum = "b365fabc795046672053e29c954733ec3b05e4be654ab130fe8f1f94d7051f35" [[package]] name = "cxx" -version = "1.0.85" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5add3fc1717409d029b20c5b6903fc0c0b02fa6741d820054f4a2efa5e5816fd" +checksum = "51d1075c37807dcf850c379432f0df05ba52cc30f279c5cfc43cc221ce7f8579" dependencies = [ "cc", "cxxbridge-flags", @@ -833,9 +834,9 @@ dependencies = [ [[package]] name = "cxx-build" -version = "1.0.85" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4c87959ba14bc6fbc61df77c3fcfe180fc32b93538c4f1031dd802ccb5f2ff0" +checksum = "5044281f61b27bc598f2f6647d480aed48d2bf52d6eb0b627d84c0361b17aa70" dependencies = [ "cc", "codespan-reporting", @@ -848,15 +849,15 @@ dependencies = [ [[package]] name = "cxxbridge-flags" -version = "1.0.85" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69a3e162fde4e594ed2b07d0f83c6c67b745e7f28ce58c6df5e6b6bef99dfb59" +checksum = "61b50bc93ba22c27b0d31128d2d130a0a6b3d267ae27ef7e4fae2167dfe8781c" [[package]] name = "cxxbridge-macro" -version = "1.0.85" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e7e2adeb6a0d4a282e581096b06e1791532b7d576dcde5ccd9382acf55db8e6" +checksum = "39e61fda7e62115119469c7b3591fd913ecca96fb766cfd3f2e2502ab7bc87a5" dependencies = [ "proc-macro2", "quote", @@ -1556,6 +1557,16 @@ dependencies = [ "version_check", ] +[[package]] +name = "gethostname" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1ebd34e35c46e00bb73e81363248d627782724609fe1b6396f553f68fe3862e" +dependencies = [ + "libc", + "winapi", +] + [[package]] name = "getrandom" version = "0.2.8" @@ -1839,9 +1850,9 @@ dependencies = [ [[package]] name = "half" -version = "2.2.0" +version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c467d36af040b7b2681f5fddd27427f6da8d3d072f575a265e181d2f8e8d157" +checksum = "02b4af3693f1b705df946e9fe5631932443781d0aabb423b62fcd4d73f6d2fd0" dependencies = [ "crunchy", ] @@ -1902,9 +1913,9 @@ dependencies = [ [[package]] name = "i18n-config" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b62affcd43abfb51f3cbd8736f9407908dc5b44fc558a9be07460bbfd104d983" +checksum = "3d9f93ceee6543011739bc81699b5e0cf1f23f3a80364649b6d80de8636bc8df" dependencies = [ "log", "serde", @@ -1916,9 +1927,9 @@ dependencies = [ [[package]] name = "i18n-embed" -version = "0.13.4" +version = "0.13.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7f21ed76e44de8ac3dfa36bb37ab2e6480be0dc75c612474949be1f3cb2c253" +checksum = "79ff7e6b37b61834ec97fc945d391315188d8bb87aa1d48f10f295e73a5f5bec" dependencies = [ "fluent", "fluent-langneg", @@ -1937,9 +1948,9 @@ dependencies = [ [[package]] name = "i18n-embed-fl" -version = "0.6.4" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9420a9718ef9d0ab727840a398e25408ea0daff9ba3c681707ba05485face98e" +checksum = "a425b9bbdc2e4cd797a2a79528662cb61894bd36db582e48da2c56c28eb727cd" dependencies = [ "dashmap", "find-crate", @@ -1996,7 +2007,7 @@ dependencies = [ [[package]] name = "iced" version = "0.6.0" -source = "git+https://github.com/pop-os/libcosmic/?branch=master#444e389496bb92a928b7731175f00f51ec4f0caa" +source = "git+https://github.com/pop-os/libcosmic/?branch=master#5224c9b75c000fcf92a1851391fe1dc82486610a" dependencies = [ "iced_core", "iced_futures", @@ -2004,7 +2015,7 @@ dependencies = [ "iced_graphics", "iced_native", "iced_sctk", - "iced_swbuf", + "iced_softbuffer", "iced_wgpu", "image", "thiserror", @@ -2013,7 +2024,7 @@ dependencies = [ [[package]] name = "iced_core" version = "0.6.2" -source = "git+https://github.com/pop-os/libcosmic/?branch=master#444e389496bb92a928b7731175f00f51ec4f0caa" +source = "git+https://github.com/pop-os/libcosmic/?branch=master#5224c9b75c000fcf92a1851391fe1dc82486610a" dependencies = [ "bitflags", "palette", @@ -2023,7 +2034,7 @@ dependencies = [ [[package]] name = "iced_futures" version = "0.5.1" -source = "git+https://github.com/pop-os/libcosmic/?branch=master#444e389496bb92a928b7731175f00f51ec4f0caa" +source = "git+https://github.com/pop-os/libcosmic/?branch=master#5224c9b75c000fcf92a1851391fe1dc82486610a" dependencies = [ "futures", "log", @@ -2035,7 +2046,7 @@ dependencies = [ [[package]] name = "iced_glow" version = "0.5.1" -source = "git+https://github.com/pop-os/libcosmic/?branch=master#444e389496bb92a928b7731175f00f51ec4f0caa" +source = "git+https://github.com/pop-os/libcosmic/?branch=master#5224c9b75c000fcf92a1851391fe1dc82486610a" dependencies = [ "bytemuck", "euclid", @@ -2050,7 +2061,7 @@ dependencies = [ [[package]] name = "iced_graphics" version = "0.5.0" -source = "git+https://github.com/pop-os/libcosmic/?branch=master#444e389496bb92a928b7731175f00f51ec4f0caa" +source = "git+https://github.com/pop-os/libcosmic/?branch=master#5224c9b75c000fcf92a1851391fe1dc82486610a" dependencies = [ "bitflags", "bytemuck", @@ -2070,7 +2081,7 @@ dependencies = [ [[package]] name = "iced_lazy" version = "0.3.0" -source = "git+https://github.com/pop-os/libcosmic/?branch=master#444e389496bb92a928b7731175f00f51ec4f0caa" +source = "git+https://github.com/pop-os/libcosmic/?branch=master#5224c9b75c000fcf92a1851391fe1dc82486610a" dependencies = [ "iced_native", "ouroboros 0.13.0", @@ -2079,7 +2090,7 @@ dependencies = [ [[package]] name = "iced_native" version = "0.7.0" -source = "git+https://github.com/pop-os/libcosmic/?branch=master#444e389496bb92a928b7731175f00f51ec4f0caa" +source = "git+https://github.com/pop-os/libcosmic/?branch=master#5224c9b75c000fcf92a1851391fe1dc82486610a" dependencies = [ "iced_core", "iced_futures", @@ -2093,7 +2104,7 @@ dependencies = [ [[package]] name = "iced_sctk" version = "0.1.0" -source = "git+https://github.com/pop-os/libcosmic/?branch=master#444e389496bb92a928b7731175f00f51ec4f0caa" +source = "git+https://github.com/pop-os/libcosmic/?branch=master#5224c9b75c000fcf92a1851391fe1dc82486610a" dependencies = [ "enum-repr", "futures", @@ -2110,19 +2121,9 @@ dependencies = [ ] [[package]] -name = "iced_style" -version = "0.5.1" -source = "git+https://github.com/pop-os/libcosmic/?branch=master#444e389496bb92a928b7731175f00f51ec4f0caa" -dependencies = [ - "iced_core", - "once_cell", - "palette", -] - -[[package]] -name = "iced_swbuf" +name = "iced_softbuffer" version = "0.1.0" -source = "git+https://github.com/pop-os/libcosmic/?branch=master#444e389496bb92a928b7731175f00f51ec4f0caa" +source = "git+https://github.com/pop-os/libcosmic/?branch=master#5224c9b75c000fcf92a1851391fe1dc82486610a" dependencies = [ "cosmic-text", "iced_graphics", @@ -2134,10 +2135,20 @@ dependencies = [ "softbuffer", ] +[[package]] +name = "iced_style" +version = "0.5.1" +source = "git+https://github.com/pop-os/libcosmic/?branch=master#5224c9b75c000fcf92a1851391fe1dc82486610a" +dependencies = [ + "iced_core", + "once_cell", + "palette", +] + [[package]] name = "iced_wgpu" version = "0.7.0" -source = "git+https://github.com/pop-os/libcosmic/?branch=master#444e389496bb92a928b7731175f00f51ec4f0caa" +source = "git+https://github.com/pop-os/libcosmic/?branch=master#5224c9b75c000fcf92a1851391fe1dc82486610a" dependencies = [ "bitflags", "bytemuck", @@ -2327,7 +2338,7 @@ checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" [[package]] name = "libcosmic" version = "0.1.0" -source = "git+https://github.com/pop-os/libcosmic/?branch=master#444e389496bb92a928b7731175f00f51ec4f0caa" +source = "git+https://github.com/pop-os/libcosmic/?branch=master#5224c9b75c000fcf92a1851391fe1dc82486610a" dependencies = [ "apply", "cosmic-panel-config", @@ -2336,7 +2347,6 @@ dependencies = [ "freedesktop-icons", "iced", "iced_core", - "iced_glow", "iced_lazy", "iced_native", "iced_style", @@ -2364,9 +2374,9 @@ checksum = "348108ab3fba42ec82ff6e9564fc4ca0247bdccdc68dd8af9764bbc79c3c8ffb" [[package]] name = "libpulse-binding" -version = "2.26.0" +version = "2.27.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17be42160017e0ae993c03bfdab4ecb6f82ce3f8d515bd8da8fdf18d10703663" +checksum = "1745b20bfc194ac12ef828f144f0ec2d4a7fe993281fa3567a0bd4969aee6890" dependencies = [ "bitflags", "libc", @@ -2378,9 +2388,9 @@ dependencies = [ [[package]] name = "libpulse-glib-binding" -version = "2.25.1" +version = "2.27.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df0e7a964c9f7e95d4f073affc19adfda009fa0d55e8831dbb66c78be1d0e6e5" +checksum = "d39d9166164cf39b619f6a029ffafac958e718a10dabdc35bcebf8f69b5fa3cf" dependencies = [ "glib", "glib-sys", @@ -2390,9 +2400,9 @@ dependencies = [ [[package]] name = "libpulse-mainloop-glib-sys" -version = "1.19.2" +version = "1.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36f61c4064926cc77ea14bb206a21ce1d5a06e175e5c0ce078804bb6c4527b28" +checksum = "9b97cd2ed4e84e54f3825b85648ec8637bec273ea7fcb981032b0a575dfef697" dependencies = [ "glib-sys", "libpulse-sys", @@ -2401,9 +2411,9 @@ dependencies = [ [[package]] name = "libpulse-sys" -version = "1.19.3" +version = "1.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "991e6bd0efe2a36e6534e136e7996925e4c1a8e35b7807fe533f2beffff27c30" +checksum = "2191e6880818d1df4cf72eac8e91dce7a5a52ba0da4b2a5cdafabc22b937eadb" dependencies = [ "libc", "num-derive", @@ -2467,8 +2477,9 @@ dependencies = [ [[package]] name = "logind-zbus" -version = "3.0.3" -source = "git+https://github.com/pop-os/logind-zbus?branch=main#0789bde15b61b3f65b1e028841eeb5411f66f474" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f2cfc54565c8d002ad7344ec08ce512c269b2de56dea59850708691e4b18fe3" dependencies = [ "serde", "zbus", @@ -2842,11 +2853,11 @@ dependencies = [ [[package]] name = "owned_ttf_parser" -version = "0.17.1" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18904d3c65493a9f0d7542293d1a7f69bfdc309a6b9ef4f46dc3e58b0577edc5" +checksum = "2a5f3c7ca08b6879e7965fb25e24d1f5eeb32ea73f9ad99b3854778a38c57e93" dependencies = [ - "ttf-parser 0.17.1", + "ttf-parser 0.18.1", ] [[package]] @@ -3265,9 +3276,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.7.0" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e076559ef8e241f2ae3479e36f97bd5741c0330689e217ad51ce2c76808b868a" +checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733" dependencies = [ "aho-corasick", "memchr", @@ -3691,12 +3702,17 @@ dependencies = [ [[package]] name = "softbuffer" -version = "0.1.1" -source = "git+https://github.com/rust-windowing/softbuffer?rev=d5bb2c1#d5bb2c1c78811854d11225ff7cc29f0062781333" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3177eca2c15033e254b9b70c4915150200b1cf6fa777de18be9977ae5850077f" dependencies = [ + "bytemuck", + "cfg_aliases", "cocoa", "core-graphics", + "fastrand", "foreign-types", + "log", "nix 0.26.1", "objc", "raw-window-handle", @@ -3709,6 +3725,7 @@ dependencies = [ "web-sys", "windows-sys 0.42.0", "x11-dl", + "x11rb", ] [[package]] @@ -3953,9 +3970,9 @@ dependencies = [ [[package]] name = "tokio" -version = "1.23.0" +version = "1.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eab6d665857cc6ca78d6e80303a02cea7a7851e85dfbd77cbdc09bd129f1ef46" +checksum = "1d9f76183f91ecfb55e1d7d5602bd1d979e38a3a522fe900241cf195624d67ae" dependencies = [ "autocfg", "bytes", @@ -4036,6 +4053,12 @@ version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "375812fa44dab6df41c195cd2f7fecb488f6c09fbaafb62807488cefab642bff" +[[package]] +name = "ttf-parser" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0609f771ad9c6155384897e1df4d948e692667cc0588548b68eb44d052b27633" + [[package]] name = "twox-hash" version = "1.6.3" @@ -4058,9 +4081,9 @@ dependencies = [ [[package]] name = "typed-arena" -version = "2.0.1" +version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0685c84d5d54d1c26f7d3eb96cd41550adb97baed141a761cf335d3d33bcd0ae" +checksum = "6af6ae20167a9ece4bcb41af5b80f8a1f1df981f6391189ce00fd257af04126a" [[package]] name = "typenum" @@ -4599,6 +4622,15 @@ dependencies = [ "winapi", ] +[[package]] +name = "winapi-wsapoll" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44c17110f57155602a80dca10be03852116403c9ff3cd25b079d666f2aa3df6e" +dependencies = [ + "winapi", +] + [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" @@ -4716,6 +4748,31 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "x11rb" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdf3c79412dd91bae7a7366b8ad1565a85e35dd049affc3a6a2c549e97419617" +dependencies = [ + "gethostname", + "libc", + "libloading", + "nix 0.25.1", + "once_cell", + "winapi", + "winapi-wsapoll", + "x11rb-protocol", +] + +[[package]] +name = "x11rb-protocol" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0b1513b141123073ce54d5bb1d33f801f17508fbd61e02060b1214e96d39c56" +dependencies = [ + "nix 0.25.1", +] + [[package]] name = "xcursor" version = "0.3.4" @@ -4737,7 +4794,7 @@ dependencies = [ [[package]] name = "xdg-shell-wrapper-config" version = "0.1.0" -source = "git+https://github.com/pop-os/xdg-shell-wrapper#82ce268c9c560be3728190a2255c849d3bae77e7" +source = "git+https://github.com/pop-os/xdg-shell-wrapper#95f6a663e383d3f6f13f689f9e9d7e691b9fe28b" dependencies = [ "serde", "wayland-protocols-wlr", @@ -4785,8 +4842,9 @@ checksum = "c03b3e19c937b5b9bd8e52b1c88f30cce5c0d33d676cf174866175bb794ff658" [[package]] name = "zbus" -version = "3.6.2" -source = "git+https://gitlab.freedesktop.org/dbus/zbus?branch=main#0b623738048395cdf398c18be24c9f00d8fdab58" +version = "3.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "379d587c0ccb632d1179cf44082653f682842f0535f0fdfaefffc34849cc855e" dependencies = [ "async-broadcast", "async-executor", @@ -4819,13 +4877,14 @@ dependencies = [ "winapi", "zbus_macros", "zbus_names", - "zvariant 3.10.0", + "zvariant", ] [[package]] name = "zbus_macros" -version = "3.6.2" -source = "git+https://gitlab.freedesktop.org/dbus/zbus?branch=main#0b623738048395cdf398c18be24c9f00d8fdab58" +version = "3.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66492a2e90c0df7190583eccb8424aa12eb7ff06edea415a4fff6688fae18cf8" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -4837,11 +4896,12 @@ dependencies = [ [[package]] name = "zbus_names" version = "2.5.0" -source = "git+https://gitlab.freedesktop.org/dbus/zbus?branch=main#0b623738048395cdf398c18be24c9f00d8fdab58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f34f314916bd89bdb9934154627fab152f4f28acdda03e7c4c68181b214fe7e3" dependencies = [ "serde", "static_assertions", - "zvariant 3.10.0", + "zvariant", ] [[package]] @@ -4850,48 +4910,25 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c110ba09c9b3a43edd4803d570df0da2414fed6e822e22b976a4e3ef50860701" -[[package]] -name = "zvariant" -version = "3.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56f8c89c183461e11867ded456db252eae90874bc6769b7adbea464caa777e51" -dependencies = [ - "byteorder", - "libc", - "serde", - "static_assertions", - "zvariant_derive 3.9.0", -] - [[package]] name = "zvariant" version = "3.10.0" -source = "git+https://gitlab.freedesktop.org/dbus/zbus?branch=main#0b623738048395cdf398c18be24c9f00d8fdab58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "576cc41e65c7f283e5460f5818073e68fb1f1631502b969ef228c2e03c862efb" dependencies = [ "byteorder", "enumflags2", "libc", "serde", "static_assertions", - "zvariant_derive 3.10.0", -] - -[[package]] -name = "zvariant_derive" -version = "3.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "155247a5d1ab55e335421c104ccd95d64f17cebbd02f50cdbc1c33385f9c4d81" -dependencies = [ - "proc-macro-crate", - "proc-macro2", - "quote", - "syn", + "zvariant_derive", ] [[package]] name = "zvariant_derive" version = "3.10.0" -source = "git+https://gitlab.freedesktop.org/dbus/zbus?branch=main#0b623738048395cdf398c18be24c9f00d8fdab58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fd4aafc0dee96ae7242a24249ce9babf21e1562822f03df650d4e68c20e41ed" dependencies = [ "proc-macro-crate", "proc-macro2", diff --git a/Cargo.toml b/Cargo.toml index 98eaaa3b..5d506a12 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,8 +12,5 @@ members = [ "cosmic-applet-workspaces", ] -[patch.crates-io] -zbus = {git = "https://gitlab.freedesktop.org/dbus/zbus", branch = "main"} - [profile.release] lto = "fat" diff --git a/cosmic-applet-network/Cargo.toml b/cosmic-applet-network/Cargo.toml index 18e681db..52278aed 100644 --- a/cosmic-applet-network/Cargo.toml +++ b/cosmic-applet-network/Cargo.toml @@ -6,18 +6,20 @@ license = "GPL-3.0-or-later" [dependencies] once_cell = "1.16.0" -cosmic-dbus-networkmanager = { git = "https://github.com/pop-os/dbus-settings-bindings", branch = "deps" } +cosmic-dbus-networkmanager = { git = "https://github.com/pop-os/dbus-settings-bindings", branch = "main" } +# cosmic-dbus-networkmanager = { path = "../../../dbus-settings-bindings/networkmanager" } futures-util = "0.3.21" -libcosmic = { git = "https://github.com/pop-os/libcosmic/", branch = "master", default-features = false, features = ["wayland", "applet"] } +libcosmic = { git = "https://github.com/pop-os/libcosmic/", branch = "master", default-features = false, features = ["wayland", "applet", "tokio"] } sctk = { package = "smithay-client-toolkit", git = "https://github.com/Smithay/client-toolkit", rev = "3776d4a" } futures = "0.3" -zbus = { version = "3.6.2", no-default-features = true } +zbus = { version = "3.7", no-default-features = true } log = "0.4" pretty_env_logger = "0.4" +itertools = "0.10.3" +slotmap = "1.0.6" +tokio = { version = "1.15.0", features = ["full"] } +anyhow = "1.0" # 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" -slotmap = "1.0.6" -tokio = { version = "1.15.0", features = ["full"] } diff --git a/cosmic-applet-network/i18n/en/cosmic_applet_network.ftl b/cosmic-applet-network/i18n/en/cosmic_applet_network.ftl index e19faeed..1a107ca3 100644 --- a/cosmic-applet-network/i18n/en/cosmic_applet_network.ftl +++ b/cosmic-applet-network/i18n/en/cosmic_applet_network.ftl @@ -5,3 +5,12 @@ ipv4 = IPv4 Address ipv6 = IPv6 Address mac = MAC megabits-per-second = Mbps +connected = Connected +connecting = Connecting +connect = Connect +cancel = Cancel +visible-wireless-networks = Visible Wireless Networks +enter-password = Enter the password or encryption key +router-wps-button = You can also connect by pressing the "WIPS" button on the router +unable-to-connect = Unable to connect to network +check-wifi-connection = Make sure Wi-Fi is connected to the internet and the password is correct \ No newline at end of file diff --git a/cosmic-applet-network/src/app.rs b/cosmic-applet-network/src/app.rs index 92949f8b..e1f38a35 100644 --- a/cosmic-applet-network/src/app.rs +++ b/cosmic-applet-network/src/app.rs @@ -1,3 +1,4 @@ +use cosmic::iced_style; use cosmic::{ applet::CosmicAppletHelper, iced::{ @@ -6,17 +7,24 @@ use cosmic::{ popup::{destroy_popup, get_popup}, SurfaceIdWrapper, }, - widget::{column, container, row, scrollable, text}, + widget::{column, container, row, scrollable, text, text_input, Column}, Alignment, Application, Color, Command, Length, Subscription, }, - iced_native::window, - iced_style::{application, svg}, + iced_native::{ + alignment::{Horizontal, Vertical}, + layout::Limits, + renderer::BorderRadius, + subscription, window, + }, + iced_style::{application, button::StyleSheet, svg}, theme::{Button, Svg}, widget::{button, horizontal_rule, icon, list_column, toggler}, Element, Theme, }; +use cosmic_dbus_networkmanager::{access_point, interface::enums::DeviceState}; use futures::channel::mpsc::UnboundedSender; +use crate::network_manager::NetworkManagerState; use crate::{ config, fl, network_manager::{ @@ -30,24 +38,47 @@ pub fn run() -> cosmic::iced::Result { CosmicNetworkApplet::run(helper.window_settings()) } -#[derive(Clone, Default)] +#[derive(Debug)] +enum NewConnectionState { + EnterPassword { + access_point: AccessPoint, + password: String, + }, + Waiting(AccessPoint), + Failure(AccessPoint), +} + +impl Into for NewConnectionState { + fn into(self) -> AccessPoint { + match self { + NewConnectionState::EnterPassword { + access_point, + password, + } => access_point, + NewConnectionState::Waiting(access_point) => access_point, + NewConnectionState::Failure(access_point) => access_point, + } + } +} + +#[derive(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_state: NetworkManagerState, + // UI state nm_sender: Option>, + show_visible_networks: bool, + new_connection: Option, } impl CosmicNetworkApplet { fn update_icon_name(&mut self) { self.icon_name = self + .nm_state .active_conns .iter() .fold("network-offline-symbolic", |icon_name, conn| { @@ -69,13 +100,18 @@ impl CosmicNetworkApplet { #[derive(Debug, Clone)] enum Message { + ActivateKnownWifi(String), TogglePopup, ToggleAirplaneMode(bool), ToggleWiFi(bool), + ToggleVisibleNetworks, Errored(String), Ignore, NetworkManagerEvent(NetworkManagerEvent), - SelectWirelessAccessPoint(String), + SelectWirelessAccessPoint(AccessPoint), + CancelNewConnection, + Password(String), + SubmitPassword, } impl Application for CosmicNetworkApplet { @@ -109,77 +145,176 @@ impl Application for CosmicNetworkApplet { let new_id = window::Id::new(self.id_ctr); self.popup.replace(new_id); - let popup_settings = self.applet_helper.get_popup_settings( + let mut popup_settings = self.applet_helper.get_popup_settings( window::Id::new(0), new_id, None, None, None, ); + popup_settings.positioner.size_limits = Limits::NONE + .min_height(1) + .min_width(1) + .max_height(600) + .max_width(600); return get_popup(popup_settings); } } Message::Errored(_) => todo!(), Message::Ignore => {} Message::ToggleAirplaneMode(enabled) => { - self.airplane_mode = enabled; - // TODO apply changes + self.nm_state.airplane_mode = enabled; + if let Some(tx) = self.nm_sender.as_mut() { + let _ = tx.unbounded_send(NetworkManagerRequest::SetAirplaneMode(enabled)); + } } Message::ToggleWiFi(enabled) => { - self.wifi = enabled; + if !enabled { + self.nm_state.clear(); + } + self.nm_state.wifi_enabled = 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, - } => { + NetworkManagerEvent::Init { sender, state } => { 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.nm_state = state; self.update_icon_name(); } NetworkManagerEvent::WiFiEnabled(enabled) => { - self.wifi = enabled; + if !enabled { + self.nm_state.clear(); + } + self.nm_state.wifi_enabled = enabled; } NetworkManagerEvent::WirelessAccessPoints(access_points) => { - self.wireless_access_points = access_points; + self.nm_state.wireless_access_points = access_points; } NetworkManagerEvent::ActiveConns(conns) => { - self.active_conns = conns; + self.nm_state.active_conns = conns; self.update_icon_name(); } NetworkManagerEvent::RequestResponse { - wireless_access_points, - active_conns, - wifi_enabled, + state, success, - .. + req, } => { if success { - self.wireless_access_points = wireless_access_points; - self.active_conns = active_conns; - self.wifi = wifi_enabled; - self.update_icon_name(); + match req { + NetworkManagerRequest::SetAirplaneMode(_) + | NetworkManagerRequest::SetWiFi(_) => {} + NetworkManagerRequest::SelectAccessPoint(_) + | NetworkManagerRequest::Password(_, _) => { + self.new_connection.take(); + self.show_visible_networks = false; + } + } + } else { + match req { + NetworkManagerRequest::SetAirplaneMode(_) + | NetworkManagerRequest::SetWiFi(_) => {} + NetworkManagerRequest::SelectAccessPoint(_) => { + if let Some(NewConnectionState::Waiting(access_point)) = + self.new_connection.as_ref() + { + self.new_connection + .replace(NewConnectionState::Failure(access_point.clone())); + } + } + NetworkManagerRequest::Password(_, _) => { + if let Some(NewConnectionState::EnterPassword { + access_point, + .. + }) = self.new_connection.as_ref() + { + self.new_connection + .replace(NewConnectionState::Failure(access_point.clone())); + } + } + } } + self.nm_state = state; + self.update_icon_name(); } }, - Message::SelectWirelessAccessPoint(ssid) => { - if let Some(tx) = self.nm_sender.as_ref() { - let _ = tx.unbounded_send(NetworkManagerRequest::SelectAccessPoint(ssid)); + Message::SelectWirelessAccessPoint(access_point) => { + let tx = if let Some(tx) = self.nm_sender.as_ref() { + tx + } else { + return Command::none(); + }; + + let _ = tx.unbounded_send(NetworkManagerRequest::SelectAccessPoint( + access_point.ssid.clone(), + )); + + self.new_connection + .replace(NewConnectionState::EnterPassword { + access_point, + password: String::new(), + }); + } + Message::ToggleVisibleNetworks => { + self.new_connection.take(); + self.show_visible_networks = !self.show_visible_networks; + } + Message::Password(entered_pw) => match &mut self.new_connection { + Some(NewConnectionState::EnterPassword { password, .. }) => { + *password = entered_pw; } + _ => {} + }, + Message::SubmitPassword => { + // save password + let tx = if let Some(tx) = self.nm_sender.as_ref() { + tx + } else { + return Command::none(); + }; + + match self.new_connection.take() { + Some(NewConnectionState::EnterPassword { + password, + access_point, + }) => { + let _ = tx.unbounded_send(NetworkManagerRequest::Password( + access_point.ssid.clone(), + password.to_string(), + )); + self.new_connection + .replace(NewConnectionState::Waiting(access_point.clone())); + } + _ => {} + }; + } + Message::ActivateKnownWifi(ssid) => { + let tx = if let Some(tx) = self.nm_sender.as_ref() { + tx + } else { + return Command::none(); + }; + let _ = tx.unbounded_send(NetworkManagerRequest::SelectAccessPoint(ssid)); + } + Message::CancelNewConnection => { + self.new_connection.take(); } } Command::none() } fn view(&self, id: SurfaceIdWrapper) -> Element { + let button_style = Button::Custom { + active: |t| iced_style::button::Appearance { + border_radius: BorderRadius::from(0.0), + ..t.active(&Button::Text) + }, + hover: |t| iced_style::button::Appearance { + border_radius: BorderRadius::from(0.0), + ..t.hovered(&Button::Text) + }, + }; match id { SurfaceIdWrapper::LayerSurface(_) => unimplemented!(), SurfaceIdWrapper::Window(_) => self @@ -188,39 +323,21 @@ impl Application for CosmicNetworkApplet { .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 { - color: 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 { + let mut vpn_ethernet_col = column![]; + let mut known_wifi = column![]; + for conn in &self.nm_state.active_conns { + match conn { ActiveConnectionInfo::Vpn { name, ip_addresses } => { - let mut ipv4 = column![]; - let mut ipv6 = column![]; + let mut ipv4 = Vec::with_capacity(ip_addresses.len()); 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() - ))); - } - } + ipv4.push( + text(format!("{}: {}", fl!("ipv4"), addr.to_string())) + .size(12) + .into(), + ); } - column![text(name), ipv4, ipv6].spacing(4) + vpn_ethernet_col = vpn_ethernet_col + .push(column![text(name), Column::with_children(ipv4)].spacing(4)); } ActiveConnectionInfo::Wired { name, @@ -228,56 +345,99 @@ impl Application for CosmicNetworkApplet { speed, ip_addresses, } => { - let mut ipv4 = column![]; - let mut ipv6 = column![]; + let mut ipv4 = Vec::with_capacity(ip_addresses.len()); 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() - ))); - } - } + ipv4.push( + text(format!("{}: {}", fl!("ipv4"), addr.to_string())) + .size(12) + .into(), + ); } - column![ - row![ - text(name), - text(format!("{speed} {}", fl!("megabits-per-second"))) + vpn_ethernet_col = vpn_ethernet_col.push( + column![ + row![ + text(name), + text(format!("{speed} {}", fl!("megabits-per-second"))) + ] + .spacing(16), + Column::with_children(ipv4), ] - .spacing(16), - ipv4, - ipv6, - text(format!("{}: {hw_address}", fl!("mac"))), - ] - .spacing(4) + .spacing(4), + ); } ActiveConnectionInfo::WiFi { - name, hw_address, .. - } => column![row![ - text(name), - text(format!("{}: {hw_address}", fl!("mac"))) - ] - .spacing(12)] - .spacing(4), + name, ip_addresses, .. + } => { + let mut ipv4 = Vec::with_capacity(ip_addresses.len()); + for addr in ip_addresses { + ipv4.push( + text(format!("{}: {}", fl!("ipv4"), addr.to_string())) + .size(12) + .into(), + ); + } + known_wifi = known_wifi.push(column![button(Button::Secondary) + .custom(vec![ + icon("network-wireless-symbolic", 24) + .style(Svg::Custom(|theme| svg::Appearance { + color: Some(theme.palette().text), + })) + .width(Length::Units(24)) + .height(Length::Units(24)) + .into(), + column![text(name).size(14), Column::with_children(ipv4)] + .into(), + text(format!("{}", fl!("connected"))) + .size(14) + .width(Length::Fill) + .height(Length::Units(24)) + .horizontal_alignment(Horizontal::Right) + .vertical_alignment(Vertical::Center) + .into() + ]) + .padding([8, 24]) + .style(button_style.clone())]); + } }; - list_col = list_col.add(el); + } + for known in &self.nm_state.known_access_points { + let mut btn = button(Button::Secondary) + .custom(vec![ + icon("network-wireless-symbolic", 24) + .style(Svg::Custom(|theme| svg::Appearance { + color: Some(theme.palette().text), + })) + .width(Length::Units(24)) + .height(Length::Units(24)) + .into(), + text(&known.ssid).size(14).into(), + ]) + .padding([8, 24]) + .width(Length::Fill) + .style(button_style.clone()); + btn = match known.state { + // DeviceState::Prepare => todo!(), + // DeviceState::Config => todo!(), + // DeviceState::NeedAuth => todo!(), + // DeviceState::IpConfig => todo!(), + // DeviceState::IpCheck => todo!(), + // DeviceState::Secondaries => todo!(), + DeviceState::Failed + | DeviceState::Unknown + | DeviceState::Unmanaged + | DeviceState::Disconnected + | DeviceState::NeedAuth => { + btn.on_press(Message::ActivateKnownWifi(known.ssid.clone())) + } + _ => btn, + }; + known_wifi = known_wifi.push(row![btn].align_items(Alignment::Center)); } let mut content = column![ - row![icon, name].spacing(8).width(Length::Fill), - list_col, - horizontal_rule(1), + vpn_ethernet_col, container( - toggler(fl!("airplane-mode"), self.airplane_mode, |m| { + toggler(fl!("airplane-mode"), self.nm_state.airplane_mode, |m| { Message::ToggleAirplaneMode(m) }) .width(Length::Fill) @@ -285,40 +445,201 @@ impl Application for CosmicNetworkApplet { .padding([0, 12]), horizontal_rule(1), container( - toggler(fl!("wifi"), self.wifi, |m| { Message::ToggleWiFi(m) }) - .width(Length::Fill) + toggler(fl!("wifi"), self.nm_state.wifi_enabled, |m| { + Message::ToggleWiFi(m) + }) + .width(Length::Fill) ) .padding([0, 12]), + horizontal_rule(1), + known_wifi, ] .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); + .padding([8, 0]); + let dropdown_icon = if self.show_visible_networks { + "go-down-symbolic" + } else { + "go-next-symbolic" + }; + let available_connections_btn = button(Button::Secondary) + .custom( + vec![ + text(fl!("visible-wireless-networks")) + .size(14) + .width(Length::Fill) + .height(Length::Units(24)) + .vertical_alignment(Vertical::Center) + .into(), + container( + icon(dropdown_icon, 14) + .style(Svg::Custom(|theme| svg::Appearance { + color: Some(theme.palette().text), + })) + .width(Length::Units(14)) + .height(Length::Units(14)), + ) + .align_x(Horizontal::Center) + .align_y(Vertical::Center) + .width(Length::Units(24)) + .height(Length::Units(24)) + .into(), + ] + .into(), + ) + .padding([8, 24]) + .style(button_style.clone()) + .on_press(Message::ToggleVisibleNetworks); + content = content.push(available_connections_btn); + if self.show_visible_networks { + if let Some(new_conn_state) = self.new_connection.as_ref() { + match new_conn_state { + NewConnectionState::EnterPassword { + access_point, + password, + } => { + let id = row![ + icon("network-wireless-symbolic", 24) + .style(Svg::Custom(|theme| svg::Appearance { + color: Some(theme.palette().text), + })) + .width(Length::Units(24)) + .height(Length::Units(24)), + text(&access_point.ssid).size(14), + ] + .align_items(Alignment::Center) + .width(Length::Fill) + .padding([0, 24]) + .spacing(12); + content = content.push(id); + let col = column![ + text(fl!("enter-password")), + text_input("", password, Message::Password) + .on_submit(Message::SubmitPassword) + .password(), + container(text(fl!("router-wps-button"))).padding(8), + row![ + button(Button::Secondary) + .custom(vec![container(text(fl!("cancel"))) + .padding([0, 24]) + .into()]) + .on_press(Message::CancelNewConnection), + button(Button::Secondary) + .custom(vec![container(text(fl!("connect"))) + .padding([0, 24]) + .into()]) + .on_press(Message::SubmitPassword) + ] + .spacing(24) + ] + .spacing(8) + .padding([0, 48]) + .align_items(Alignment::Center); + content = content.push(col); + } + NewConnectionState::Waiting(access_point) => { + let id = row![ + icon("network-wireless-symbolic", 24) + .style(Svg::Custom(|theme| svg::Appearance { + color: Some(theme.palette().text), + })) + .width(Length::Units(24)) + .height(Length::Units(24)), + text(&access_point.ssid).size(14), + ] + .align_items(Alignment::Center) + .width(Length::Fill) + .spacing(12); + let connecting = row![ + id, + icon("process-working-symbolic", 24) + .style(Svg::Custom(|theme| svg::Appearance { + color: Some(theme.palette().text), + })) + .width(Length::Units(24)) + .height(Length::Units(24)), + ] + .spacing(8) + .padding([0, 24]); + content = content.push(connecting); + } + NewConnectionState::Failure(access_point) => { + let id = row![ + icon("network-wireless-symbolic", 24) + .style(Svg::Custom(|theme| svg::Appearance { + color: Some(theme.palette().text), + })) + .width(Length::Units(24)) + .height(Length::Units(24)), + text(&access_point.ssid).size(14), + ] + .align_items(Alignment::Center) + .width(Length::Fill) + .padding([0, 24]) + .spacing(12); + content = content.push(id); + let col = column![ + text(fl!("unable-to-connect")), + text(fl!("check-wifi-connection")), + row![ + button(Button::Secondary) + .custom(vec![container(text("Cancel")) + .padding([0, 24]) + .into()]) + .on_press(Message::CancelNewConnection), + button(Button::Secondary) + .custom(vec![container(text("Connect")) + .padding([0, 24]) + .into()]) + .on_press(Message::SelectWirelessAccessPoint( + access_point.clone() + )) + ] + .spacing(24) + ] + .spacing(16) + .padding([0, 48]) + .align_items(Alignment::Center); + content = content.push(col); + } + } + } else if self.nm_state.wifi_enabled { + let mut list_col = + Vec::with_capacity(self.nm_state.wireless_access_points.len()); + for ap in &self.nm_state.wireless_access_points { + if self + .nm_state + .active_conns + .iter() + .any(|a| ap.ssid == a.name()) + { + continue; + } + let button = button(button_style) + .custom(vec![row![ + icon("network-wireless-symbolic", 16) + .style(Svg::Custom(|theme| svg::Appearance { + color: Some(theme.palette().text), + })) + .width(Length::Units(16)) + .height(Length::Units(16)), + text(&ap.ssid) + .size(14) + .height(Length::Units(24)) + .vertical_alignment(Vertical::Center) + ] + .align_items(Alignment::Center) + .spacing(12) + .into()]) + .on_press(Message::SelectWirelessAccessPoint(ap.clone())) + .width(Length::Fill) + .padding([8, 24]); + list_col.push(button.into()); + } + content = content.push( + scrollable(Column::with_children(list_col)).height(Length::Units(300)), + ); } - content = content.push(scrollable(list_col).height(Length::Units(300))); } self.applet_helper.popup_container(content).into() } diff --git a/cosmic-applet-network/src/network_manager/available_wifi.rs b/cosmic-applet-network/src/network_manager/available_wifi.rs index d1018822..62e90e86 100644 --- a/cosmic-applet-network/src/network_manager/available_wifi.rs +++ b/cosmic-applet-network/src/network_manager/available_wifi.rs @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-or-later -use cosmic_dbus_networkmanager::device::wireless::WirelessDevice; +use cosmic_dbus_networkmanager::{device::wireless::WirelessDevice, interface::enums::DeviceState}; use futures_util::StreamExt; use itertools::Itertools; @@ -16,6 +16,13 @@ pub async fn handle_wireless_device(device: WirelessDevice<'_>) -> zbus::Result< } } let access_points = device.get_access_points().await?; + let state: DeviceState = device + .upcast() + .await + .and_then(|dev| dev.cached_state()) + .unwrap_or_default() + .map(|s| s.into()) + .unwrap_or_else(|| DeviceState::Unknown); // Sort by strength and remove duplicates let mut aps = HashMap::::new(); for ap in access_points { @@ -26,7 +33,14 @@ pub async fn handle_wireless_device(device: WirelessDevice<'_>) -> zbus::Result< continue; } } - aps.insert(ssid.clone(), AccessPoint { ssid, strength }); + aps.insert( + ssid.clone(), + AccessPoint { + ssid, + strength, + state: state, + }, + ); } let aps = aps .into_iter() @@ -40,4 +54,5 @@ pub async fn handle_wireless_device(device: WirelessDevice<'_>) -> zbus::Result< pub struct AccessPoint { pub ssid: String, pub strength: u8, + pub state: DeviceState, } diff --git a/cosmic-applet-network/src/network_manager/current_networks.rs b/cosmic-applet-network/src/network_manager/current_networks.rs index de714af6..9024770f 100644 --- a/cosmic-applet-network/src/network_manager/current_networks.rs +++ b/cosmic-applet-network/src/network_manager/current_networks.rs @@ -5,36 +5,25 @@ use cosmic_dbus_networkmanager::{ device::SpecificDevice, interface::enums::{ApFlags, ApSecurityFlags}, }; -use std::net::IpAddr; +use std::net::Ipv4Addr; pub async fn active_connections( active_connections: Vec>, ) -> zbus::Result> { let mut info = Vec::::with_capacity(active_connections.len()); for connection in active_connections { + let ipv4 = connection + .ip4_config() + .await? + .address_data() + .await + .unwrap_or_default(); + let addresses: Vec<_> = ipv4.iter().map(|d| d.address).collect(); + 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, + ip_addresses: addresses.clone(), }); continue; } @@ -46,36 +35,18 @@ pub async fn active_connections( .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, + ip_addresses: addresses.clone(), }); } 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(), + ip_addresses: addresses.clone(), hw_address: wireless_device.hw_address().await?, flags: access_point.flags().await?, rsn_flags: access_point.rsn_flags().await?, @@ -84,28 +55,9 @@ pub async fn active_connections( } } 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, + ip_addresses: addresses.clone(), }); } _ => {} @@ -131,10 +83,11 @@ pub enum ActiveConnectionInfo { name: String, hw_address: String, speed: u32, - ip_addresses: Vec, + ip_addresses: Vec, }, WiFi { name: String, + ip_addresses: Vec, hw_address: String, flags: ApFlags, rsn_flags: ApSecurityFlags, @@ -142,6 +95,16 @@ pub enum ActiveConnectionInfo { }, Vpn { name: String, - ip_addresses: Vec, + ip_addresses: Vec, }, } + +impl ActiveConnectionInfo { + pub fn name(&self) -> String { + match &self { + ActiveConnectionInfo::Wired { name, .. } => name.clone(), + ActiveConnectionInfo::WiFi { name, .. } => name.clone(), + ActiveConnectionInfo::Vpn { name, .. } => name.clone(), + } + } +} diff --git a/cosmic-applet-network/src/network_manager/mod.rs b/cosmic-applet-network/src/network_manager/mod.rs index 615467f3..79eb869a 100644 --- a/cosmic-applet-network/src/network_manager/mod.rs +++ b/cosmic-applet-network/src/network_manager/mod.rs @@ -1,17 +1,32 @@ pub mod available_wifi; pub mod current_networks; -use std::{fmt::Debug, hash::Hash, time::Duration}; +use std::{collections::HashMap, fmt::Debug, hash::Hash, ops::Deref, time::Duration}; use cosmic::iced::{self, subscription}; use cosmic_dbus_networkmanager::{ - device::SpecificDevice, interface::enums::DeviceType, nm::NetworkManager, + active_connection::ActiveConnection, + device::SpecificDevice, + interface::{ + active_connection::ActiveConnectionProxy, enums, enums::DeviceType, + settings::connection::ConnectionSettingsProxy, + }, + nm::{self, NetworkManager}, + settings::{ + connection::{ConnectionSettings, Secrets, Settings}, + NetworkManagerSettings, + }, }; use futures::{ channel::mpsc::{unbounded, UnboundedReceiver, UnboundedSender}, + future::ok, FutureExt, StreamExt, }; -use zbus::Connection; +use tokio::{process::Command, time::timeout}; +use zbus::{ + zvariant::{self, ObjectPath, Value}, + Connection, +}; use self::{ available_wifi::{handle_wireless_device, AccessPoint}, @@ -35,7 +50,7 @@ pub enum State { Finished, } -async fn start_listening( +async fn start_listening( id: I, state: State, ) -> (Option<(I, NetworkManagerEvent)>, State) { @@ -45,59 +60,15 @@ async fn start_listening( 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); + let nm_state = NetworkManagerState::new(&conn).await.unwrap_or_default(); return ( Some(( id, NetworkManagerEvent::Init { sender: tx, - wireless_access_points, - wifi_enabled, - airplane_mode: false, - active_conns, + state: nm_state, }, )), State::Waiting(conn, rx), @@ -108,6 +79,7 @@ async fn start_listening( 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; @@ -119,47 +91,278 @@ async fn start_listening( 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::SetAirplaneMode(airplane_mode)) => { + // wifi + let mut success = network_manager.set_wireless_enabled(!airplane_mode).await.is_ok(); + // bluetooth + success = success && Command::new("rfkill") + .arg(if airplane_mode { "block" } else { "unblock" }) + .arg("bluetooth") + .output() + .await + .is_ok(); + let response = NetworkManagerEvent::RequestResponse { + req: NetworkManagerRequest::SetAirplaneMode(airplane_mode), + success: true, + state: NetworkManagerState::new(&conn).await.unwrap_or_default(), + }; + (Some((id, response)), 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), + let response = NetworkManagerEvent::RequestResponse { + req: NetworkManagerRequest::SetAirplaneMode(enabled), success, - active_conns, - wireless_access_points, - wifi_enabled: enabled, - airplane_mode: false, - })), false) + state: NetworkManagerState::new(&conn).await.unwrap_or_default(), + }; + (Some((id, response)), 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 + Some(NetworkManagerRequest::Password(ssid, password)) => { + let s = match NetworkManagerSettings::new(&conn).await { + Ok(s) => s, + Err(_) => return (None, State::Finished), + }; + + let mut status = (None, false); + + // First try known connections + // TODO more convenient methods of managing settings + for c in s.list_connections().await.unwrap_or_default() { + let mut settings = match c.get_settings().await.ok() { + Some(s) => s, + None => continue, + }; + + let cur_ssid = settings + .get("802-11-wireless") + .and_then(|w| w.get("ssid")) + .cloned() + .and_then(|ssid| ssid.try_into().ok()) + .and_then(|ssid| String::from_utf8(ssid).ok()); + if cur_ssid.as_ref() != Some(&ssid) { + continue; + } + + let mut secrets = match + c.get_secrets("802-11-wireless-security") + .await { + Ok(s) => s, + _ => HashMap::from([("802-11-wireless-security".into(), HashMap::from([ + ("psk".into(), Value::Str(password.as_str().into()).to_owned()), + ("key-mgmt".into(), Value::Str("wpa-psk".into()).to_owned()) + ]))]), + }; + if let Some(s) = secrets.get_mut("802-11-wireless-security") { + s.insert("psk".into(), Value::Str(password.clone().into()).to_owned()); + drop(s); + settings.extend(secrets.into_iter()); + let settings: HashMap<_, _> = settings.iter().map(|(k, v)| { + let map = (k.as_str(), v.iter() + .map(|(k, v)| (k.as_str(), v.into())) + .collect::>()); + map + }).collect(); + let updated = c.update(settings).await; + if updated.is_ok() { + let success = if let Ok(path) = network_manager.deref().activate_connection(c.deref().path(), &ObjectPath::try_from("/").unwrap(), &ObjectPath::try_from("/").unwrap()).await { + // let active_conn = ActiveConnection::from(ActiveConnectionProxy::from(conn.1)); + let dummy = ActiveConnectionProxy::new(&conn).await.unwrap(); + let active = ActiveConnectionProxy::builder(&conn).path(path).unwrap().destination(dummy.destination()).unwrap().interface(dummy.interface()).unwrap().build().await.unwrap(); + let state = enums::ActiveConnectionState::from(active.state().await.unwrap_or_default()); + let s = if let enums::ActiveConnectionState::Activating = state { + if let Ok(Some(s)) = timeout(Duration::from_secs(10), active.receive_state_changed().await.next()).await { + s.get().await.unwrap_or_default().into() + } else { + state + } + } else { + state + }; + matches!(s, enums::ActiveConnectionState::Activated) + } else { + false + }; + status = (Some((id, NetworkManagerEvent::RequestResponse { + req: NetworkManagerRequest::Password(ssid.clone(), password.clone()), + success, + state: NetworkManagerState::new(&conn).await.unwrap_or_default(), + })), false); + } + + break; + } + } + + // create a connection + if status.0.is_none() { + for device in network_manager.devices().await.ok().unwrap_or_default() { + if matches!(device.device_type().await.unwrap_or(DeviceType::Other), DeviceType::Wifi) { + let conn_settings: HashMap<&str, HashMap<&str, zvariant::Value>> = HashMap::from([ + ("802-11-wireless".into(), HashMap::from([ + ("ssid".into(), Value::Array(ssid.as_bytes().into())), + ])), + ("connection".into(), HashMap::from([ + ("id".into(), Value::Str(ssid.as_str().into())), + ("type".into(), Value::Str("802-11-wireless".into())), + ])), + ("802-11-wireless-security".into(), HashMap::from([ + ("psk".into(), Value::Str(password.as_str().into())), + ("key-mgmt".into(), Value::Str("wpa-psk".into())) + ])) + ]); + let success = if let Ok((_, path)) = network_manager.add_and_activate_connection(conn_settings, device.path(), &ObjectPath::try_from("/").unwrap()).await { + let dummy = ActiveConnectionProxy::new(&conn).await.unwrap(); + let active = ActiveConnectionProxy::builder(&conn).path(path).unwrap().destination(dummy.destination()).unwrap().interface(dummy.interface()).unwrap().build().await.unwrap(); + let state = enums::ActiveConnectionState::from(active.state().await.unwrap_or_default()); + let s = if let enums::ActiveConnectionState::Activating = state { + if let Ok(Some(s)) = timeout(Duration::from_secs(10), active.receive_state_changed().await.next()).await { + s.get().await.unwrap_or_default().into() + } else { + state + } + } else { + state + }; + matches!(s, enums::ActiveConnectionState::Activated) + } else { + false + }; + status = (Some((id, NetworkManagerEvent::RequestResponse { + req: NetworkManagerRequest::Password(ssid.clone(), password.clone()), + success, + state: NetworkManagerState::new(&conn).await.unwrap_or_default(), + })), false); + + break; } } } - (None, false) + + if status.0.is_none() { + status = (Some((id, NetworkManagerEvent::RequestResponse { + req: NetworkManagerRequest::Password(ssid, password), + success: false, + state: NetworkManagerState::new(&conn).await.unwrap_or_default(), + })), false); + } + + status + } + Some(NetworkManagerRequest::SelectAccessPoint(ssid)) => { + let s = match NetworkManagerSettings::new(&conn).await { + Ok(s) => s, + Err(_) => return (None, State::Finished), + }; + // find known connection with matching ssid and activate + let mut status = (None, false); + + let devices = network_manager.devices().await.ok().unwrap_or_default(); + + + for c in s.list_connections().await.unwrap_or_default() { + let settings = match c.get_settings().await.ok() { + Some(s) => s, + None => continue, + }; + + let cur_ssid = settings + .get("802-11-wireless") + .and_then(|w| w.get("ssid")) + .cloned() + .and_then(|ssid| ssid.try_into().ok()) + .and_then(|ssid| String::from_utf8(ssid).ok()); + + if cur_ssid.as_ref() != Some(&ssid) { + continue; + } + + let success = if let Ok(path) = network_manager.deref().activate_connection(c.deref().path(), &ObjectPath::try_from("/").unwrap(), &ObjectPath::try_from("/").unwrap()).await { + let dummy = ActiveConnectionProxy::new(&conn).await.unwrap(); + let active = ActiveConnectionProxy::builder(&conn).path(path).unwrap().destination(dummy.destination()).unwrap().interface(dummy.interface()).unwrap().build().await.unwrap(); + let state = enums::ActiveConnectionState::from(active.state().await.unwrap_or_default()); + let s = if let enums::ActiveConnectionState::Activating = state { + if let Ok(Some(s)) = timeout(Duration::from_secs(10), active.receive_state_changed().await.next()).await { + s.get().await.unwrap_or_default().into() + } else { + state + } + } else { + state + }; + matches!(s, enums::ActiveConnectionState::Activated) + } else { + false + }; + status = (Some((id, NetworkManagerEvent::RequestResponse { + req: NetworkManagerRequest::SelectAccessPoint(ssid.clone()), + success, + state: NetworkManagerState::new(&conn).await.unwrap_or_default(), + })), false); + + break; + } + let mut ap = None; + + for d in &devices { + if let Ok(Some(SpecificDevice::Wireless(wireless_device))) = + d.downcast_to_device().await { + for a in wireless_device.access_points().await.ok().unwrap_or_default() { + if String::from_utf8(a.ssid().await.unwrap_or_default()).unwrap_or_default() == ssid { + ap = Some(a); + break; + } + } + } + }; + if status.0.is_none() { + + for device in network_manager.devices().await.ok().unwrap_or_default() { + if matches!(device.device_type().await.unwrap_or(DeviceType::Other), DeviceType::Wifi) { + let conn_settings: HashMap<&str, HashMap<&str, zvariant::Value>> = HashMap::from([ + ("802-11-wireless".into(), HashMap::from([ + ("ssid".into(), Value::Array(ssid.as_bytes().into())), + ])), + ("connection".into(), HashMap::from([ + ("id".into(), Value::Str(ssid.as_str().into())), + ("type".into(), Value::Str("802-11-wireless".into())), + ])), + ]); + let success = if let Ok((_, path)) = network_manager.add_and_activate_connection(conn_settings, device.path(), &ap.as_ref().map(|ap| ap.path().clone()).unwrap_or_else(||ObjectPath::try_from("/").unwrap().into_owned())).await { + let dummy = ActiveConnectionProxy::new(&conn).await.unwrap(); + let active = ActiveConnectionProxy::builder(&conn).path(path).unwrap().destination(dummy.destination()).unwrap().interface(dummy.interface()).unwrap().build().await.unwrap(); + let state = enums::ActiveConnectionState::from(active.state().await.unwrap_or_default()); + let s = if let enums::ActiveConnectionState::Activating = state { + if let Ok(Some(s)) = timeout(Duration::from_secs(10), active.receive_state_changed().await.next()).await { + s.get().await.unwrap_or_default().into() + } else { + state + } + } else { + state + }; + matches!(s, enums::ActiveConnectionState::Activated) + } else { + false + }; + status = (Some((id, NetworkManagerEvent::RequestResponse { + req: NetworkManagerRequest::SelectAccessPoint(ssid.clone()), + success, + state: NetworkManagerState::new(&conn).await.unwrap_or_default(), + })), false); + + break; + } + } + } + + if status.0.is_none() { + status = (Some((id, NetworkManagerEvent::RequestResponse { + req: NetworkManagerRequest::SelectAccessPoint(ssid.clone()), + success: false, + state: NetworkManagerState::new(&conn).await.unwrap_or_default(), + })), false); + } + status } None => { (None, true) @@ -217,26 +420,126 @@ pub enum NetworkManagerRequest { SetAirplaneMode(bool), SetWiFi(bool), SelectAccessPoint(String), + Password(String, 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, + state: NetworkManagerState, success: bool, }, + Init { + sender: UnboundedSender, + state: NetworkManagerState, + }, WiFiEnabled(bool), WirelessAccessPoints(Vec), ActiveConns(Vec), } + +#[derive(Debug, Clone, Default)] +pub struct NetworkManagerState { + pub wireless_access_points: Vec, + pub active_conns: Vec, + pub known_access_points: Vec, + pub wifi_enabled: bool, + pub airplane_mode: bool, +} + +impl NetworkManagerState { + pub async fn new(conn: &Connection) -> anyhow::Result { + let network_manager = NetworkManager::new(&conn).await?; + let mut _self = Self::default(); + + // airplane mode + let airplaine_mode = Command::new("rfkill") + .arg("list") + .arg("bluetooth") + .output() + .await?; + let airplane_mode = std::str::from_utf8(&airplaine_mode.stdout).unwrap_or_default(); + let bluetooth_disabled = airplane_mode.contains("Soft blocked: yes"); + + if !network_manager.wireless_enabled().await.unwrap_or_default() { + _self.airplane_mode = bluetooth_disabled; + return Ok(_self); + } else { + _self.wifi_enabled = true; + }; + + let s = NetworkManagerSettings::new(&conn).await?; + + let known_conns = s.list_connections().await.unwrap_or_default(); + + 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 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 { + let mut access_points = f.await; + wireless_access_points.append(&mut access_points); + } + let mut known_ssid = Vec::with_capacity(known_conns.len()); + for c in known_conns { + let s = c.get_settings().await.unwrap(); + let s = Settings::new(s); + if let Some(cur_ssid) = s + .wifi + .clone() + .and_then(|w| w.ssid) + .and_then(|ssid| String::from_utf8(ssid).ok()) + { + known_ssid.push(cur_ssid); + } + } + let known_access_points: Vec<_> = wireless_access_points + .iter() + .filter(|a| { + known_ssid.contains(&a.ssid) && !active_conns.iter().any(|ac| ac.name() == a.ssid) + }) + .cloned() + .collect(); + wireless_access_points.sort_by(|a, b| b.strength.cmp(&a.strength)); + + _self.wireless_access_points = wireless_access_points; + _self.active_conns = active_conns; + _self.known_access_points = known_access_points; + Ok(_self) + } + + pub fn clear(&mut self) { + self.active_conns = Vec::new(); + self.known_access_points = Vec::new(); + self.wireless_access_points = Vec::new(); + } +} diff --git a/cosmic-applet-power/Cargo.toml b/cosmic-applet-power/Cargo.toml index 80402eac..42f746ed 100644 --- a/cosmic-applet-power/Cargo.toml +++ b/cosmic-applet-power/Cargo.toml @@ -12,13 +12,5 @@ tokio = { version = "1.20.1", features=["full"] } libcosmic = { git = "https://github.com/pop-os/libcosmic/", branch = "master", default-features = false, features = ["wayland", "applet"] } sctk = { package = "smithay-client-toolkit", git = "https://github.com/Smithay/client-toolkit", rev = "3776d4a" } nix = "0.26.1" - -# Until the 3.6.3 release, need the implementation of clone on zbus::Error -[dependencies.zbus] -git = "https://gitlab.freedesktop.org/dbus/zbus" -branch = "main" - -# Until zbus 3.6.3 is released -[dependencies.logind-zbus] -git = "https://github.com/pop-os/logind-zbus" -branch = "main" +zbus = "3.7" +logind-zbus = "3.1"