feat: add console window toggling

This commit is contained in:
Timofey Gelazoniya 2024-09-06 01:55:10 +03:00
parent 58d139d156
commit 559b0e843c
Signed by: zeldon
GPG Key ID: 047886915281DD2A
6 changed files with 80 additions and 19 deletions

1
Cargo.lock generated
View File

@ -1821,6 +1821,7 @@ dependencies = [
"pretty_env_logger", "pretty_env_logger",
"tao", "tao",
"tray-icon", "tray-icon",
"winapi",
] ]
[[package]] [[package]]

View File

@ -25,3 +25,6 @@ tray-icon = "0.17.0"
# Image manipulation # Image manipulation
image = "0.25.2" image = "0.25.2"
# Windows API
winapi = { version = "0.3.9", features = ["winuser", "wincon", "consoleapi"] }

View File

@ -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 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 ## Usage
### Downloading a Prebuilt Binary ### Downloading a Prebuilt Binary
> *Todo*
> _Todo_
### Building from Source ### 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` 4. Executable will be located at `target/release/razer-battery-report.exe`
## Adding new devices yourself ## 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) > You can grab `pid` and other data from the [openrazer](https://github.com/openrazer/openrazer/blob/352d13c416f42e572016c02fd10a52fc9848644a/driver/razermouse_driver.h#L9)
## Todo ## Todo
- [x] Tray Applet - [x] Tray Applet
- [ ] Force update devices button in tray menu - [ ] Force update devices button in tray menu
- [ ] Colored tray icons for different battery levels - [ ] Colored tray icons for different battery levels
- [ ] Show log window button in tray menu - [x] Show log window button in tray menu
- [ ] Prebuilt Binary - [ ] Prebuilt Binary
- [ ] Command Line Arguments for update frequency - [ ] 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) - [ ] Support for other Razer Devices (I only have DeathAdder V3 Pro, so I won't be able to test it with other devices)
## Acknowledgments ## Acknowledgments
* Linux Drivers for Razer devices: https://github.com/openrazer/openrazer
* This python script: https://github.com/spozer/razer-battery-checker - Linux Drivers for Razer devices: https://github.com/openrazer/openrazer
* 🖱️ Logitech Battery Level Tray Indicator (Elem): https://github.com/Fuwn/elem - This python script: https://github.com/spozer/razer-battery-checker
- 🖱️ Logitech Battery Level Tray Indicator (Elem): https://github.com/Fuwn/elem

View File

@ -1,6 +1,7 @@
#![windows_subsystem = "windows"] #![windows_subsystem = "windows"]
use tray::TrayApp; use tray::TrayApp;
use winapi::um::{self, wincon, winuser};
mod controller; mod controller;
mod devices; mod devices;
@ -8,6 +9,26 @@ mod manager;
mod tray; mod tray;
fn main() { 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"); std::env::set_var("RUST_LOG", "trace");
pretty_env_logger::init(); pretty_env_logger::init();
let checker = TrayApp::new(); let checker = TrayApp::new();

View File

@ -85,7 +85,6 @@ impl DeviceManager {
let mut added_devices = HashSet::new(); let mut added_devices = HashSet::new();
for device in RAZER_DEVICE_LIST.iter() { for device in RAZER_DEVICE_LIST.iter() {
// Create a new HidApi instance
let api = match HidApi::new() { let api = match HidApi::new() {
Ok(api) => api, Ok(api) => api,
Err(err) => { Err(err) => {
@ -94,7 +93,6 @@ impl DeviceManager {
} }
}; };
// Iterate over the device list to find matching devices
for hid_device in api.device_list() { for hid_device in api.device_list() {
if hid_device.vendor_id() == device.vid if hid_device.vendor_id() == device.vid
&& hid_device.product_id() == device.pid && hid_device.product_id() == device.pid
@ -110,7 +108,6 @@ impl DeviceManager {
// Only add the device if it hasn't been added yet // Only add the device if it hasn't been added yet
if !added_devices.contains(&device.pid) { if !added_devices.contains(&device.pid) {
// Create a new DeviceController
match DeviceController::new( match DeviceController::new(
device.name.to_owned(), device.name.to_owned(),
device.pid, device.pid,

View File

@ -6,12 +6,13 @@ use std::{
}; };
use crate::manager::DeviceManager; use crate::manager::DeviceManager;
use log::{error, info}; use log::{error, info, trace};
use tao::event_loop::EventLoopBuilder; use tao::event_loop::EventLoopBuilder;
use tray_icon::{ use tray_icon::{
menu::{Menu, MenuEvent, MenuItem}, menu::{Menu, MenuEvent, MenuItem},
TrayIcon, TrayIconBuilder, 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 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); const DEVICE_FETCH_INTERVAL: std::time::Duration = std::time::Duration::from_secs(5);
@ -42,6 +43,8 @@ pub struct TrayApp {
device_manager: Arc<Mutex<DeviceManager>>, device_manager: Arc<Mutex<DeviceManager>>,
devices: Arc<Mutex<HashMap<u32, MemoryDevice>>>, devices: Arc<Mutex<HashMap<u32, MemoryDevice>>>,
tray_icon: Rc<Mutex<Option<TrayIcon>>>, tray_icon: Rc<Mutex<Option<TrayIcon>>>,
console_state: Arc<Mutex<bool>>,
menu_items: Arc<Mutex<Vec<MenuItem>>>
} }
impl TrayApp { impl TrayApp {
@ -50,13 +53,15 @@ impl TrayApp {
device_manager: Arc::new(Mutex::new(DeviceManager::new())), device_manager: Arc::new(Mutex::new(DeviceManager::new())),
devices: Arc::new(Mutex::new(HashMap::new())), devices: Arc::new(Mutex::new(HashMap::new())),
tray_icon: Rc::new(Mutex::new(None)), 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) { pub fn run(&self) {
let icon = Self::create_icon(); let icon = Self::create_icon();
let event_loop = EventLoopBuilder::new().build(); let event_loop = EventLoopBuilder::new().build();
let tray_menu = Self::create_menu(); let tray_menu = self.create_menu();
let (sender, receiver) = mpsc::channel(); let (sender, receiver) = mpsc::channel();
@ -77,10 +82,17 @@ impl TrayApp {
tray_icon::Icon::from_rgba(rgba, width, height).expect("Failed to create icon") 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 tray_menu = Menu::new();
let show_console_item = MenuItem::new("Show Log Window", true, None);
let quit_item = MenuItem::new("Exit", 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 tray_menu
} }
@ -113,7 +125,7 @@ impl TrayApp {
} }
} }
} }
if !connected_devices.is_empty() { if !connected_devices.is_empty() {
tx.send(connected_devices).unwrap(); tx.send(connected_devices).unwrap();
} }
@ -142,6 +154,8 @@ impl TrayApp {
let tray_icon = Rc::clone(&self.tray_icon); let tray_icon = Rc::clone(&self.tray_icon);
let devices = Arc::clone(&self.devices); let devices = Arc::clone(&self.devices);
let device_manager = Arc::clone(&self.device_manager); 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(); let menu_channel = MenuEvent::receiver();
@ -155,18 +169,39 @@ impl TrayApp {
} }
if let tao::event::Event::NewEvents(tao::event::StartCause::Init) = event { 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 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; *control_flow = tao::event_loop::ControlFlow::Exit;
} }
} }
}); });
} }
fn create_tray_icon( fn build_tray(
tray_icon: &Rc<Mutex<Option<TrayIcon>>>, tray_icon: &Rc<Mutex<Option<TrayIcon>>>,
tray_menu: &Menu, tray_menu: &Menu,
icon: tray_icon::Icon, icon: tray_icon::Icon,