From 559b0e843c891ee38639957ab210e457eb42f1e4 Mon Sep 17 00:00:00 2001 From: xzeldon Date: Fri, 6 Sep 2024 01:55:10 +0300 Subject: [PATCH] feat: add console window toggling --- Cargo.lock | 1 + Cargo.toml | 3 +++ README.md | 20 ++++++++++++-------- src/main.rs | 21 +++++++++++++++++++++ src/manager.rs | 3 --- src/tray.rs | 51 ++++++++++++++++++++++++++++++++++++++++++-------- 6 files changed, 80 insertions(+), 19 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fe6b651..8eafdcb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1821,6 +1821,7 @@ dependencies = [ "pretty_env_logger", "tao", "tray-icon", + "winapi", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index f59627c..0537ce0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,3 +25,6 @@ tray-icon = "0.17.0" # Image manipulation image = "0.25.2" + +# Windows API +winapi = { version = "0.3.9", features = ["winuser", "wincon", "consoleapi"] } diff --git a/README.md b/README.md index e3ea2ab..d1a8c8d 100644 --- a/README.md +++ b/README.md @@ -12,12 +12,13 @@ Show your wireless Razer devices battery levels in your system tray. > This is a work in progress and currently support only **Razer DeathAdder V3 Pro**. -> This works pretty well on **Windows**, should work on **Linux** if you *add udev rule to get access to usb devices* (see [here](https://github.com/libusb/hidapi/blob/master/udev/69-hid.rules)). But I haven't tested yet. +> This works pretty well on **Windows**, should work on **Linux** if you _add udev rule to get access to usb devices_ (see [here](https://github.com/libusb/hidapi/blob/master/udev/69-hid.rules)). But I haven't tested yet. ## Usage ### Downloading a Prebuilt Binary -> *Todo* + +> _Todo_ ### Building from Source @@ -30,21 +31,24 @@ To build, you must have [Rust](https://www.rust-lang.org/) and 4. Executable will be located at `target/release/razer-battery-report.exe` ## Adding new devices yourself -* add device with `name`, `pid`, `interface`, `usage_page`, `usage` to [devices.rs](/src/devices.rs) -* add `transaction_id` to switch statement in `DeviceInfo` in [devices.rs](/src/devices.rs) + +- add device with `name`, `pid`, `interface`, `usage_page`, `usage` to [devices.rs](/src/devices.rs) +- add `transaction_id` to switch statement in `DeviceInfo` in [devices.rs](/src/devices.rs) > You can grab `pid` and other data from the [openrazer](https://github.com/openrazer/openrazer/blob/352d13c416f42e572016c02fd10a52fc9848644a/driver/razermouse_driver.h#L9) ## Todo + - [x] Tray Applet - [ ] Force update devices button in tray menu - [ ] Colored tray icons for different battery levels - - [ ] Show log window button in tray menu + - [x] Show log window button in tray menu - [ ] Prebuilt Binary - [ ] Command Line Arguments for update frequency - [ ] Support for other Razer Devices (I only have DeathAdder V3 Pro, so I won't be able to test it with other devices) ## Acknowledgments -* Linux Drivers for Razer devices: https://github.com/openrazer/openrazer -* This python script: https://github.com/spozer/razer-battery-checker -* 🖱️ Logitech Battery Level Tray Indicator (Elem): https://github.com/Fuwn/elem \ No newline at end of file + +- Linux Drivers for Razer devices: https://github.com/openrazer/openrazer +- This python script: https://github.com/spozer/razer-battery-checker +- 🖱️ Logitech Battery Level Tray Indicator (Elem): https://github.com/Fuwn/elem diff --git a/src/main.rs b/src/main.rs index afd9959..49dac28 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,7 @@ #![windows_subsystem = "windows"] use tray::TrayApp; +use winapi::um::{self, wincon, winuser}; mod controller; mod devices; @@ -8,6 +9,26 @@ mod manager; mod tray; fn main() { + unsafe { + // Allocate new console for the process + um::consoleapi::AllocConsole(); + + // Modify the console window's style to remove the system menu (close, minimize, etc.). + winuser::SetWindowLongPtrW( + wincon::GetConsoleWindow(), + winuser::GWL_STYLE, + #[allow(clippy::cast_possible_wrap)] + { + winuser::GetWindowLongPtrW(wincon::GetConsoleWindow(), winuser::GWL_STYLE) + & !winuser::WS_SYSMENU as isize + }, + ); + + // Hide the console window + winuser::ShowWindow(wincon::GetConsoleWindow(), winuser::SW_HIDE); + } + + std::env::set_var("RUST_LOG", "trace"); pretty_env_logger::init(); let checker = TrayApp::new(); diff --git a/src/manager.rs b/src/manager.rs index c64fa74..aa18ea4 100644 --- a/src/manager.rs +++ b/src/manager.rs @@ -85,7 +85,6 @@ impl DeviceManager { let mut added_devices = HashSet::new(); for device in RAZER_DEVICE_LIST.iter() { - // Create a new HidApi instance let api = match HidApi::new() { Ok(api) => api, Err(err) => { @@ -94,7 +93,6 @@ impl DeviceManager { } }; - // Iterate over the device list to find matching devices for hid_device in api.device_list() { if hid_device.vendor_id() == device.vid && hid_device.product_id() == device.pid @@ -110,7 +108,6 @@ impl DeviceManager { // Only add the device if it hasn't been added yet if !added_devices.contains(&device.pid) { - // Create a new DeviceController match DeviceController::new( device.name.to_owned(), device.pid, diff --git a/src/tray.rs b/src/tray.rs index ecdc075..dfc2aa6 100644 --- a/src/tray.rs +++ b/src/tray.rs @@ -6,12 +6,13 @@ use std::{ }; use crate::manager::DeviceManager; -use log::{error, info}; +use log::{error, info, trace}; 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: std::time::Duration = std::time::Duration::from_secs(60); const DEVICE_FETCH_INTERVAL: std::time::Duration = std::time::Duration::from_secs(5); @@ -42,6 +43,8 @@ pub struct TrayApp { device_manager: Arc>, devices: Arc>>, tray_icon: Rc>>, + console_state: Arc>, + menu_items: Arc>> } impl TrayApp { @@ -50,13 +53,15 @@ impl TrayApp { device_manager: Arc::new(Mutex::new(DeviceManager::new())), devices: Arc::new(Mutex::new(HashMap::new())), tray_icon: Rc::new(Mutex::new(None)), + console_state: Arc::new(Mutex::new(false)), + menu_items: Arc::new(Mutex::new(Vec::new())) } } pub fn run(&self) { let icon = Self::create_icon(); let event_loop = EventLoopBuilder::new().build(); - let tray_menu = Self::create_menu(); + let tray_menu = self.create_menu(); let (sender, receiver) = mpsc::channel(); @@ -77,10 +82,17 @@ impl TrayApp { tray_icon::Icon::from_rgba(rgba, width, height).expect("Failed to create icon") } - fn create_menu() -> Menu { + fn create_menu(&self) -> Menu { let tray_menu = Menu::new(); + + let show_console_item = MenuItem::new("Show Log Window", true, None); let quit_item = MenuItem::new("Exit", true, None); - tray_menu.append_items(&[&quit_item]).unwrap(); + + let mut menu_items = self.menu_items.lock().unwrap(); + menu_items.push(show_console_item); + menu_items.push(quit_item); + + tray_menu.append_items(&[&menu_items[0], &menu_items[1]]).unwrap(); tray_menu } @@ -113,7 +125,7 @@ impl TrayApp { } } } - + if !connected_devices.is_empty() { tx.send(connected_devices).unwrap(); } @@ -142,6 +154,8 @@ impl TrayApp { let tray_icon = Rc::clone(&self.tray_icon); let devices = Arc::clone(&self.devices); let device_manager = Arc::clone(&self.device_manager); + let console_state = Arc::clone(&self.console_state); + let menu_items = Arc::clone(&self.menu_items); let menu_channel = MenuEvent::receiver(); @@ -155,18 +169,39 @@ impl TrayApp { } if let tao::event::Event::NewEvents(tao::event::StartCause::Init) = event { - Self::create_tray_icon(&tray_icon, &tray_menu, icon.clone()); + Self::build_tray(&tray_icon, &tray_menu, icon.clone()); } if let Ok(event) = menu_channel.try_recv() { - if event.id == tray_menu.items()[0].id() { + let menu_items = menu_items.lock().unwrap(); + + 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; + } else { + unsafe {ShowWindow(GetConsoleWindow(), SW_SHOW)}; + show_console_item.set_text("Hide Log Window"); + trace!("showing log window"); + *visible = true + } + } + + if event.id == quit_item.id() { *control_flow = tao::event_loop::ControlFlow::Exit; } } }); } - fn create_tray_icon( + fn build_tray( tray_icon: &Rc>>, tray_menu: &Menu, icon: tray_icon::Icon,