Compare commits

..

No commits in common. "f5349a56a5852aaa2b2337c86181018e0ce39bda" and "ce2cd4605518db1c0211e99535549b65f868264a" have entirely different histories.

4 changed files with 70 additions and 88 deletions

2
Cargo.lock generated
View File

@ -1813,7 +1813,7 @@ dependencies = [
[[package]] [[package]]
name = "razer-battery-report" name = "razer-battery-report"
version = "0.2.3" version = "0.2.2"
dependencies = [ dependencies = [
"hidapi", "hidapi",
"image", "image",

View File

@ -1,6 +1,6 @@
[package] [package]
name = "razer-battery-report" name = "razer-battery-report"
version = "0.2.3" version = "0.2.2"
authors = ["xzeldon <contact@zeldon.ru>"] authors = ["xzeldon <contact@zeldon.ru>"]
edition = "2021" edition = "2021"
description = "Razer Battery Level Tray Indicator" description = "Razer Battery Level Tray Indicator"
@ -11,13 +11,6 @@ lto = "fat"
codegen-units = 1 codegen-units = 1
opt-level = 3 opt-level = 3
# Faster builds, slower executables
[profile.dev]
opt-level = 0
lto = false
incremental = true
codegen-units = 16
[dependencies] [dependencies]
# Communicate with HID devices # Communicate with HID devices
hidapi = "2.6.3" hidapi = "2.6.3"

View File

@ -43,7 +43,7 @@ To build, you must have [Rust](https://www.rust-lang.org/) and
- [ ] 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
- [x] Show log window button in tray menu - [x] Show log window button in tray menu
- [x] Further reduce CPU usage by using Event Loop Proxy events (more info [here](https://github.com/tauri-apps/tray-icon/issues/83#issuecomment-1697773065)) - [ ] Further reduce CPU usage by using Event Loop Proxy events (more info [here](https://github.com/tauri-apps/tray-icon/issues/83#issuecomment-1697773065))
- [ ] 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)

View File

@ -1,20 +1,21 @@
use std::{ use std::{
collections::{HashMap, HashSet}, collections::HashMap,
sync::Arc, rc::Rc,
sync::{mpsc, Arc},
thread, thread,
time::Duration, time::{Duration, Instant},
}; };
use crate::{console::DebugConsole, manager::DeviceManager}; use crate::{console::DebugConsole, manager::DeviceManager};
use log::{error, info, trace}; use log::{error, info, trace};
use parking_lot::Mutex; use parking_lot::Mutex;
use tao::event_loop::{EventLoopBuilder, EventLoopProxy}; use tao::event_loop::EventLoopBuilder;
use tray_icon::{ use tray_icon::{
menu::{IsMenuItem, Menu, MenuEvent, MenuItem}, menu::{Menu, MenuEvent, MenuItem},
TrayIcon, TrayIconBuilder, TrayIcon, TrayIconBuilder,
}; };
const BATTERY_UPDATE_INTERVAL: Duration = Duration::from_secs(300); // 5 min const BATTERY_UPDATE_INTERVAL: Duration = Duration::from_secs(60);
const DEVICE_FETCH_INTERVAL: Duration = Duration::from_secs(5); const DEVICE_FETCH_INTERVAL: Duration = Duration::from_secs(5);
#[derive(Debug)] #[derive(Debug)]
@ -40,7 +41,7 @@ impl MemoryDevice {
} }
pub struct TrayInner { pub struct TrayInner {
tray_icon: Arc<Mutex<Option<TrayIcon>>>, tray_icon: Rc<Mutex<Option<TrayIcon>>>,
menu_items: Arc<Mutex<Vec<MenuItem>>>, menu_items: Arc<Mutex<Vec<MenuItem>>>,
debug_console: Arc<DebugConsole>, debug_console: Arc<DebugConsole>,
} }
@ -48,7 +49,7 @@ pub struct TrayInner {
impl TrayInner { impl TrayInner {
fn new(debug_console: Arc<DebugConsole>) -> Self { fn new(debug_console: Arc<DebugConsole>) -> Self {
Self { Self {
tray_icon: Arc::new(Mutex::new(None)), tray_icon: Rc::new(Mutex::new(None)),
menu_items: Arc::new(Mutex::new(Vec::new())), menu_items: Arc::new(Mutex::new(Vec::new())),
debug_console, debug_console,
} }
@ -64,19 +65,14 @@ impl TrayInner {
menu_items.push(show_console_item); menu_items.push(show_console_item);
menu_items.push(quit_item); menu_items.push(quit_item);
let item_refs: Vec<&dyn IsMenuItem> = menu_items
.iter()
.map(|item| item as &dyn IsMenuItem)
.collect();
tray_menu tray_menu
.append_items(&item_refs) .append_items(&[&menu_items[0], &menu_items[1]])
.expect("Failed to append menu items"); .unwrap();
tray_menu tray_menu
} }
fn build_tray( fn build_tray(
tray_icon: &Arc<Mutex<Option<TrayIcon>>>, tray_icon: &Rc<Mutex<Option<TrayIcon>>>,
tray_menu: &Menu, tray_menu: &Menu,
icon: tray_icon::Icon, icon: tray_icon::Icon,
) { ) {
@ -99,12 +95,6 @@ pub struct TrayApp {
tray_inner: TrayInner, tray_inner: TrayInner,
} }
#[derive(Debug)]
enum TrayEvent {
DeviceUpdate(Vec<u32>),
MenuEvent(MenuEvent),
}
impl TrayApp { impl TrayApp {
pub fn new(debug_console: DebugConsole) -> Self { pub fn new(debug_console: DebugConsole) -> Self {
Self { Self {
@ -116,15 +106,15 @@ impl TrayApp {
pub fn run(&self) { pub fn run(&self) {
let icon = Self::create_icon(); let icon = Self::create_icon();
let event_loop = EventLoopBuilder::with_user_event().build(); let event_loop = EventLoopBuilder::new().build();
let tray_menu = self.tray_inner.create_menu(); let tray_menu = self.tray_inner.create_menu();
let proxy = event_loop.create_proxy(); let (sender, receiver) = mpsc::channel();
self.spawn_device_fetch_thread(proxy.clone()); self.spawn_device_fetch_thread(sender.clone());
self.spawn_battery_check_thread(proxy.clone()); self.spawn_battery_check_thread(sender);
self.run_event_loop(event_loop, icon, tray_menu, proxy); self.run_event_loop(event_loop, icon, tray_menu, receiver);
} }
fn create_icon() -> tray_icon::Icon { fn create_icon() -> tray_icon::Icon {
@ -138,18 +128,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 spawn_device_fetch_thread(&self, proxy: EventLoopProxy<TrayEvent>) { fn spawn_device_fetch_thread(&self, tx: mpsc::Sender<Vec<u32>>) {
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);
thread::spawn(move || { thread::spawn(move || loop {
let mut last_devices = HashSet::new();
loop {
let (removed_devices, connected_devices) = { let (removed_devices, connected_devices) = {
let mut manager = device_manager.lock(); let mut manager = device_manager.lock();
manager.fetch_devices() manager.fetch_devices()
}; };
{
let mut devices = devices.lock(); let mut devices = devices.lock();
for id in removed_devices { for id in removed_devices {
if let Some(device) = devices.remove(&id) { if let Some(device) = devices.remove(&id) {
@ -158,69 +147,75 @@ impl TrayApp {
} }
for &id in &connected_devices { for &id in &connected_devices {
if !devices.contains_key(&id) { if let std::collections::hash_map::Entry::Vacant(e) = devices.entry(id) {
if let Some(name) = device_manager.lock().get_device_name(id) { if let Some(name) = device_manager.lock().get_device_name(id) {
devices.insert(id, MemoryDevice::new(name.clone(), id)); e.insert(MemoryDevice::new(name.clone(), id));
info!("New device: {}", name); info!("New device: {}", name);
} else { } else {
error!("Failed to get device name for id: {}", id); error!("Failed to get device name for id: {}", id);
} }
} }
} }
}
let current_devices: HashSet<_> = connected_devices.iter().cloned().collect(); if !connected_devices.is_empty() {
if current_devices != last_devices { tx.send(connected_devices).unwrap();
let _ = proxy.send_event(TrayEvent::DeviceUpdate(connected_devices));
last_devices = current_devices;
} }
thread::sleep(DEVICE_FETCH_INTERVAL); thread::sleep(DEVICE_FETCH_INTERVAL);
}
}); });
} }
fn spawn_battery_check_thread(&self, proxy: EventLoopProxy<TrayEvent>) { fn spawn_battery_check_thread(&self, tx: mpsc::Sender<Vec<u32>>) {
let devices = Arc::clone(&self.devices); let devices = Arc::clone(&self.devices);
thread::spawn(move || loop { thread::spawn(move || loop {
let device_ids: Vec<u32> = devices.lock().keys().cloned().collect(); let device_ids: Vec<u32> = devices.lock().keys().cloned().collect();
let _ = proxy.send_event(TrayEvent::DeviceUpdate(device_ids)); tx.send(device_ids).unwrap();
thread::sleep(BATTERY_UPDATE_INTERVAL); thread::sleep(BATTERY_UPDATE_INTERVAL);
}); });
} }
fn run_event_loop( fn run_event_loop(
&self, &self,
event_loop: tao::event_loop::EventLoop<TrayEvent>, event_loop: tao::event_loop::EventLoop<()>,
icon: tray_icon::Icon, icon: tray_icon::Icon,
tray_menu: Menu, tray_menu: Menu,
proxy: EventLoopProxy<TrayEvent>, rx: mpsc::Receiver<Vec<u32>>,
) { ) {
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 tray_icon = Arc::clone(&self.tray_inner.tray_icon); let tray_icon = Rc::clone(&self.tray_inner.tray_icon);
let debug_console = Arc::clone(&self.tray_inner.debug_console); let debug_console = Arc::clone(&self.tray_inner.debug_console);
let menu_items = Arc::clone(&self.tray_inner.menu_items); let menu_items = Arc::clone(&self.tray_inner.menu_items);
let menu_channel = MenuEvent::receiver(); let menu_channel = MenuEvent::receiver();
event_loop.run(move |event, _, control_flow| { event_loop.run(move |event, _, control_flow| {
*control_flow = tao::event_loop::ControlFlow::Wait; *control_flow = tao::event_loop::ControlFlow::WaitUntil(
Instant::now() + Duration::from_millis(100),
);
match event { if let Ok(device_ids) = rx.try_recv() {
tao::event::Event::NewEvents(tao::event::StartCause::Init) => {
TrayInner::build_tray(&tray_icon, &tray_menu, icon.clone());
}
tao::event::Event::UserEvent(TrayEvent::DeviceUpdate(device_ids)) => {
Self::update(&devices, &device_manager, &device_ids, &tray_icon); Self::update(&devices, &device_manager, &device_ids, &tray_icon);
} }
tao::event::Event::UserEvent(TrayEvent::MenuEvent(event)) => {
if let tao::event::Event::NewEvents(tao::event::StartCause::Init) = event {
// We create the icon once the event loop is actually running
// to prevent issues like https://github.com/tauri-apps/tray-icon/issues/90
TrayInner::build_tray(&tray_icon, &tray_menu, icon.clone());
}
if let Ok(event) = menu_channel.try_recv() {
let menu_items = menu_items.lock(); let menu_items = menu_items.lock();
if event.id == menu_items[0].id() { let show_console_item = &menu_items[0];
let quit_item = &menu_items[1];
if event.id == show_console_item.id() {
debug_console.toggle_visibility(); debug_console.toggle_visibility();
let visible = debug_console.is_visible(); let visible = debug_console.is_visible();
menu_items[0].set_text(if visible { show_console_item.set_text(if visible {
"Hide Log Window" "Hide Log Window"
} else { } else {
"Show Log Window" "Show Log Window"
@ -228,16 +223,10 @@ impl TrayApp {
trace!("{} log window", if visible { "showing" } else { "hiding" }); trace!("{} log window", if visible { "showing" } else { "hiding" });
} }
if event.id == menu_items[1].id() { if event.id == quit_item.id() {
*control_flow = tao::event_loop::ControlFlow::Exit; *control_flow = tao::event_loop::ControlFlow::Exit;
} }
} }
_ => (),
}
if let Ok(event) = menu_channel.try_recv() {
let _ = proxy.send_event(TrayEvent::MenuEvent(event));
}
}); });
} }
@ -245,7 +234,7 @@ impl TrayApp {
devices: &Arc<Mutex<HashMap<u32, MemoryDevice>>>, devices: &Arc<Mutex<HashMap<u32, MemoryDevice>>>,
manager: &Arc<Mutex<DeviceManager>>, manager: &Arc<Mutex<DeviceManager>>,
device_ids: &[u32], device_ids: &[u32],
tray_icon: &Arc<Mutex<Option<TrayIcon>>>, tray_icon: &Rc<Mutex<Option<TrayIcon>>>,
) { ) {
let mut devices = devices.lock(); let mut devices = devices.lock();
let manager = manager.lock(); let manager = manager.lock();