Compare commits
No commits in common. "f5349a56a5852aaa2b2337c86181018e0ce39bda" and "ce2cd4605518db1c0211e99535549b65f868264a" have entirely different histories.
f5349a56a5
...
ce2cd46055
|
@ -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",
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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)
|
||||||
|
|
109
src/tray.rs
109
src/tray.rs
|
@ -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();
|
||||||
|
|
Loading…
Reference in New Issue