wip: update libcosmic (#93)
* wip: update libcosmic * fix: damge issue resolved by updating iced * fix: high cpu usage by time applet and app-list * refactor subscriptions to produce fewer events * refactor network applet to use less cpu * fix: text size * refactor: i18n for audio applet * refactor: power applet i18n setup * fix (battery): always send profile update * fix (battery): set toggler width to layout correctly * fix (app-list): backoff for restarts of toplevel subscription * fix (network): alignment * feat: ask for comfirmation before applying power applet actions * wip: integrate cosmic-config * update zbus * feat: update to use latest libcosmic * update iced * udpate deps * update deps * refactor: move applet helpers to this repo, outside of libcosmic. this should help alleviate some dependency hell * chore update deps * update deps * cleanup
This commit is contained in:
parent
8b46cc209f
commit
9ebd9b511a
48 changed files with 2841 additions and 1681 deletions
56
cosmic-applet-network/src/network_manager/active_conns.rs
Normal file
56
cosmic-applet-network/src/network_manager/active_conns.rs
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
use super::{NetworkManagerEvent, NetworkManagerState};
|
||||
use cosmic::iced::{self, subscription};
|
||||
use cosmic_dbus_networkmanager::nm::NetworkManager;
|
||||
use futures::StreamExt;
|
||||
use log::error;
|
||||
use std::fmt::Debug;
|
||||
use std::hash::Hash;
|
||||
use zbus::Connection;
|
||||
|
||||
pub fn active_conns_subscription<I: 'static + Hash + Copy + Send + Sync + Debug>(
|
||||
id: I,
|
||||
conn: Connection,
|
||||
) -> iced::Subscription<(I, NetworkManagerEvent)> {
|
||||
subscription::unfold(id, State::Continue(conn), move |mut state| async move {
|
||||
loop {
|
||||
let (update, new_state) = start_listening(id, state).await;
|
||||
state = new_state;
|
||||
if let Some(update) = update {
|
||||
return (update, state);
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum State {
|
||||
Continue(Connection),
|
||||
Error,
|
||||
}
|
||||
|
||||
async fn start_listening<I: Copy + Debug>(
|
||||
id: I,
|
||||
state: State,
|
||||
) -> (Option<(I, NetworkManagerEvent)>, State) {
|
||||
let conn = match state {
|
||||
State::Continue(conn) => conn,
|
||||
State::Error => iced::futures::future::pending().await,
|
||||
};
|
||||
let network_manager = match NetworkManager::new(&conn).await {
|
||||
Ok(n) => n,
|
||||
Err(e) => {
|
||||
error!("Failed to connect to NetworkManager: {}", e);
|
||||
return (None, State::Error);
|
||||
}
|
||||
};
|
||||
|
||||
let mut active_conns_changed = network_manager.receive_active_connections_changed().await;
|
||||
active_conns_changed.next().await;
|
||||
|
||||
let new_state = NetworkManagerState::new(&conn).await.unwrap_or_default();
|
||||
|
||||
(
|
||||
Some((id, NetworkManagerEvent::ActiveConns(new_state))),
|
||||
State::Continue(conn),
|
||||
)
|
||||
}
|
||||
59
cosmic-applet-network/src/network_manager/devices.rs
Normal file
59
cosmic-applet-network/src/network_manager/devices.rs
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
use super::{NetworkManagerEvent, NetworkManagerState};
|
||||
use cosmic::iced::{self, subscription};
|
||||
use cosmic_dbus_networkmanager::nm::NetworkManager;
|
||||
use log::error;
|
||||
use std::fmt::Debug;
|
||||
use std::hash::Hash;
|
||||
use zbus::Connection;
|
||||
use futures::StreamExt;
|
||||
|
||||
pub fn devices_subscription<I: 'static + Hash + Copy + Send + Sync + Debug>(
|
||||
id: I,
|
||||
conn: Connection,
|
||||
) -> iced::Subscription<(I, NetworkManagerEvent)> {
|
||||
subscription::unfold(id, State::Continue(conn), move |mut state| async move {
|
||||
loop {
|
||||
let (update, new_state) = start_listening(id, state).await;
|
||||
state = new_state;
|
||||
if let Some(update) = update {
|
||||
return (update, state);
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum State {
|
||||
Continue(Connection),
|
||||
Error,
|
||||
}
|
||||
|
||||
async fn start_listening<I: Copy + Debug>(
|
||||
id: I,
|
||||
state: State,
|
||||
) -> (Option<(I, NetworkManagerEvent)>, State) {
|
||||
let conn = match state {
|
||||
State::Continue(conn) => conn,
|
||||
State::Error => iced::futures::future::pending().await,
|
||||
};
|
||||
let network_manager = match NetworkManager::new(&conn).await {
|
||||
Ok(n) => n,
|
||||
Err(e) => {
|
||||
error!("Failed to connect to NetworkManager: {}", e);
|
||||
return (None, State::Error);
|
||||
}
|
||||
};
|
||||
|
||||
let mut devices_changed = network_manager.receive_devices_changed().await;
|
||||
devices_changed.next().await;
|
||||
|
||||
let new_state = NetworkManagerState::new(&conn).await.unwrap_or_default();
|
||||
|
||||
(
|
||||
Some((
|
||||
id,
|
||||
NetworkManagerEvent::WirelessAccessPoints(new_state),
|
||||
)),
|
||||
State::Continue(conn),
|
||||
)
|
||||
}
|
||||
|
|
@ -1,5 +1,8 @@
|
|||
pub mod active_conns;
|
||||
pub mod available_wifi;
|
||||
pub mod current_networks;
|
||||
pub mod devices;
|
||||
pub mod wireless_enabled;
|
||||
|
||||
use std::{collections::HashMap, fmt::Debug, hash::Hash, ops::Deref, time::Duration};
|
||||
|
||||
|
|
@ -32,7 +35,9 @@ use self::{
|
|||
pub fn network_manager_subscription<I: 'static + Hash + Copy + Send + Sync + Debug>(
|
||||
id: I,
|
||||
) -> iced::Subscription<(I, NetworkManagerEvent)> {
|
||||
subscription::unfold(id, State::Ready, move |state| start_listening(id, state))
|
||||
subscription::unfold(id, State::Ready, move |state| {
|
||||
start_listening_loop(id, state)
|
||||
})
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
|
@ -42,6 +47,19 @@ pub enum State {
|
|||
Finished,
|
||||
}
|
||||
|
||||
pub async fn start_listening_loop<I: Copy + Debug>(
|
||||
id: I,
|
||||
mut state: State,
|
||||
) -> ((I, NetworkManagerEvent), State) {
|
||||
loop {
|
||||
let (update, new_state) = start_listening(id, state).await;
|
||||
state = new_state;
|
||||
if let Some(update) = update {
|
||||
return (update, state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn start_listening<I: Copy + Debug>(
|
||||
id: I,
|
||||
state: State,
|
||||
|
|
@ -59,6 +77,7 @@ async fn start_listening<I: Copy + Debug>(
|
|||
Some((
|
||||
id,
|
||||
NetworkManagerEvent::Init {
|
||||
conn: conn.clone(),
|
||||
sender: tx,
|
||||
state: nm_state,
|
||||
},
|
||||
|
|
@ -72,289 +91,402 @@ async fn start_listening<I: Copy + Debug>(
|
|||
Err(_) => return (None, State::Finished),
|
||||
};
|
||||
|
||||
let mut active_conns_changed = tokio::time::sleep(Duration::from_secs(5))
|
||||
.then(|_| async { network_manager.receive_active_connections_changed().await })
|
||||
.await;
|
||||
let mut devices_changed = network_manager.receive_devices_changed().await;
|
||||
let mut wireless_enabled_changed =
|
||||
network_manager.receive_wireless_enabled_changed().await;
|
||||
let mut req = rx.next().boxed().fuse();
|
||||
|
||||
let (update, should_exit) = futures::select! {
|
||||
req = req => {
|
||||
match req {
|
||||
Some(NetworkManagerRequest::Disconnect(ssid)) => {
|
||||
let mut success = false;
|
||||
for c in network_manager.active_connections().await.unwrap_or_default() {
|
||||
if c.id().await.unwrap_or_default() == ssid {
|
||||
if let Ok(_) = network_manager.deactivate_connection(&c).await {
|
||||
success = true;
|
||||
}
|
||||
}
|
||||
let (update, should_exit) = match rx.next().await {
|
||||
Some(NetworkManagerRequest::Disconnect(ssid)) => {
|
||||
let mut success = false;
|
||||
for c in network_manager
|
||||
.active_connections()
|
||||
.await
|
||||
.unwrap_or_default()
|
||||
{
|
||||
if c.id().await.unwrap_or_default() == ssid {
|
||||
if let Ok(_) = network_manager.deactivate_connection(&c).await {
|
||||
success = true;
|
||||
}
|
||||
(Some((id,
|
||||
NetworkManagerEvent::RequestResponse {
|
||||
}
|
||||
}
|
||||
(
|
||||
Some((
|
||||
id,
|
||||
NetworkManagerEvent::RequestResponse {
|
||||
req: NetworkManagerRequest::Disconnect(ssid.clone()),
|
||||
success,
|
||||
state: NetworkManagerState::new(&conn).await.unwrap_or_default(),
|
||||
})), false)
|
||||
},
|
||||
)),
|
||||
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,
|
||||
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 response = NetworkManagerEvent::RequestResponse {
|
||||
req: NetworkManagerRequest::SetAirplaneMode(enabled),
|
||||
success,
|
||||
state: NetworkManagerState::new(&conn).await.unwrap_or_default(),
|
||||
};
|
||||
(Some((id, response)), false)
|
||||
}
|
||||
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;
|
||||
}
|
||||
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,
|
||||
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 response = NetworkManagerEvent::RequestResponse {
|
||||
req: NetworkManagerRequest::SetAirplaneMode(enabled),
|
||||
success,
|
||||
state: NetworkManagerState::new(&conn).await.unwrap_or_default(),
|
||||
};
|
||||
(Some((id, response)), false)
|
||||
}
|
||||
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()
|
||||
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::<HashMap<_, _>>());
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
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 {
|
||||
.collect::<HashMap<_, _>>(),
|
||||
);
|
||||
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 mut state = enums::ActiveConnectionState::from(active.state().await.unwrap_or_default());
|
||||
while let enums::ActiveConnectionState::Activating = state {
|
||||
if let Ok(Some(s)) = timeout(Duration::from_secs(20), active.receive_state_changed().await.next()).await {
|
||||
state = s.get().await.unwrap_or_default().into();
|
||||
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 {
|
||||
break;
|
||||
state
|
||||
}
|
||||
} else {
|
||||
state
|
||||
};
|
||||
matches!(state, enums::ActiveConnectionState::Activated)
|
||||
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);
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if status.0.is_none() {
|
||||
status = (Some((id, NetworkManagerEvent::RequestResponse {
|
||||
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);
|
||||
|
||||
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 mut state = enums::ActiveConnectionState::from(
|
||||
active.state().await.unwrap_or_default(),
|
||||
);
|
||||
while let enums::ActiveConnectionState::Activating = state {
|
||||
if let Ok(Some(s)) = timeout(
|
||||
Duration::from_secs(20),
|
||||
active.receive_state_changed().await.next(),
|
||||
)
|
||||
.await
|
||||
{
|
||||
state = s.get().await.unwrap_or_default().into();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
matches!(state, 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)
|
||||
}
|
||||
}}
|
||||
_ = active_conns_changed.next().boxed().fuse() => {
|
||||
(Some((id, NetworkManagerEvent::ActiveConns(NetworkManagerState::new(&conn).await.unwrap_or_default()))), false)
|
||||
}
|
||||
_ = devices_changed.next().boxed().fuse() => {
|
||||
let devices = network_manager.devices().await.ok().unwrap_or_default();
|
||||
let wireless_access_point_futures: Vec<_> = devices.into_iter().map(|device| async move {
|
||||
if let Ok(Some(SpecificDevice::Wireless(wireless_device))) =
|
||||
device.downcast_to_device().await
|
||||
{
|
||||
handle_wireless_device(wireless_device).await.unwrap_or_default()
|
||||
} else {
|
||||
Vec::new()
|
||||
}
|
||||
}).collect();
|
||||
let mut wireless_access_points = Vec::with_capacity(wireless_access_point_futures.len());
|
||||
for f in wireless_access_point_futures {
|
||||
wireless_access_points.append(&mut f.await);
|
||||
state: NetworkManagerState::new(&conn)
|
||||
.await
|
||||
.unwrap_or_default(),
|
||||
},
|
||||
)),
|
||||
false,
|
||||
);
|
||||
}
|
||||
(Some((id, NetworkManagerEvent::WirelessAccessPoints(NetworkManagerState::new(&conn).await.unwrap_or_default()))), false)
|
||||
}
|
||||
enabled = wireless_enabled_changed.next().boxed().fuse() => {
|
||||
let update = if let Some(update) = enabled {
|
||||
if let Ok(_) = update.get().await {
|
||||
Some((id, NetworkManagerEvent::WiFiEnabled(NetworkManagerState::new(&conn).await.unwrap_or_default())))
|
||||
}
|
||||
else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
(update, false)
|
||||
status
|
||||
}
|
||||
None => (None, true),
|
||||
};
|
||||
drop(active_conns_changed);
|
||||
drop(wireless_enabled_changed);
|
||||
drop(req);
|
||||
(
|
||||
update,
|
||||
if should_exit {
|
||||
|
|
@ -385,6 +517,7 @@ pub enum NetworkManagerEvent {
|
|||
success: bool,
|
||||
},
|
||||
Init {
|
||||
conn: Connection,
|
||||
sender: UnboundedSender<NetworkManagerRequest>,
|
||||
state: NetworkManagerState,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -0,0 +1,56 @@
|
|||
use super::{NetworkManagerEvent, NetworkManagerState};
|
||||
use cosmic::iced::{self, subscription};
|
||||
use cosmic_dbus_networkmanager::nm::NetworkManager;
|
||||
use futures::StreamExt;
|
||||
use log::error;
|
||||
use std::fmt::Debug;
|
||||
use std::hash::Hash;
|
||||
use zbus::Connection;
|
||||
|
||||
pub fn wireless_enabled_subscription<I: 'static + Hash + Copy + Send + Sync + Debug>(
|
||||
id: I,
|
||||
conn: Connection,
|
||||
) -> iced::Subscription<(I, NetworkManagerEvent)> {
|
||||
subscription::unfold(id, State::Continue(conn), move |mut state| async move {
|
||||
loop {
|
||||
let (update, new_state) = start_listening(id, state).await;
|
||||
state = new_state;
|
||||
if let Some(update) = update {
|
||||
return (update, state);
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum State {
|
||||
Continue(Connection),
|
||||
Error,
|
||||
}
|
||||
|
||||
async fn start_listening<I: Copy + Debug>(
|
||||
id: I,
|
||||
state: State,
|
||||
) -> (Option<(I, NetworkManagerEvent)>, State) {
|
||||
let conn = match state {
|
||||
State::Continue(conn) => conn,
|
||||
State::Error => iced::futures::future::pending().await,
|
||||
};
|
||||
let network_manager = match NetworkManager::new(&conn).await {
|
||||
Ok(n) => n,
|
||||
Err(e) => {
|
||||
error!("Failed to connect to NetworkManager: {}", e);
|
||||
return (None, State::Error);
|
||||
}
|
||||
};
|
||||
|
||||
let mut wireless_enabled_changed = network_manager.receive_wireless_enabled_changed().await;
|
||||
wireless_enabled_changed.next().await;
|
||||
|
||||
let new_state = NetworkManagerState::new(&conn).await.unwrap_or_default();
|
||||
|
||||
(
|
||||
Some((id, NetworkManagerEvent::WiFiEnabled(new_state))),
|
||||
State::Continue(conn),
|
||||
)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue