From 85779205045f55f1bbd33edf82dd96724b4445e4 Mon Sep 17 00:00:00 2001 From: xzeldon Date: Tue, 10 Sep 2024 00:16:21 +0300 Subject: [PATCH] refactor: abstract debug console logic and migrate to parking_lot - Added dependency parking_lot to Cargo.toml configuration. - Added a DebugConsole struct to manage and toggle the visibility of a debug console window. --- Cargo.lock | 1 + Cargo.toml | 3 +++ src/console.rs | 58 ++++++++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 37 +++++-------------------------- src/manager.rs | 13 ++++++----- src/tray.rs | 60 ++++++++++++++++++++++---------------------------- 6 files changed, 100 insertions(+), 72 deletions(-) create mode 100644 src/console.rs diff --git a/Cargo.lock b/Cargo.lock index 4601c0f..7c19c31 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1818,6 +1818,7 @@ dependencies = [ "hidapi", "image", "log", + "parking_lot", "pretty_env_logger", "tao", "tray-icon", diff --git a/Cargo.toml b/Cargo.toml index 5e7ede7..aad4cf0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,3 +28,6 @@ image = "0.25.2" # Windows API winapi = { version = "0.3.9", features = ["winuser", "wincon", "consoleapi"] } + +# Efficient synchronization primitives (e.g. Mutex, RwLock and etc.) +parking_lot = "0.12" diff --git a/src/console.rs b/src/console.rs new file mode 100644 index 0000000..6ee13ff --- /dev/null +++ b/src/console.rs @@ -0,0 +1,58 @@ +use parking_lot::Mutex; +use std::{ffi::OsStr, os::windows::ffi::OsStrExt, sync::Arc}; +use winapi::um::{consoleapi, wincon, winuser}; + +pub struct DebugConsole { + hwnd: *mut winapi::shared::windef::HWND__, + visible: Arc>, +} + +impl DebugConsole { + pub fn new(title: &str) -> Self { + unsafe { + consoleapi::AllocConsole(); + + let title: Vec = OsStr::new(title) + .encode_wide() + .chain(std::iter::once(0)) + .collect(); + wincon::SetConsoleTitleW(title.as_ptr()); + + let hwnd = wincon::GetConsoleWindow(); + + if !hwnd.is_null() { + let hmenu = winuser::GetSystemMenu(hwnd, 0); + if !hmenu.is_null() { + winuser::DeleteMenu(hmenu, winuser::SC_CLOSE as u32, winuser::MF_BYCOMMAND); + } + winuser::ShowWindow(hwnd, winuser::SW_HIDE); + } + + Self { + hwnd, + visible: Arc::new(Mutex::new(false)), + } + } + } + + pub fn toggle_visibility(&self) { + if !self.hwnd.is_null() { + let mut visible = self.visible.lock(); + *visible = !*visible; + unsafe { + winuser::ShowWindow( + self.hwnd, + if *visible { + winuser::SW_SHOW + } else { + winuser::SW_HIDE + }, + ); + } + } + } + + pub fn is_visible(&self) -> bool { + *self.visible.lock() + } +} diff --git a/src/main.rs b/src/main.rs index 74fdccc..805414a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,47 +1,20 @@ #![windows_subsystem = "windows"] -use std::{ffi::OsStr, os::windows::ffi::OsStrExt}; - +use console::DebugConsole; use tray::TrayApp; +mod console; mod controller; mod devices; mod manager; mod tray; fn main() { - unsafe { - // Allocate new console for the process - winapi::um::consoleapi::AllocConsole(); - - let title: Vec = OsStr::new("Razer Battery Report Debug Console") - .encode_wide() - .chain(std::iter::once(0)) - .collect(); - winapi::um::wincon::SetConsoleTitleW(title.as_ptr()); - - let hwnd = winapi::um::wincon::GetConsoleWindow(); - - // Disable close command in the sys.menu of the new console, otherwise the whole process will quit: https://stackoverflow.com/a/12015131/126995 - if !hwnd.is_null() { - let hmenu = winapi::um::winuser::GetSystemMenu(hwnd, 0); - if !hmenu.is_null() { - winapi::um::winuser::DeleteMenu( - hmenu, - winapi::um::winuser::SC_CLOSE as u32, - winapi::um::winuser::MF_BYCOMMAND, - ); - } - } - - // Hide the console window - if !hwnd.is_null() { - winapi::um::winuser::ShowWindow(hwnd, winapi::um::winuser::SW_HIDE); - } - } + let console = DebugConsole::new("Razer Battery Report Debug Console"); std::env::set_var("RUST_LOG", "trace"); pretty_env_logger::init(); - let checker = TrayApp::new(); + + let checker = TrayApp::new(console); checker.run(); } diff --git a/src/manager.rs b/src/manager.rs index aa18ea4..7f3b9eb 100644 --- a/src/manager.rs +++ b/src/manager.rs @@ -1,7 +1,8 @@ use hidapi::HidApi; use log::warn; +use parking_lot::Mutex; use std::collections::HashSet; -use std::sync::{Arc, Mutex}; +use std::sync::Arc; use std::vec::Vec; use crate::controller::DeviceController; @@ -21,7 +22,7 @@ impl DeviceManager { pub fn fetch_devices(&mut self) -> (Vec, Vec) { let old_ids: HashSet = { - let controllers = self.device_controllers.lock().unwrap(); + let controllers = self.device_controllers.lock(); controllers .iter() .map(|controller| controller.pid as u32) @@ -37,13 +38,13 @@ impl DeviceManager { let removed_devices: Vec = old_ids.difference(&new_ids).cloned().collect(); let connected_devices: Vec = new_ids.difference(&old_ids).cloned().collect(); - *self.device_controllers.lock().unwrap() = new_controllers; + *self.device_controllers.lock() = new_controllers; (removed_devices, connected_devices) } pub fn get_device_name(&self, id: u32) -> Option { - let controllers = self.device_controllers.lock().unwrap(); + let controllers = self.device_controllers.lock(); controllers .iter() .find(|controller| controller.pid as u32 == id) @@ -51,7 +52,7 @@ impl DeviceManager { } pub fn get_device_battery_level(&self, id: u32) -> Option { - let controllers = self.device_controllers.lock().unwrap(); + let controllers = self.device_controllers.lock(); let controller = controllers .iter() .find(|controller| controller.pid as u32 == id)?; @@ -66,7 +67,7 @@ impl DeviceManager { } pub fn is_device_charging(&self, id: u32) -> Option { - let controllers = self.device_controllers.lock().unwrap(); + let controllers = self.device_controllers.lock(); let controller = controllers .iter() .find(|controller| controller.pid as u32 == id)?; diff --git a/src/tray.rs b/src/tray.rs index 27175ce..712f4a4 100644 --- a/src/tray.rs +++ b/src/tray.rs @@ -1,22 +1,19 @@ use std::{ collections::HashMap, rc::Rc, - sync::{mpsc, Arc, Mutex}, + sync::{mpsc, Arc}, thread, time::{Duration, Instant}, }; -use crate::manager::DeviceManager; +use crate::{console::DebugConsole, manager::DeviceManager}; use log::{error, info, trace}; +use parking_lot::Mutex; use tao::event_loop::EventLoopBuilder; use tray_icon::{ menu::{Menu, MenuEvent, MenuItem}, TrayIcon, TrayIconBuilder, }; -use winapi::um::{ - wincon::GetConsoleWindow, - winuser::{self, ShowWindow, SW_SHOW}, -}; const BATTERY_UPDATE_INTERVAL: Duration = Duration::from_secs(60); const DEVICE_FETCH_INTERVAL: Duration = Duration::from_secs(5); @@ -45,16 +42,16 @@ impl MemoryDevice { pub struct TrayInner { tray_icon: Rc>>, - console_state: Arc>, menu_items: Arc>>, + debug_console: Arc, } impl TrayInner { - fn new() -> Self { + fn new(debug_console: Arc) -> Self { Self { tray_icon: Rc::new(Mutex::new(None)), - console_state: Arc::new(Mutex::new(false)), menu_items: Arc::new(Mutex::new(Vec::new())), + debug_console, } } @@ -64,7 +61,7 @@ impl TrayInner { let show_console_item = MenuItem::new("Show Log Window", true, None); let quit_item = MenuItem::new("Exit", true, None); - let mut menu_items = self.menu_items.lock().unwrap(); + let mut menu_items = self.menu_items.lock(); menu_items.push(show_console_item); menu_items.push(quit_item); @@ -86,7 +83,7 @@ impl TrayInner { .build(); match tray_builder { - Ok(tray) => *tray_icon.lock().unwrap() = Some(tray), + Ok(tray) => *tray_icon.lock() = Some(tray), Err(err) => error!("Failed to create tray icon: {}", err), } } @@ -99,11 +96,11 @@ pub struct TrayApp { } impl TrayApp { - pub fn new() -> Self { + pub fn new(debug_console: DebugConsole) -> Self { Self { device_manager: Arc::new(Mutex::new(DeviceManager::new())), devices: Arc::new(Mutex::new(HashMap::new())), - tray_inner: TrayInner::new(), + tray_inner: TrayInner::new(Arc::new(debug_console)), } } @@ -137,12 +134,12 @@ impl TrayApp { thread::spawn(move || loop { let (removed_devices, connected_devices) = { - let mut manager = device_manager.lock().unwrap(); + let mut manager = device_manager.lock(); manager.fetch_devices() }; { - let mut devices = devices.lock().unwrap(); + let mut devices = devices.lock(); for id in removed_devices { if let Some(device) = devices.remove(&id) { info!("Device removed: {}", device.name); @@ -151,7 +148,7 @@ impl TrayApp { for &id in &connected_devices { if let std::collections::hash_map::Entry::Vacant(e) = devices.entry(id) { - if let Some(name) = device_manager.lock().unwrap().get_device_name(id) { + if let Some(name) = device_manager.lock().get_device_name(id) { e.insert(MemoryDevice::new(name.clone(), id)); info!("New device: {}", name); } else { @@ -173,7 +170,7 @@ impl TrayApp { let devices = Arc::clone(&self.devices); thread::spawn(move || loop { - let device_ids: Vec = devices.lock().unwrap().keys().cloned().collect(); + let device_ids: Vec = devices.lock().keys().cloned().collect(); tx.send(device_ids).unwrap(); thread::sleep(BATTERY_UPDATE_INTERVAL); }); @@ -189,7 +186,7 @@ impl TrayApp { let devices = Arc::clone(&self.devices); let device_manager = Arc::clone(&self.device_manager); let tray_icon = Rc::clone(&self.tray_inner.tray_icon); - let console_state = Arc::clone(&self.tray_inner.console_state); + let debug_console = Arc::clone(&self.tray_inner.debug_console); let menu_items = Arc::clone(&self.tray_inner.menu_items); let menu_channel = MenuEvent::receiver(); @@ -210,25 +207,20 @@ impl TrayApp { } if let Ok(event) = menu_channel.try_recv() { - let menu_items = menu_items.lock().unwrap(); + let menu_items = menu_items.lock(); let show_console_item = &menu_items[0]; let quit_item = &menu_items[1]; if event.id == show_console_item.id() { - let mut visible = console_state.lock().unwrap(); - - if *visible { - unsafe { ShowWindow(GetConsoleWindow(), winuser::SW_HIDE) }; - show_console_item.set_text("Show Log Window"); - trace!("hiding log window"); - *visible = false; + debug_console.toggle_visibility(); + let visible = debug_console.is_visible(); + show_console_item.set_text(if visible { + "Hide Log Window" } else { - unsafe { ShowWindow(GetConsoleWindow(), SW_SHOW) }; - show_console_item.set_text("Hide Log Window"); - trace!("showing log window"); - *visible = true - } + "Show Log Window" + }); + trace!("{} log window", if visible { "showing" } else { "hiding" }); } if event.id == quit_item.id() { @@ -244,8 +236,8 @@ impl TrayApp { device_ids: &[u32], tray_icon: &Rc>>, ) { - let mut devices = devices.lock().unwrap(); - let manager = manager.lock().unwrap(); + let mut devices = devices.lock(); + let manager = manager.lock(); for &id in device_ids { if let Some(device) = devices.get_mut(&id) { @@ -262,7 +254,7 @@ impl TrayApp { Self::check_notify(device); - if let Some(tray_icon) = tray_icon.lock().unwrap().as_mut() { + if let Some(tray_icon) = tray_icon.lock().as_mut() { let _ = tray_icon .set_tooltip(Some(format!("{}: {}%", device.name, battery_level))); }