From 30d3bbf91aecb48d94003e045fe161a0ca0cc3a8 Mon Sep 17 00:00:00 2001 From: Akrm Al-Hakimi Date: Sun, 2 Nov 2025 13:20:40 -0500 Subject: [PATCH 1/4] dbus: `forget` method, disconnecting and forgetting associated networks --- nmrs-core/src/dbus.rs | 119 +++++++++++++++++++++++++++++++++ nmrs-core/src/models.rs | 1 + nmrs-ui/src/style.css | 16 +++++ nmrs-ui/src/ui/header.rs | 8 ++- nmrs-ui/src/ui/mod.rs | 16 ++++- nmrs-ui/src/ui/network_page.rs | 31 ++++++++- nmrs-ui/src/ui/networks.rs | 2 +- 7 files changed, 188 insertions(+), 5 deletions(-) diff --git a/nmrs-core/src/dbus.rs b/nmrs-core/src/dbus.rs index 591dab28..81199f8c 100644 --- a/nmrs-core/src/dbus.rs +++ b/nmrs-core/src/dbus.rs @@ -547,4 +547,123 @@ impl NetworkManager { _ => "Unknown", } } + + pub async fn forget(&self, ssid: &str) -> zbus::Result<()> { + use std::collections::HashMap; + use zvariant::{OwnedObjectPath, Value}; + + let nm = NMProxy::new(&self.conn).await?; + + // 1) Disconnect any Wi-Fi device currently connected to this SSID + let devices = nm.get_devices().await?; + for dev_path in &devices { + let dev = NMDeviceProxy::builder(&self.conn) + .path(dev_path.clone())? + .build() + .await?; + if dev.device_type().await? != 2 { + continue; + } + + let wifi = NMWirelessProxy::builder(&self.conn) + .path(dev_path.clone())? + .build() + .await?; + if let Ok(ap_path) = wifi.active_access_point().await + && ap_path.as_str() != "/" + { + let ap = NMAccessPointProxy::builder(&self.conn) + .path(ap_path.clone())? + .build() + .await?; + if let Ok(bytes) = ap.ssid().await + && std::str::from_utf8(&bytes).ok() == Some(ssid) + { + let dev_proxy: zbus::Proxy<'_> = zbus::proxy::Builder::new(&self.conn) + .destination("org.freedesktop.NetworkManager")? + .path(dev_path.clone())? + .interface("org.freedesktop.NetworkManager.Device")? + .build() + .await?; + let _ = dev_proxy.call_method("Disconnect", &()).await; + + for _ in 0..10 { + if dev.state().await? == 30 { + break; + } + tokio::time::sleep(std::time::Duration::from_millis(200)).await; + } + } + } + } + + // 2) Delete any saved profiles for this SSID + let settings: zbus::Proxy<'_> = zbus::proxy::Builder::new(&self.conn) + .destination("org.freedesktop.NetworkManager")? + .path("/org/freedesktop/NetworkManager/Settings")? + .interface("org.freedesktop.NetworkManager.Settings")? + .build() + .await?; + + let list_reply = settings.call_method("ListConnections", &()).await?; + let conns: Vec = list_reply.body().deserialize()?; + + let mut deleted_any = false; + + for cpath in conns { + let cproxy: zbus::Proxy<'_> = zbus::proxy::Builder::new(&self.conn) + .destination("org.freedesktop.NetworkManager")? + .path(cpath.clone())? + .interface("org.freedesktop.NetworkManager.Settings.Connection")? + .build() + .await?; + + if let Ok(id) = cproxy.get_property::("Id").await + && id == ssid + { + let _ = cproxy.call_method("Delete", &()).await; + deleted_any = true; + continue; + } + + if let Ok(msg) = cproxy.call_method("GetSettings", &()).await { + let body = msg.body(); + let settings_map: HashMap> = body.deserialize()?; + + if let Some(conn_sec) = settings_map.get("connection") + && let Some(Value::Str(id)) = conn_sec.get("id") + && id.as_str() == ssid + { + let _ = cproxy.call_method("Delete", &()).await; + deleted_any = true; + continue; + } + + if let Some(wifi_sec) = settings_map.get("802-11-wireless") + && let Some(Value::Array(arr)) = wifi_sec.get("ssid") + { + let mut raw = Vec::new(); + for v in arr.iter() { + if let Ok(b) = u8::try_from(v.clone()) { + raw.push(b); + } + } + if std::str::from_utf8(&raw).ok() == Some(ssid) { + let _ = cproxy.call_method("Delete", &()).await; + deleted_any = true; + continue; + } + } + } + } + + if deleted_any { + println!("Forgot and disconnected '{ssid}'"); + Ok(()) + } else { + Err(zbus::Error::Failure(format!( + "No saved connection for {ssid}" + ))) + } + } } diff --git a/nmrs-core/src/models.rs b/nmrs-core/src/models.rs index 85f7bd01..fc7f413a 100644 --- a/nmrs-core/src/models.rs +++ b/nmrs-core/src/models.rs @@ -13,6 +13,7 @@ pub struct Network { pub is_eap: bool, } +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct NetworkInfo { pub ssid: String, pub bssid: String, diff --git a/nmrs-ui/src/style.css b/nmrs-ui/src/style.css index 349ae0bd..cd68114d 100644 --- a/nmrs-ui/src/style.css +++ b/nmrs-ui/src/style.css @@ -168,3 +168,19 @@ label.network-poor { color: #ef4444; } .wifi-open { color: white; } + +.loading-spinner { + margin-top: 12px; + margin-bottom: 12px; + opacity: 0.6; +} + +.forget-button { + font-size: 0.85em; + opacity: 0.7; + padding: 2px 6px; + border-radius: 6px; +} +.forget-button:hover { + opacity: 1; +} diff --git a/nmrs-ui/src/ui/header.rs b/nmrs-ui/src/ui/header.rs index b06829fc..fb0cce28 100644 --- a/nmrs-ui/src/ui/header.rs +++ b/nmrs-ui/src/ui/header.rs @@ -37,6 +37,7 @@ pub fn build_header( let wifi_switch = Switch::new(); wifi_switch.set_valign(gtk::Align::Center); header.pack_end(&wifi_switch); + wifi_switch.set_size_request(24, 24); header.pack_end(&status); @@ -48,6 +49,7 @@ pub fn build_header( let stack_clone = stack.clone(); glib::MainContext::default().spawn_local(async move { + stack_clone.set_visible_child_name("loading"); clear_children(&list_container_clone); match NetworkManager::new().await { @@ -56,12 +58,15 @@ pub fn build_header( wifi_switch_clone.set_active(enabled); if enabled { - status_clone.set_text(""); + status_clone.set_text("Scanning..."); + let _ = nm.scan_networks().await; + tokio::time::sleep(std::time::Duration::from_secs(1)).await; match nm.list_networks().await { Ok(nets) => { let list: ListBox = networks::networks_view(&nets, &pw, &stack_clone); list_container_clone.append(&list); + stack_clone.set_visible_child_name("networks"); } Err(err) => { status_clone @@ -117,6 +122,7 @@ pub fn build_header( let list: ListBox = networks::networks_view(&nets, &pw, &stack_inner); list_container_clone.append(&list); + stack_inner.set_visible_child_name("networks"); } Err(err) => { status_clone diff --git a/nmrs-ui/src/ui/mod.rs b/nmrs-ui/src/ui/mod.rs index 6a2ace72..0085e247 100644 --- a/nmrs-ui/src/ui/mod.rs +++ b/nmrs-ui/src/ui/mod.rs @@ -5,7 +5,8 @@ pub mod networks; use gtk::prelude::*; use gtk::{ - Application, ApplicationWindow, Box as GtkBox, Label, Orientation, ScrolledWindow, Stack, + Application, ApplicationWindow, Box as GtkBox, Label, Orientation, ScrolledWindow, Spinner, + Stack, }; pub fn build_ui(app: &Application) { @@ -18,8 +19,19 @@ pub fn build_ui(app: &Application) { let list_container = GtkBox::new(Orientation::Vertical, 0); let stack = Stack::new(); + + let spinner = Spinner::new(); + spinner.set_halign(gtk::Align::Center); + spinner.set_valign(gtk::Align::Center); + spinner.set_property("width-request", 24i32); + spinner.set_property("height-request", 24i32); + spinner.add_css_class("loading-spinner"); + spinner.start(); + + stack.add_named(&spinner, Some("loading")); + stack.set_visible_child_name("loading"); + stack.add_named(&list_container, Some("networks")); - stack.set_visible_child_name("networks"); let header = header::build_header(&status, &list_container, &win, &stack); vbox.append(&header); diff --git a/nmrs-ui/src/ui/network_page.rs b/nmrs-ui/src/ui/network_page.rs index 36a927f3..f235510b 100644 --- a/nmrs-ui/src/ui/network_page.rs +++ b/nmrs-ui/src/ui/network_page.rs @@ -1,9 +1,10 @@ use glib::clone; use gtk::prelude::*; use gtk::{Align, Box, Button, Image, Label, Orientation}; +use nmrs_core::NetworkManager; use nmrs_core::models::NetworkInfo; -pub fn network_page(info: &NetworkInfo, stack: >k::Stack) -> Box { +pub fn network_page(info: NetworkInfo, stack: >k::Stack) -> Box { let container = Box::new(Orientation::Vertical, 12); container.add_css_class("network-page"); @@ -27,8 +28,36 @@ pub fn network_page(info: &NetworkInfo, stack: >k::Stack) -> Box { icon.set_pixel_size(24); let title = Label::new(Some(&info.ssid)); title.add_css_class("network-title"); + + let spacer = Box::new(Orientation::Horizontal, 0); + spacer.set_hexpand(true); + + let forget_btn = Button::with_label("Forget"); + forget_btn.add_css_class("forget-button"); + forget_btn.set_halign(Align::End); + forget_btn.set_valign(Align::Center); + forget_btn.set_cursor_from_name(Some("pointer")); + + forget_btn.connect_clicked(clone!( + #[strong] + info, + #[weak] + stack, + move |_| { + let ssid = info.ssid.clone(); + glib::MainContext::default().spawn_local(async move { + if let Ok(nm) = NetworkManager::new().await { + let _ = nm.forget(&ssid).await; + stack.set_visible_child_name("networks"); + } + }); + } + )); + header.append(&icon); header.append(&title); + header.append(&spacer); + header.append(&forget_btn); container.append(&header); // Basic info section diff --git a/nmrs-ui/src/ui/networks.rs b/nmrs-ui/src/ui/networks.rs index 3b6efbc3..6dafc822 100644 --- a/nmrs-ui/src/ui/networks.rs +++ b/nmrs-ui/src/ui/networks.rs @@ -74,7 +74,7 @@ pub fn networks_view( if let Ok(nm) = NetworkManager::new().await && let Ok(details) = nm.show_details(&net_data).await { - let container = network_page(&details, &stack); + let container = network_page(details, &stack); if let Some(old) = stack.child_by_name("details") { stack.remove(&old); From 33ea6f96220054d3bf4ee42ff8bf89cfe7ff9a7f Mon Sep 17 00:00:00 2001 From: Akrm Al-Hakimi Date: Sun, 2 Nov 2025 13:45:01 -0500 Subject: [PATCH 2/4] fix(dbus): DRAFT - `wifi_builder` debugging --- nmrs-core/src/dbus.rs | 18 ++++++++++++++++-- nmrs-core/src/wifi_builders.rs | 12 ++++++++++++ 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/nmrs-core/src/dbus.rs b/nmrs-core/src/dbus.rs index 81199f8c..da45905a 100644 --- a/nmrs-core/src/dbus.rs +++ b/nmrs-core/src/dbus.rs @@ -325,10 +325,24 @@ impl NetworkManager { } else { let specific_object = ap_path.unwrap_or_else(|| ObjectPath::from_str_unchecked("/").into()); - nm.add_and_activate_connection(settings, wifi_device.clone(), specific_object) - .await?; + match nm + .add_and_activate_connection(settings, wifi_device.clone(), specific_object) + .await + { + Ok(_) => println!("add_and_activate_connection() succeeded"), + Err(e) => { + eprintln!("add_and_activate_connection() failed: {e}"); + return Err(e); + } + } } + let dev_proxy = NMDeviceProxy::builder(&self.conn) + .path(wifi_device.clone())? + .build() + .await?; + println!("Dev state after connect(): {:?}", dev_proxy.state().await?); + println!("Connection request for '{ssid}' submitted successfully"); Ok(()) } diff --git a/nmrs-core/src/wifi_builders.rs b/nmrs-core/src/wifi_builders.rs index 1b112814..86782576 100644 --- a/nmrs-core/src/wifi_builders.rs +++ b/nmrs-core/src/wifi_builders.rs @@ -117,11 +117,14 @@ pub fn build_wifi_connection( s_conn.insert("type", Value::from("802-11-wireless")); s_conn.insert("id", Value::from(ssid.to_string())); s_conn.insert("uuid", Value::from(uuid::Uuid::new_v4().to_string())); + s_conn.insert("autoconnect", Value::from(true)); + s_conn.insert("interface-name", Value::from("wlan0")); conn.insert("connection", s_conn); let mut s_wifi = HashMap::new(); s_wifi.insert("ssid", Value::from(ssid.as_bytes().to_vec())); s_wifi.insert("mode", Value::from("infrastructure")); + s_wifi.insert("security", Value::from("802-11-wireless-security")); conn.insert("802-11-wireless", s_wifi); match security { @@ -129,11 +132,20 @@ pub fn build_wifi_connection( models::WifiSecurity::WpaPsk { psk } => { let mut s_sec = HashMap::new(); s_sec.insert("key-mgmt", Value::from("wpa-psk")); + s_sec.insert("auth-alg", Value::from(psk.to_string())); s_sec.insert("psk", Value::from(psk.to_string())); conn.insert("802-11-wireless-security", s_sec); } _ => {} } + let mut ipv4 = HashMap::new(); + ipv4.insert("method", Value::from("auto")); + conn.insert("ipv4", ipv4); + + let mut ipv6 = HashMap::new(); + ipv6.insert("method", Value::from("auto")); + conn.insert("ipv6", ipv6); + conn } From 3ca05fa1938f5d132c64d73f00a32b9c3b31c854 Mon Sep 17 00:00:00 2001 From: Akrm Al-Hakimi Date: Sun, 2 Nov 2025 20:21:21 -0500 Subject: [PATCH 3/4] fix: resolve async runtime conflict preventing WiFi connections Fixed critical bug where WiFi connection attempts would hang indefinitely after requesting a network scan. The issue was caused by mixing async runtimes - using `tokio::time::sleep()` within `glib::MainContext::spawn_local()` tasks, which are incompatible. --- Cargo.lock | 226 ++++++--------------------------- nmrs-core/src/dbus.rs | 79 +++++++----- nmrs-core/src/models.rs | 6 + nmrs-core/src/wifi_builders.rs | 7 +- nmrs-ui/src/ui/connect.rs | 17 ++- 5 files changed, 107 insertions(+), 228 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 26a8e325..4cc087ec 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,21 +2,6 @@ # It is not intended for manual editing. version = 4 -[[package]] -name = "addr2line" -version = "0.24.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" -dependencies = [ - "gimli", -] - -[[package]] -name = "adler2" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" - [[package]] name = "async-broadcast" version = "0.7.2" @@ -160,21 +145,6 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" -[[package]] -name = "backtrace" -version = "0.3.75" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002" -dependencies = [ - "addr2line", - "cfg-if", - "libc", - "miniz_oxide", - "object", - "rustc-demangle", - "windows-targets 0.52.6", -] - [[package]] name = "bitflags" version = "2.9.4" @@ -723,12 +693,6 @@ dependencies = [ "syn", ] -[[package]] -name = "gimli" -version = "0.31.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" - [[package]] name = "gio" version = "0.21.2" @@ -970,17 +934,6 @@ dependencies = [ "hashbrown", ] -[[package]] -name = "io-uring" -version = "0.7.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "046fa2d4d00aea763528b4950358d0ead425372445dc8ff86312b3c69ff7727b" -dependencies = [ - "bitflags", - "cfg-if", - "libc", -] - [[package]] name = "ipnetwork" version = "0.21.1" @@ -1049,15 +1002,6 @@ dependencies = [ "autocfg", ] -[[package]] -name = "miniz_oxide" -version = "0.8.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" -dependencies = [ - "adler2", -] - [[package]] name = "mio" version = "1.0.4" @@ -1154,15 +1098,6 @@ dependencies = [ "zbus", ] -[[package]] -name = "object" -version = "0.36.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" -dependencies = [ - "memchr", -] - [[package]] name = "once_cell" version = "1.21.3" @@ -1229,7 +1164,7 @@ dependencies = [ "libc", "redox_syscall", "smallvec", - "windows-targets 0.52.6", + "windows-targets", ] [[package]] @@ -1382,12 +1317,6 @@ dependencies = [ "syn", ] -[[package]] -name = "rustc-demangle" -version = "0.1.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" - [[package]] name = "rustc_version" version = "0.4.1" @@ -1430,9 +1359,9 @@ checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" [[package]] name = "serde" -version = "1.0.226" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dca6411025b24b60bfa7ec1fe1f8e710ac09782dca409ee8237ba74b51295fd" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" dependencies = [ "serde_core", "serde_derive", @@ -1440,18 +1369,18 @@ dependencies = [ [[package]] name = "serde_core" -version = "1.0.226" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba2ba63999edb9dac981fb34b3e5c0d111a69b0924e253ed29d83f7c99e966a4" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.226" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8db53ae22f34573731bafa1db20f04027b2d25e02d8205921b569171699cdb33" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", @@ -1611,18 +1540,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.16" +version = "2.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3467d614147380f2e4e374161426ff399c91084acd2363eaf549172b3d5e60c0" +checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "2.0.16" +version = "2.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c5e1be1c48b9172ee610da68fd9cd2770e7a4056cb3fc98710ee6906f0c7960" +checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" dependencies = [ "proc-macro2", "quote", @@ -1631,29 +1560,26 @@ dependencies = [ [[package]] name = "tokio" -version = "1.47.1" +version = "1.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038" +checksum = "ff360e02eab121e0bc37a2d3b4d4dc622e6eda3a8e5253d5435ecf5bd4c68408" dependencies = [ - "backtrace", "bytes", - "io-uring", "libc", "mio", "parking_lot", "pin-project-lite", "signal-hook-registry", - "slab", "socket2", "tokio-macros", - "windows-sys 0.59.0", + "windows-sys 0.61.0", ] [[package]] name = "tokio-macros" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" +checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" dependencies = [ "proc-macro2", "quote", @@ -1781,6 +1707,7 @@ checksum = "2f87b8aa10b915a06587d0dec516c282ff295b475d94abf425d62b57710070a2" dependencies = [ "getrandom 0.3.3", "js-sys", + "serde", "wasm-bindgen", ] @@ -2013,12 +1940,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -[[package]] -name = "windows-link" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" - [[package]] name = "windows-link" version = "0.2.0" @@ -2031,16 +1952,7 @@ version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-sys" -version = "0.60.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" -dependencies = [ - "windows-targets 0.53.3", + "windows-targets", ] [[package]] @@ -2049,7 +1961,7 @@ version = "0.61.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e201184e40b2ede64bc2ea34968b28e33622acdbbf37104f0e4a33f7abe657aa" dependencies = [ - "windows-link 0.2.0", + "windows-link", ] [[package]] @@ -2058,31 +1970,14 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm 0.52.6", - "windows_aarch64_msvc 0.52.6", - "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm 0.52.6", - "windows_i686_msvc 0.52.6", - "windows_x86_64_gnu 0.52.6", - "windows_x86_64_gnullvm 0.52.6", - "windows_x86_64_msvc 0.52.6", -] - -[[package]] -name = "windows-targets" -version = "0.53.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91" -dependencies = [ - "windows-link 0.1.3", - "windows_aarch64_gnullvm 0.53.0", - "windows_aarch64_msvc 0.53.0", - "windows_i686_gnu 0.53.0", - "windows_i686_gnullvm 0.53.0", - "windows_i686_msvc 0.53.0", - "windows_x86_64_gnu 0.53.0", - "windows_x86_64_gnullvm 0.53.0", - "windows_x86_64_msvc 0.53.0", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", ] [[package]] @@ -2091,96 +1986,48 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" - [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" -[[package]] -name = "windows_aarch64_msvc" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" - [[package]] name = "windows_i686_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" -[[package]] -name = "windows_i686_gnu" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" - [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" -[[package]] -name = "windows_i686_gnullvm" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" - [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" -[[package]] -name = "windows_i686_msvc" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" - [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" -[[package]] -name = "windows_x86_64_gnu" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" - [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" - [[package]] name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" -[[package]] -name = "windows_x86_64_msvc" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" - [[package]] name = "winnow" version = "0.5.40" @@ -2233,9 +2080,9 @@ dependencies = [ [[package]] name = "zbus" -version = "5.11.0" +version = "5.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d07e46d035fb8e375b2ce63ba4e4ff90a7f73cf2ffb0138b29e1158d2eaadf7" +checksum = "b622b18155f7a93d1cd2dc8c01d2d6a44e08fb9ebb7b3f9e6ed101488bad6c91" dependencies = [ "async-broadcast", "async-executor", @@ -2257,7 +2104,8 @@ dependencies = [ "serde_repr", "tracing", "uds_windows", - "windows-sys 0.60.2", + "uuid", + "windows-sys 0.61.0", "winnow 0.7.13", "zbus_macros", "zbus_names", @@ -2266,9 +2114,9 @@ dependencies = [ [[package]] name = "zbus_macros" -version = "5.11.0" +version = "5.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57e797a9c847ed3ccc5b6254e8bcce056494b375b511b3d6edcec0aeb4defaca" +checksum = "1cdb94821ca8a87ca9c298b5d1cbd80e2a8b67115d99f6e4551ac49e42b6a314" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -2293,9 +2141,9 @@ dependencies = [ [[package]] name = "zvariant" -version = "5.7.0" +version = "5.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "999dd3be73c52b1fccd109a4a81e4fcd20fab1d3599c8121b38d04e1419498db" +checksum = "2be61892e4f2b1772727be11630a62664a1826b62efa43a6fe7449521cb8744c" dependencies = [ "endi", "enumflags2", @@ -2307,9 +2155,9 @@ dependencies = [ [[package]] name = "zvariant_derive" -version = "5.7.0" +version = "5.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6643fd0b26a46d226bd90d3f07c1b5321fe9bb7f04673cb37ac6d6883885b68e" +checksum = "da58575a1b2b20766513b1ec59d8e2e68db2745379f961f86650655e862d2006" dependencies = [ "proc-macro-crate", "proc-macro2", diff --git a/nmrs-core/src/dbus.rs b/nmrs-core/src/dbus.rs index da45905a..89054d10 100644 --- a/nmrs-core/src/dbus.rs +++ b/nmrs-core/src/dbus.rs @@ -249,9 +249,10 @@ impl NetworkManager { ); let nm = NMProxy::new(&self.conn).await?; - let devices = nm.get_devices().await?; + let devices = nm.get_devices().await?; let mut wifi_device: Option = None; + for dp in devices { let dev = NMDeviceProxy::builder(&self.conn) .path(dp.clone())? @@ -259,9 +260,11 @@ impl NetworkManager { .await?; if dev.device_type().await? == 2 { wifi_device = Some(dp.clone()); + eprintln!(" Found WiFi device: {dp}"); break; } } + let wifi_device = wifi_device.ok_or(zbus::Error::Failure("no Wi-Fi device found".into()))?; @@ -271,32 +274,50 @@ impl NetworkManager { .await?; if let Some(active) = self.current_ssid().await { + eprintln!("Currently connected to: {active}"); if active == ssid { eprintln!("Already connected to {active}, skipping connect()"); return Ok(()); } else { - eprintln!("Currently connected to {active}, disconnecting before reconnecting..."); + eprintln!("Disconnecting from {active}."); if let Ok(conns) = nm.active_connections().await { for conn_path in conns { + eprintln!("Deactivating connection: {conn_path}"); let _ = nm.deactivate_connection(conn_path).await; } } - for _ in 0..10 { + for i in 0..10 { let d = NMDeviceProxy::builder(&self.conn) .path(wifi_device.clone())? .build() .await?; - if DeviceState::from(d.state().await?) == DeviceState::Disconnected { + let state = DeviceState::from(d.state().await?); + eprintln!("Loop {i}: Device state = {state:?}"); + + if state == DeviceState::Disconnected + || state == DeviceState::Unavailable + || state == DeviceState::Deactivating + { + eprintln!("Device disconneced or deactivating"); break; } - tokio::time::sleep(std::time::Duration::from_millis(300)).await; + + Delay::new(Duration::from_millis(300)).await; } + + eprintln!("Disconnect complete"); } + } else { + eprintln!("Not currently connected to any network"); } - let _ = wifi.request_scan(HashMap::new()).await; - tokio::time::sleep(std::time::Duration::from_secs(2)).await; + match wifi.request_scan(HashMap::new()).await { + Ok(_) => eprintln!("Scan requested successfully"), + Err(e) => eprintln!("Scan request FAILED: {e}"), + } + Delay::new(Duration::from_secs(3)).await; + eprintln!("Scan wait complete"); let mut ap_path: Option = None; for ap in wifi.get_all_access_points().await? { @@ -306,34 +327,30 @@ impl NetworkManager { .await?; let ssid_bytes = apx.ssid().await?; let ap_ssid = std::str::from_utf8(&ssid_bytes).unwrap_or(""); + eprintln!("Found AP: '{ap_ssid}'"); if ap_ssid == ssid { ap_path = Some(ap.clone()); + eprintln!("Matched target SSID"); break; } } + if ap_path.is_none() { + eprintln!("Could not find AP for '{ssid}'"); + } + let settings = build_wifi_connection(ssid, &creds); - if matches!(creds, crate::models::WifiSecurity::Open) { - println!("Connecting to open network '{ssid}'"); - nm.add_and_activate_connection( - settings, - wifi_device.clone(), - ObjectPath::from_str_unchecked("/").into(), - ) - .await?; - } else { - let specific_object = - ap_path.unwrap_or_else(|| ObjectPath::from_str_unchecked("/").into()); - match nm - .add_and_activate_connection(settings, wifi_device.clone(), specific_object) - .await - { - Ok(_) => println!("add_and_activate_connection() succeeded"), - Err(e) => { - eprintln!("add_and_activate_connection() failed: {e}"); - return Err(e); - } + let specific_object = ap_path.unwrap_or_else(|| ObjectPath::from_str_unchecked("/").into()); + + match nm + .add_and_activate_connection(settings, wifi_device.clone(), specific_object) + .await + { + Ok(_) => eprintln!("add_and_activate_connection() succeeded"), + Err(e) => { + eprintln!("add_and_activate_connection() failed: {e}"); + return Err(e); } } @@ -341,9 +358,9 @@ impl NetworkManager { .path(wifi_device.clone())? .build() .await?; - println!("Dev state after connect(): {:?}", dev_proxy.state().await?); + eprintln!("Dev state after connect(): {:?}", dev_proxy.state().await?); + eprintln!("---Connection request for '{ssid}' submitted successfully---"); - println!("Connection request for '{ssid}' submitted successfully"); Ok(()) } @@ -568,7 +585,6 @@ impl NetworkManager { let nm = NMProxy::new(&self.conn).await?; - // 1) Disconnect any Wi-Fi device currently connected to this SSID let devices = nm.get_devices().await?; for dev_path in &devices { let dev = NMDeviceProxy::builder(&self.conn) @@ -611,7 +627,6 @@ impl NetworkManager { } } - // 2) Delete any saved profiles for this SSID let settings: zbus::Proxy<'_> = zbus::proxy::Builder::new(&self.conn) .destination("org.freedesktop.NetworkManager")? .path("/org/freedesktop/NetworkManager/Settings")? @@ -672,7 +687,7 @@ impl NetworkManager { } if deleted_any { - println!("Forgot and disconnected '{ssid}'"); + eprintln!("Forgot and disconnected '{ssid}'"); Ok(()) } else { Err(zbus::Error::Failure(format!( diff --git a/nmrs-core/src/models.rs b/nmrs-core/src/models.rs index fc7f413a..0fa0dc7d 100644 --- a/nmrs-core/src/models.rs +++ b/nmrs-core/src/models.rs @@ -81,6 +81,8 @@ pub enum DeviceState { Prepare, Config, Activated, + Deactivating, + Failed, Other(u32), } @@ -115,6 +117,8 @@ impl From for DeviceState { 40 => DeviceState::Prepare, 50 => DeviceState::Config, 100 => DeviceState::Activated, + 110 => DeviceState::Deactivating, + 120 => DeviceState::Failed, v => DeviceState::Other(v), } } @@ -141,6 +145,8 @@ impl Display for DeviceState { DeviceState::Prepare => write!(f, "Preparing"), DeviceState::Config => write!(f, "Configuring"), DeviceState::Activated => write!(f, "Activated"), + DeviceState::Deactivating => write!(f, "Deactivating"), + DeviceState::Failed => write!(f, "Failed"), DeviceState::Other(v) => write!(f, "Other({v})"), } } diff --git a/nmrs-core/src/wifi_builders.rs b/nmrs-core/src/wifi_builders.rs index 86782576..29eb2360 100644 --- a/nmrs-core/src/wifi_builders.rs +++ b/nmrs-core/src/wifi_builders.rs @@ -124,21 +124,22 @@ pub fn build_wifi_connection( let mut s_wifi = HashMap::new(); s_wifi.insert("ssid", Value::from(ssid.as_bytes().to_vec())); s_wifi.insert("mode", Value::from("infrastructure")); - s_wifi.insert("security", Value::from("802-11-wireless-security")); - conn.insert("802-11-wireless", s_wifi); match security { models::WifiSecurity::Open => {} models::WifiSecurity::WpaPsk { psk } => { + s_wifi.insert("security", Value::from("802-11-wireless-security")); let mut s_sec = HashMap::new(); s_sec.insert("key-mgmt", Value::from("wpa-psk")); - s_sec.insert("auth-alg", Value::from(psk.to_string())); + s_sec.insert("auth-alg", Value::from("open")); s_sec.insert("psk", Value::from(psk.to_string())); conn.insert("802-11-wireless-security", s_sec); } _ => {} } + conn.insert("802-11-wireless", s_wifi); + let mut ipv4 = HashMap::new(); ipv4.insert("method", Value::from("auto")); conn.insert("ipv4", ipv4); diff --git a/nmrs-ui/src/ui/connect.rs b/nmrs-ui/src/ui/connect.rs index 781faf1b..afcfaacb 100644 --- a/nmrs-ui/src/ui/connect.rs +++ b/nmrs-ui/src/ui/connect.rs @@ -78,11 +78,16 @@ fn draw_connect_modal(parent: &ApplicationWindow, ssid: &str, is_eap: bool) { .unwrap_or_default(); let ssid = ssid_owned.clone(); - println!("User entered username={username}, password={pwd}"); + eprintln!("User entered username={username}, password={pwd}"); glib::MainContext::default().spawn_local(async move { + eprintln!("---in spawned task here--"); + eprintln!("Creating NetworkManager"); + match NetworkManager::new().await { Ok(nm) => { + println!("NetworkManager created successfully"); + let creds = if is_eap { WifiSecurity::WpaEap { opts: EapOptions { @@ -100,12 +105,16 @@ fn draw_connect_modal(parent: &ApplicationWindow, ssid: &str, is_eap: bool) { WifiSecurity::WpaPsk { psk: pwd } }; - if let Err(err) = nm.connect(&ssid, creds).await { - eprintln!("Failed to connect: {err}"); + println!("Calling nm.connect() for '{ssid}'"); + match nm.connect(&ssid, creds).await { + Ok(_) => println!("nm.connect() succeeded!"), + Err(err) => eprintln!("nm.connect() failed: {err}"), } } - Err(err) => eprintln!("Failed to init NetworkManager: {err}"), + Err(err) => eprintln!("Failed to create NetworkManager: {err}"), } + + println!("---finsihed spawned task---"); }); dialog_rc.close(); From 623c7563bf12cc4706189f15cdc9be199ec71fda Mon Sep 17 00:00:00 2001 From: akrm al-hakimi <111914307+cachebag@users.noreply.github.com> Date: Sun, 2 Nov 2025 20:26:42 -0500 Subject: [PATCH 4/4] Improve README formatting and add center alignment Updated README to enhance formatting and add an image. --- README.md | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index a2f73cea..edcaef6c 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,11 @@ ![CI](https://github.com/cachebag/nmrs/actions/workflows/ci.yml/badge.svg) -# nmrs -#### Wayland-native frontend for NetworkManager. Provides a GTK4 UI and a D-Bus proxy core, built in Rust. -image +# nmrs 🦀 +
+

Wayland-native frontend for NetworkManager. Provides a GTK4 UI and a D-Bus proxy core, built in Rust.

+
+

+ image +

#