Compare commits
3 Commits
805ba16c2c
...
ce2cd46055
Author | SHA1 | Date |
---|---|---|
Timofey Gelazoniya | ce2cd46055 | |
Timofey Gelazoniya | 8577920504 | |
Timofey Gelazoniya | fc7dd809fe |
|
@ -1813,11 +1813,12 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "razer-battery-report"
|
||||
version = "0.2.1"
|
||||
version = "0.2.2"
|
||||
dependencies = [
|
||||
"hidapi",
|
||||
"image",
|
||||
"log",
|
||||
"parking_lot",
|
||||
"pretty_env_logger",
|
||||
"tao",
|
||||
"tray-icon",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "razer-battery-report"
|
||||
version = "0.2.1"
|
||||
version = "0.2.2"
|
||||
authors = ["xzeldon <contact@zeldon.ru>"]
|
||||
edition = "2021"
|
||||
description = "Razer Battery Level Tray Indicator"
|
||||
|
@ -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"
|
||||
|
|
|
@ -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<Mutex<bool>>,
|
||||
}
|
||||
|
||||
impl DebugConsole {
|
||||
pub fn new(title: &str) -> Self {
|
||||
unsafe {
|
||||
consoleapi::AllocConsole();
|
||||
|
||||
let title: Vec<u16> = 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()
|
||||
}
|
||||
}
|
25
src/main.rs
25
src/main.rs
|
@ -1,35 +1,20 @@
|
|||
#![windows_subsystem = "windows"]
|
||||
|
||||
use console::DebugConsole;
|
||||
use tray::TrayApp;
|
||||
use winapi::um::{self, wincon, winuser};
|
||||
|
||||
mod console;
|
||||
mod controller;
|
||||
mod devices;
|
||||
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);
|
||||
}
|
||||
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();
|
||||
}
|
||||
|
|
|
@ -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<u32>, Vec<u32>) {
|
||||
let old_ids: HashSet<u32> = {
|
||||
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<u32> = old_ids.difference(&new_ids).cloned().collect();
|
||||
let connected_devices: Vec<u32> = 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<String> {
|
||||
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<i32> {
|
||||
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<bool> {
|
||||
let controllers = self.device_controllers.lock().unwrap();
|
||||
let controllers = self.device_controllers.lock();
|
||||
let controller = controllers
|
||||
.iter()
|
||||
.find(|controller| controller.pid as u32 == id)?;
|
||||
|
|
60
src/tray.rs
60
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<Mutex<Option<TrayIcon>>>,
|
||||
console_state: Arc<Mutex<bool>>,
|
||||
menu_items: Arc<Mutex<Vec<MenuItem>>>,
|
||||
debug_console: Arc<DebugConsole>,
|
||||
}
|
||||
|
||||
impl TrayInner {
|
||||
fn new() -> Self {
|
||||
fn new(debug_console: Arc<DebugConsole>) -> 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<u32> = devices.lock().unwrap().keys().cloned().collect();
|
||||
let device_ids: Vec<u32> = 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<Mutex<Option<TrayIcon>>>,
|
||||
) {
|
||||
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)));
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue