diff --git a/locales/app.yml b/locales/app.yml index ba31967..f12d62f 100644 --- a/locales/app.yml +++ b/locales/app.yml @@ -49,10 +49,14 @@ menus: fr: "Déconnexion" settings: options: + power_off_adapter: + name: + en: "Power Off Adapter" + fr: "Mettre l'adaptateur hors tension" disable_device: name: - en: "Disable Wi-Fi" - fr: "Désactiver le Wi-Fi" + en: "Disable Wi-Fi/AP Device" + fr: "Désactiver le périphérique Wi-Fi/AP" switch_mode: name: en: "%{mode} Mode" @@ -85,12 +89,22 @@ menus: name: en: "Settings" fr: "Paramètres" + adapter: + options: + power_on_adapter: + name: + en: "Power On Adapter" + fr: "Mettre l'adaptateur sous tension" device: options: + power_off_adapter: + name: + en: "Power Off Adapter" + fr: "Mettre l'adaptateur hors tension" enable_device: name: - en: "Enable Wi-Fi" - fr: "Activer le Wi-Fi" + en: "Enable Wi-Fi/AP Device" + fr: "Activer le périphérique Wi-Fi/AP" notifications: station: @@ -176,20 +190,29 @@ notifications: unknown_mode: en: "Unknown mode" fr: "Mode inconnu" + adapter_powered_on: + en: "Adapter Powered On" + fr: "Adaptateur sous tension" + adapter_powered_off: + en: "Adapter Powered Off" + fr: "Adaptateur hors tension" device_enabled: - en: "Wi-Fi enabled" - fr: "Wi-Fi activé" + en: "Wi-Fi/AP device enabled" + fr: "Périphérique Wi-Fi/AP activé" device_disabled: - en: "Wi-Fi disabled" - fr: "Wi-Fi désactivé" + en: "Wi-Fi/AP device disabled" + fr: "Périphérique Wi-Fi/AP désactivé" main_menu_exited: en: "Exited main menu" fr: "Sortie du menu principal" ap_menu_exited: en: "Exited Access Point menu" fr: "Sortie du menu Point d'accès" - device_menu_exited: + adapter_menu_exited: en: "Exited adapter menu" + fr: "Sortie du menu de l'adaptateur" + device_menu_exited: + en: "Exited device menu" fr: "Sortie du menu de l'appareil" modes: diff --git a/src/app.rs b/src/app.rs index ed3a316..02bcd74 100644 --- a/src/app.rs +++ b/src/app.rs @@ -2,8 +2,8 @@ use crate::{ icons::Icons, iw::{adapter::Adapter, agent::AgentManager, known_network::KnownNetwork, network::Network}, menu::{ - ApMenuOptions, DeviceMenuOptions, KnownNetworkOptions, MainMenuOptions, Menu, - SettingsMenuOptions, + AdapterMenuOptions, ApMenuOptions, DeviceMenuOptions, KnownNetworkOptions, MainMenuOptions, + Menu, SettingsMenuOptions, }, notification::NotificationManager, }; @@ -79,15 +79,21 @@ impl App { icon_type: &str, spaces: usize, ) -> Result> { + if !self.adapter.is_powered { + self.handle_adapter_options(menu, menu_command, icon_type, spaces) + .await?; + } + if self.running && !self.adapter.device.is_enabled { + self.handle_device_options(menu, menu_command, icon_type, spaces) + .await?; + } + if !self.running { + return Ok(None); + } + while self.running { self.adapter.refresh().await?; - if !self.adapter.device.is_powered { - self.handle_device_options(menu, menu_command, icon_type, spaces) - .await?; - continue; - } - match self.adapter.device.mode { Mode::Station => { self.run_station_mode(menu, menu_command, icon_type, spaces) @@ -256,7 +262,8 @@ impl App { ) .await? { - self.handle_settings_options(option, menu).await?; + self.handle_settings_options(option, menu, menu_command, icon_type, spaces) + .await?; if !self.interactive { self.running = false; } @@ -391,7 +398,8 @@ impl App { if matches!(option, SettingsMenuOptions::Back) { stay_in_settings_menu = false; } else { - self.handle_settings_options(option, menu).await?; + self.handle_settings_options(option, menu, menu_command, icon_type, spaces) + .await?; if !self.interactive { self.running = false; stay_in_settings_menu = false; @@ -415,11 +423,20 @@ impl App { &mut self, option: SettingsMenuOptions, menu: &Menu, + menu_command: &Option, + icon_type: &str, + spaces: usize, ) -> Result { match option { SettingsMenuOptions::Back => Ok(false), + SettingsMenuOptions::PowerOffAdapter => { + self.perform_power_off_adapter(menu, menu_command, icon_type, spaces) + .await?; + Ok(false) + } SettingsMenuOptions::DisableDevice => { - self.perform_device_disable().await?; + self.perform_device_disable(menu, menu_command, icon_type, spaces) + .await?; Ok(false) } SettingsMenuOptions::SwitchMode => { @@ -431,36 +448,89 @@ impl App { } } - async fn handle_device_options( + async fn handle_adapter_options( &mut self, menu: &Menu, menu_command: &Option, icon_type: &str, spaces: usize, ) -> Result<()> { - if let Some(option) = menu.prompt_enable_device(menu_command, icon_type, spaces) { + if let Some(option) = menu.prompt_power_on_adapter(menu_command, icon_type, spaces) { match option { - DeviceMenuOptions::EnableDevice => { - self.adapter.device.power_on().await?; + AdapterMenuOptions::PowerOnAdapter => { + self.adapter.power_on().await?; self.reset(self.current_mode).await?; - info!("{}", t!("notifications.app.device_enabled")); + info!("{}", t!("notifications.app.adapter_powered_on")); try_send_notification!( self.notification_manager, None, - Some(t!("notifications.app.device_enabled").to_string()), - Some("enable_device"), + Some(t!("notifications.app.adapter_powered_on").to_string()), + Some("power_on_adapter"), None ); } } } else { - debug!("{}", t!("notifications.app.device_menu_exited")); + debug!("{}", t!("notifications.app.adapter_menu_exited")); self.running = false; } Ok(()) } + async fn handle_device_options( + &mut self, + menu: &Menu, + menu_command: &Option, + icon_type: &str, + spaces: usize, + ) -> Result<()> { + loop { + if let Some(option) = menu.prompt_enable_device(menu_command, icon_type, spaces) { + match option { + DeviceMenuOptions::PowerOffAdapter => { + self.adapter.power_off().await?; + self.reset(self.current_mode).await?; + info!("{}", t!("notifications.app.adapter_powered_off")); + try_send_notification!( + self.notification_manager, + None, + Some(t!("notifications.app.adapter_powered_off").to_string()), + Some("power_off_adapter"), + None + ); + } + DeviceMenuOptions::EnableDevice => { + self.adapter.device.enable().await?; + self.reset(self.current_mode).await?; + info!("{}", t!("notifications.app.device_enabled")); + try_send_notification!( + self.notification_manager, + None, + Some(t!("notifications.app.device_enabled").to_string()), + Some("enable_device"), + None + ); + } + } + } else { + debug!("{}", t!("notifications.app.device_menu_exited")); + self.running = false; + } + if !self.adapter.is_powered { + self.handle_adapter_options(menu, menu_command, icon_type, spaces) + .await?; + } + if !self.running { + break; + } + if self.adapter.device.is_enabled { + break; + } + } + Ok(()) + } + async fn handle_network_selection( &mut self, menu: &Menu, @@ -862,8 +932,39 @@ impl App { Ok(()) } - async fn perform_device_disable(&mut self) -> Result<()> { - self.adapter.device.power_off().await?; + async fn perform_power_off_adapter( + &mut self, + menu: &Menu, + menu_command: &Option, + icon_type: &str, + spaces: usize, + ) -> Result<()> { + self.adapter.power_off().await?; + + let msg = t!("notifications.app.adapter_powered_off").to_string(); + info!("{msg}"); + try_send_notification!( + self.notification_manager, + None, + Some(msg), + Some("power_off_adapter"), + None + ); + + self.handle_adapter_options(menu, menu_command, icon_type, spaces) + .await?; + + Ok(()) + } + + async fn perform_device_disable( + &mut self, + menu: &Menu, + menu_command: &Option, + icon_type: &str, + spaces: usize, + ) -> Result<()> { + self.adapter.device.disable().await?; let msg = t!("notifications.app.device_disabled").to_string(); info!("{msg}"); @@ -875,6 +976,9 @@ impl App { None ); + self.handle_device_options(menu, menu_command, icon_type, spaces) + .await?; + Ok(()) } diff --git a/src/icons.rs b/src/icons.rs index 79f10f8..a1ba9f2 100644 --- a/src/icons.rs +++ b/src/icons.rs @@ -56,8 +56,10 @@ impl Icons { font_icons.insert("disconnect", '\u{f0338}'); font_icons.insert("scan", '\u{f46a}'); font_icons.insert("settings", '\u{f0493}'); - font_icons.insert("disable_device", '\u{f092d}'); - font_icons.insert("enable_device", '\u{f0425}'); + font_icons.insert("disable_device", '\u{f05aa}'); + font_icons.insert("enable_device", '\u{f05a9}'); + font_icons.insert("power_off_adapter", '\u{f0902}'); + font_icons.insert("power_on_adapter", '\u{f0425}'); font_icons.insert("switch_mode", '\u{f0fe2}'); font_icons.insert("start_ap", '\u{f040d}'); font_icons.insert("stop_ap", '\u{f0667}'); @@ -139,9 +141,16 @@ impl Icons { "scan_in_progress", IconDefinition::simple("network-wireless-acquiring-symbolic"), ); - xdg_icons.insert("disable_device", + xdg_icons.insert( + "disable_device", IconDefinition::with_fallbacks( Some("network-wireless-disabled-symbolic"), + "network-wireless-disabled-symbolic,network-wireless-off", + ), + ); + xdg_icons.insert("power_off_adapter", + IconDefinition::with_fallbacks( + Some("network-wireless-hardware-disabled-symbolic"), "network-wireless-hardware-disabled-symbolic,network-wireless-disabled-symbolic,network-wireless-off" ) ); @@ -234,6 +243,10 @@ impl Icons { "enable_device", IconDefinition::simple("network-wireless-symbolic"), ); + xdg_icons.insert( + "power_on_adapter", + IconDefinition::simple("network-wireless-symbolic"), + ); Icons { font_icons, diff --git a/src/iw/adapter.rs b/src/iw/adapter.rs index 9144a53..36004b5 100644 --- a/src/iw/adapter.rs +++ b/src/iw/adapter.rs @@ -7,6 +7,7 @@ use std::sync::Arc; #[derive(Debug, Clone)] pub struct Adapter { pub adapter: IwdAdapter, + pub is_powered: bool, pub name: String, pub model: Option, pub vendor: Option, @@ -22,6 +23,11 @@ impl Adapter { .next() .ok_or_else(|| anyhow!("No adapter found"))?; + let is_powered = adapter + .is_powered() + .await + .context("Failed to get adapter power state")?; + let name = adapter.name().await?; let model = adapter @@ -48,6 +54,7 @@ impl Adapter { Ok(Self { adapter, + is_powered, name, model, vendor, @@ -56,7 +63,27 @@ impl Adapter { }) } + pub async fn power_off(&mut self) -> Result<()> { + self.adapter + .set_power(false) + .await + .context("Failed to power off the adapter")?; + self.is_powered = false; + Ok(()) + } + + pub async fn power_on(&mut self) -> Result<()> { + self.adapter + .set_power(true) + .await + .context("Failed to power on the adapter")?; + self.is_powered = true; + Ok(()) + } + pub async fn refresh(&mut self) -> Result<()> { + self.is_powered = self.adapter.is_powered().await?; + self.device .refresh() .await diff --git a/src/iw/device.rs b/src/iw/device.rs index 9e90b47..2db16ff 100644 --- a/src/iw/device.rs +++ b/src/iw/device.rs @@ -12,7 +12,7 @@ pub struct Device { pub name: String, pub address: String, pub mode: Mode, - pub is_powered: bool, + pub is_enabled: bool, pub station: Option, pub access_point: Option, } @@ -32,10 +32,10 @@ impl Device { .get_mode() .await .context("Failed to retrieve device mode")?; - let is_powered = device + let is_enabled = device .is_powered() .await - .context("Failed to check if the device is powered")?; + .context("Failed to check if the device is enabled")?; let station = Self::initialize_station(session.clone()).await; let access_point = Self::initialize_access_point(session.clone()).await; @@ -46,7 +46,7 @@ impl Device { name, address, mode, - is_powered, + is_enabled, station, access_point, }) @@ -87,26 +87,26 @@ impl Device { .context("Failed to set device mode") } - pub async fn power_off(&mut self) -> Result<()> { + pub async fn disable(&mut self) -> Result<()> { self.device .set_power(false) .await - .context("Failed to power off the device")?; - self.is_powered = false; + .context("Failed to disable the device")?; + self.is_enabled = false; Ok(()) } - pub async fn power_on(&mut self) -> Result<()> { + pub async fn enable(&mut self) -> Result<()> { self.device .set_power(true) .await - .context("Failed to power on the device")?; - self.is_powered = true; + .context("Failed to enable the device")?; + self.is_enabled = true; Ok(()) } pub async fn refresh(&mut self) -> Result<()> { - self.is_powered = self.device.is_powered().await?; + self.is_enabled = self.device.is_powered().await?; let current_mode = self .device diff --git a/src/menu.rs b/src/menu.rs index 00e3892..9206093 100644 --- a/src/menu.rs +++ b/src/menu.rs @@ -89,6 +89,7 @@ impl KnownNetworkOptions { #[derive(Debug, Clone, Copy)] pub enum SettingsMenuOptions { + PowerOffAdapter, DisableDevice, SwitchMode, Back, @@ -97,6 +98,7 @@ pub enum SettingsMenuOptions { impl SettingsMenuOptions { pub fn from_id(id: &str) -> Option { match id { + "power_off_adapter" => Some(SettingsMenuOptions::PowerOffAdapter), "disable_device" => Some(SettingsMenuOptions::DisableDevice), "switch_mode" => Some(SettingsMenuOptions::SwitchMode), "back" => Some(SettingsMenuOptions::Back), @@ -106,6 +108,7 @@ impl SettingsMenuOptions { pub fn to_id(&self) -> &'static str { match self { + SettingsMenuOptions::PowerOffAdapter => "power_off_adapter", SettingsMenuOptions::DisableDevice => "disable_device", SettingsMenuOptions::SwitchMode => "switch_mode", SettingsMenuOptions::Back => "back", @@ -114,6 +117,9 @@ impl SettingsMenuOptions { pub fn to_str(&self) -> Cow<'static, str> { match self { + SettingsMenuOptions::PowerOffAdapter => { + t!("menus.settings.options.power_off_adapter.name") + } SettingsMenuOptions::DisableDevice => { t!("menus.settings.options.disable_device.name") } @@ -182,13 +188,48 @@ impl ApMenuOptions { } #[derive(Debug, Clone, Copy)] +pub enum AdapterMenuOptions { + PowerOnAdapter, +} + +impl AdapterMenuOptions { + pub fn from_id(id: &str) -> Option { + match id { + "power_on_adapter" => Some(AdapterMenuOptions::PowerOnAdapter), + _ => None, + } + } + + pub fn to_id(&self) -> &'static str { + match self { + AdapterMenuOptions::PowerOnAdapter => "power_on_adapter", + } + } + + pub fn from_string(option: &str) -> Option { + if option == t!("menus.adapter.options.power_on_adapter.name") { + Some(AdapterMenuOptions::PowerOnAdapter) + } else { + None + } + } + + pub fn to_str(&self) -> Cow<'static, str> { + match self { + AdapterMenuOptions::PowerOnAdapter => t!("menus.adapter.options.power_on_adapter.name"), + } + } +} + pub enum DeviceMenuOptions { + PowerOffAdapter, EnableDevice, } impl DeviceMenuOptions { pub fn from_id(id: &str) -> Option { match id { + "power_off_adapter" => Some(DeviceMenuOptions::PowerOffAdapter), "enable_device" => Some(DeviceMenuOptions::EnableDevice), _ => None, } @@ -196,12 +237,15 @@ impl DeviceMenuOptions { pub fn to_id(&self) -> &'static str { match self { + DeviceMenuOptions::PowerOffAdapter => "power_off_adapter", DeviceMenuOptions::EnableDevice => "enable_device", } } pub fn from_string(option: &str) -> Option { - if option == t!("menus.device.options.enable_device.name") { + if option == t!("menus.device.options.power_off_adapter.name") { + Some(DeviceMenuOptions::PowerOffAdapter) + } else if option == t!("menus.device.options.enable_device.name") { Some(DeviceMenuOptions::EnableDevice) } else { None @@ -210,6 +254,7 @@ impl DeviceMenuOptions { pub fn to_str(&self) -> Cow<'static, str> { match self { + DeviceMenuOptions::PowerOffAdapter => t!("menus.device.options.power_off_adapter.name"), DeviceMenuOptions::EnableDevice => t!("menus.device.options.enable_device.name"), } } @@ -497,6 +542,15 @@ impl Menu { }; let mut options = vec![ + ( + SettingsMenuOptions::PowerOffAdapter.to_id(), + self.icons.format_display_with_icon( + &SettingsMenuOptions::PowerOffAdapter.to_str(), + &self.icons.get_icon("power_off_adapter", icon_type), + icon_type, + spaces, + ), + ), ( SettingsMenuOptions::DisableDevice.to_id(), self.icons.format_display_with_icon( @@ -540,7 +594,9 @@ impl Menu { if let Some(output) = menu_output { let cleaned_output = self.clean_menu_output(&output, icon_type); - if cleaned_output == SettingsMenuOptions::DisableDevice.to_str() { + if cleaned_output == SettingsMenuOptions::PowerOffAdapter.to_str() { + return Ok(Some(SettingsMenuOptions::PowerOffAdapter)); + } else if cleaned_output == SettingsMenuOptions::DisableDevice.to_str() { return Ok(Some(SettingsMenuOptions::DisableDevice)); } else if cleaned_output == switch_mode_text { return Ok(Some(SettingsMenuOptions::SwitchMode)); @@ -565,9 +621,55 @@ impl Menu { icon_type: &str, spaces: usize, ) -> Option { + let options = vec![ + ( + DeviceMenuOptions::PowerOffAdapter.to_id(), + self.icons.format_display_with_icon( + &DeviceMenuOptions::PowerOffAdapter.to_str(), + &self.icons.get_icon("power_off_adapter", icon_type), + icon_type, + spaces, + ), + ), + ( + DeviceMenuOptions::EnableDevice.to_id(), + self.icons.format_display_with_icon( + &DeviceMenuOptions::EnableDevice.to_str(), + &self.icons.get_icon("enable_device", icon_type), + icon_type, + spaces, + ), + ), + ]; + + let input = options + .into_iter() + .map(|(_, formatted_text)| formatted_text) + .collect::>() + .join("\n"); + + if let Ok(Some(output)) = + self.run_launcher(menu_command, Some(&input), icon_type, None, false) + { + let cleaned_output = self.clean_menu_output(&output, icon_type); + + if let Some(option) = DeviceMenuOptions::from_string(&cleaned_output) { + return Some(option); + } + } + + None + } + + pub fn prompt_power_on_adapter( + &self, + menu_command: &Option, + icon_type: &str, + spaces: usize, + ) -> Option { let options = vec![( - DeviceMenuOptions::EnableDevice.to_id(), - DeviceMenuOptions::EnableDevice.to_str(), + AdapterMenuOptions::PowerOnAdapter.to_id(), + AdapterMenuOptions::PowerOnAdapter.to_str(), )]; let input = self.icons.get_icon_text(options, icon_type, spaces); @@ -577,7 +679,7 @@ impl Menu { { let cleaned_output = self.clean_menu_output(&output, icon_type); - if let Some(option) = DeviceMenuOptions::from_string(&cleaned_output) { + if let Some(option) = AdapterMenuOptions::from_string(&cleaned_output) { return Some(option); } }