diff --git a/src/devices/mod.rs b/src/devices/mod.rs index f14f919..bb76c75 100644 --- a/src/devices/mod.rs +++ b/src/devices/mod.rs @@ -25,6 +25,14 @@ use thistermination::TerminationFull; const PASSIVE_REFRESH_TIME_OUT: Duration = Duration::from_secs(2); +pub fn format_int_value(value: u8, suffix: &str) -> String { + if value == 0 && suffix == "min" { + "never".to_string() + } else { + format!("{}{}", value, suffix) + } +} + type DeviceFactory = fn(DeviceState) -> Box; struct DeviceEntry { @@ -589,8 +597,8 @@ impl DeviceProperties { let (prefix, data, suffix) = match prop { PropertyDescriptorWrapper::Int(property_descriptor, _) => ( property_descriptor.prefix, - &property_descriptor.data.map(|v| v.to_string()), - property_descriptor.suffix, + &property_descriptor.data.map(|v| format_int_value(v, property_descriptor.suffix)), + "", ), PropertyDescriptorWrapper::Bool(property_descriptor) => ( property_descriptor.prefix, @@ -617,8 +625,8 @@ impl DeviceProperties { let (prefix, data, suffix, property_type) = match prop { PropertyDescriptorWrapper::Int(property_descriptor, _) => ( property_descriptor.prefix, - &property_descriptor.data.map(|v| v.to_string()), - property_descriptor.suffix, + &property_descriptor.data.map(|v| format_int_value(v, property_descriptor.suffix)), + "", property_descriptor.property_type, ), PropertyDescriptorWrapper::Bool(property_descriptor) => ( diff --git a/src/main.rs b/src/main.rs index 858313e..b494adf 100644 --- a/src/main.rs +++ b/src/main.rs @@ -190,6 +190,12 @@ fn main() { .required(false) .help("Use verbose output ") ) + .arg(Arg::new("monochrome_icons") + .long("monochrome-icons") + .action(ArgAction::SetTrue) + .required(false) + .help("Use the symbolic (monochrome) variants of the system tray icons") + ) .get_matches(); let press_mute_key = *matches.get_one::("press_mute_key").unwrap_or(&true); @@ -205,11 +211,12 @@ fn main() { None }; VERBOSE.set(matches.get_flag("verbose")).unwrap(); + let monochrome_icons = matches.get_flag("monochrome_icons"); let refresh_interval = *matches.get_one::("refresh_interval").unwrap_or(&3); let refresh_interval = Duration::from_secs(refresh_interval); let (tx, rx) = mpsc::channel(); - let tray_handler = TrayHandler::new(StatusTray::new(tx)); + let tray_handler = TrayHandler::new(StatusTray::new(tx, monochrome_icons)); loop { let mut device = loop { match connect_compatible_device() { diff --git a/src/status_tray.rs b/src/status_tray.rs index e830302..779a930 100644 --- a/src/status_tray.rs +++ b/src/status_tray.rs @@ -1,6 +1,8 @@ use std::sync::mpsc::Sender; -use hyper_headset::devices::{DeviceEvent, DeviceProperties, DeviceState, PropertyType}; +use hyper_headset::devices::{ + format_int_value, DeviceEvent, DeviceProperties, DeviceState, PropertyType, +}; use ksni::{ menu::{StandardItem, SubMenu}, Handle, MenuItem, ToolTip, Tray, TrayService, @@ -39,13 +41,31 @@ impl TrayHandler { pub struct StatusTray { device_properties: Option, update_sender: Sender, + monochrome_icons: bool, } impl StatusTray { - pub fn new(update_sender: Sender) -> Self { + pub fn new(update_sender: Sender, monochrome_icons: bool) -> Self { StatusTray { device_properties: None, update_sender, + monochrome_icons, + } + } + + fn fallback_headset_icon(&self) -> &'static str { + if self.monochrome_icons { + "audio-headset-symbolic" + } else { + "audio-headset" + } + } + + fn exit_icon(&self) -> &'static str { + if self.monochrome_icons { + "application-exit-symbolic" + } else { + "application-exit" } } } @@ -57,7 +77,7 @@ impl Tray for StatusTray { fn icon_name(&self) -> String { TrayBatteryIconState::from_device_properties(self.device_properties.as_ref()) - .linux_icon_name() + .linux_icon_name(self.monochrome_icons) .to_string() } @@ -66,7 +86,7 @@ impl Tray for StatusTray { return ToolTip { title: "Unknown".to_string(), description: NO_COMPATIBLE_DEVICE.to_string(), - icon_name: "audio-headset".into(), + icon_name: self.fallback_headset_icon().into(), icon_pixmap: Vec::new(), }; }; @@ -88,16 +108,17 @@ impl Tray for StatusTray { .unwrap_or("Unknown".to_string()), description, icon_name: TrayBatteryIconState::from_device_properties(Some(device_properties)) - .linux_icon_name() + .linux_icon_name(self.monochrome_icons) .to_string(), icon_pixmap: Vec::new(), } } fn menu(&self) -> Vec> { + let exit_icon = self.exit_icon(); let make_exit = || StandardItem { label: "Quit".into(), - icon_name: "application-exit".into(), + icon_name: exit_icon.into(), activate: Box::new(|_| std::process::exit(0)), ..Default::default() }; @@ -162,7 +183,7 @@ impl Tray for StatusTray { .map(|val| { let update_sender = self.update_sender.clone(); StandardItem { - label: format!("{}{}", val, property.suffix), + label: format_int_value(*val, property.suffix), enabled: property.property_type == PropertyType::ReadWrite && property.data.is_some(), activate: Box::new(move |_| { @@ -178,8 +199,9 @@ impl Tray for StatusTray { menu_items.push( SubMenu { label: format!( - "{} {}{}", - property.prefix, current_value, property.suffix + "{} {}", + property.prefix, + format_int_value(current_value, property.suffix) ), enabled: property.property_type == PropertyType::ReadWrite && property.data.is_some(), diff --git a/src/status_tray_not_linux.rs b/src/status_tray_not_linux.rs index b5f1e8a..384a2b2 100644 --- a/src/status_tray_not_linux.rs +++ b/src/status_tray_not_linux.rs @@ -5,7 +5,7 @@ use std::{ #[cfg(target_os = "windows")] use image::{Rgba, RgbaImage}; -use hyper_headset::devices::{DeviceEvent, DeviceProperties, PropertyType}; +use hyper_headset::devices::{format_int_value, DeviceEvent, DeviceProperties, PropertyType}; use tray_icon::{ menu::{CheckMenuItem, Menu, MenuEvent, MenuId, MenuItem, PredefinedMenuItem, Submenu}, TrayIcon, TrayIconBuilder, @@ -404,7 +404,7 @@ impl TrayApp { continue; }; let menu_item = MenuItem::new( - format!("{} {}{}", property.prefix, current_value, property.suffix), + format!("{} {}", property.prefix, format_int_value(current_value, property.suffix)), false, None, ); @@ -415,13 +415,13 @@ impl TrayApp { continue; }; let submenu = Submenu::new( - format!("{} {}{}", property.prefix, current_value, property.suffix), + format!("{} {}", property.prefix, format_int_value(current_value, property.suffix)), property.property_type == PropertyType::ReadWrite, ); for item_value in items { - let entry = - MenuItem::new(format!("{}{}", item_value, property.suffix), true, None); + let entry = + MenuItem::new(format_int_value(*item_value, property.suffix), true, None, ); submenu.append(&entry).unwrap(); let create_event = property.create_event; diff --git a/src/tray_battery_icon_state.rs b/src/tray_battery_icon_state.rs index 01157cf..6068dc7 100644 --- a/src/tray_battery_icon_state.rs +++ b/src/tray_battery_icon_state.rs @@ -48,9 +48,15 @@ impl TrayBatteryIconState { } #[cfg(target_os = "linux")] - pub fn linux_icon_name(self) -> &'static str { + pub fn linux_icon_name(self, monochrome: bool) -> &'static str { match self { - Self::NoDevice | Self::Disconnected | Self::ConnectedUnknown => "audio-headset", + Self::NoDevice | Self::Disconnected | Self::ConnectedUnknown => { + if monochrome { + "audio-headset-symbolic" + } else { + "audio-headset" + } + } Self::Connected { percent, charging } => { let level_name = if percent <= 10 { "battery-caution" @@ -64,15 +70,27 @@ impl TrayBatteryIconState { "battery-full" }; if charging { - match level_name { - "battery-caution" => "battery-caution-charging", - "battery-low" => "battery-low-charging", - "battery-medium" => "battery-medium-charging", - "battery-good" => "battery-good-charging", - _ => "battery-full-charging", + match (level_name, monochrome) { + ("battery-caution", false) => "battery-caution-charging", + ("battery-low", false) => "battery-low-charging", + ("battery-medium", false) => "battery-medium-charging", + ("battery-good", false) => "battery-good-charging", + (_, false) => "battery-full-charging", + ("battery-caution", true) => "battery-caution-charging-symbolic", + ("battery-low", true) => "battery-low-charging-symbolic", + ("battery-medium", true) => "battery-medium-charging-symbolic", + ("battery-good", true) => "battery-good-charging-symbolic", + (_, true) => "battery-full-charging-symbolic", } } else { - level_name + match (level_name, monochrome) { + ("battery-caution", true) => "battery-caution-symbolic", + ("battery-low", true) => "battery-low-symbolic", + ("battery-medium", true) => "battery-medium-symbolic", + ("battery-good", true) => "battery-good-symbolic", + (name, false) => name, + (_, true) => "battery-full-symbolic", + } } } }