Connect tray to new code base

This commit is contained in:
Lennard Kittner
2025-03-22 12:23:15 +01:00
parent 9a4f3805dc
commit 00b47d86ea
2 changed files with 73 additions and 151 deletions

View File

@@ -1,5 +1,6 @@
use hyper_x_cloud_ii_wireless::Device; use hyper_x_cloud_ii_wireless::devices::{Device, DeviceState};
use ksni::{Tray, MenuItem, menu::{StandardItem}, ToolTip, TrayService, Handle}; use ksni::{menu::StandardItem, Handle, MenuItem, ToolTip, Tray, TrayService};
use std::sync::{Arc, RwLock, RwLockReadGuard};
pub struct TrayHandler { pub struct TrayHandler {
handle: Handle<BatteryTray>, handle: Handle<BatteryTray>,
@@ -10,58 +11,35 @@ impl TrayHandler {
let tray_service = TrayService::new(tray); let tray_service = TrayService::new(tray);
let handle = tray_service.handle(); let handle = tray_service.handle();
tray_service.spawn(); tray_service.spawn();
TrayHandler { TrayHandler { handle }
handle,
}
} }
pub fn update(&self, device: &Device) { pub fn update(&self, device_state: &DeviceState) {
self.handle.update(|tray: &mut BatteryTray| { tray.update(device); }) let message = if device_state.connected.unwrap_or(false) {
} Some(device_state.to_string())
} else {
pub fn set_status(&mut self, message: &str) { None
self.handle.update(|tray: &mut BatteryTray| { tray.set_status(message); }) };
} let name = device_state.device_name.clone();
self.handle.update(|tray| {
pub fn clear_status(&mut self) { tray.message = message;
self.handle.update(|tray: &mut BatteryTray| { tray.clear_status(); }) tray.device_name = name;
})
} }
} }
#[derive(Debug)]
pub struct BatteryTray { pub struct BatteryTray {
battery_level: u8, device_name: Option<String>,
charging: Option<bool>, message: Option<String>,
muted: Option<bool>,
mic_connected: Option<bool>,
status_message: Option<String>,
} }
impl BatteryTray { impl BatteryTray {
pub fn new() -> Self { pub fn new() -> Self {
BatteryTray { BatteryTray {
battery_level: 0, device_name: None,
charging: None, message: None,
muted: None,
mic_connected: None,
status_message: Some("No device found".to_string()),
} }
} }
pub fn update(&mut self, device: &Device) {
self.battery_level = device.battery_level;
self.charging = device.charging;
self.muted = device.muted;
self.mic_connected = device.mic_connected;
}
pub fn set_status(&mut self, message: &str) {
self.status_message = Some(message.to_string());
}
pub fn clear_status(&mut self) {
self.status_message = None;
}
} }
impl Tray for BatteryTray { impl Tray for BatteryTray {
@@ -72,71 +50,45 @@ impl Tray for BatteryTray {
"audio-headset".into() "audio-headset".into()
} }
fn tool_tip(&self) -> ToolTip { fn tool_tip(&self) -> ToolTip {
let description = match &self.status_message { println!("tool_tip");
Some(m) => m.clone(), let description = if let Some(message) = self.message.clone() {
None => { message
let mut description = format!("Battery level: {}%", self.battery_level); } else {
if let Some(charging) = self.charging { "Headset is not connected".to_string()
if charging {
description += "\nCharging";
} else {
description += "\nNot charging";
}
}
if let Some(muted) = self.muted {
if muted {
description += "\nMuted";
} else {
description += "\nNot muted";
}
}
if let Some(mic_connected) = self.mic_connected {
if mic_connected {
description += "\nMicrophone connected";
} else {
description += "\nMicrophone not connected";
}
}
description
},
}; };
ToolTip { ToolTip {
title: "HyperX Cloud II".to_string(), title: self.device_name.clone().unwrap_or("Unknown".to_string()),
description, description,
icon_name: "audio-headset".into(), icon_name: "audio-headset".into(),
icon_pixmap: Vec::new(), icon_pixmap: Vec::new(),
} }
} }
fn menu(&self) -> Vec<MenuItem<Self>> { fn menu(&self) -> Vec<MenuItem<Self>> {
let mut items = vec![ println!("menu");
StandardItem { let message = if let Some(message) = &self.message {
label: format!("Battery level: {bat}% ({crg})", bat = self.battery_level,crg = (if self.charging.is_some() { "Charging" } else {"Discharging"})), message
enabled: false, } else {
..Default::default() &"Headset is not connected".to_string()
} };
.into(),
StandardItem { let mut state_items: Vec<MenuItem<Self>> = message
label: "Exit".into(), .lines()
icon_name: "application-exit".into(), .map(|line| {
activate: Box::new(|_| std::process::exit(0)), StandardItem {
..Default::default() label: line.to_string(),
} enabled: false,
.into(), ..Default::default()
]; }
if let Some(muted) = self.muted { .into()
items.insert(1, StandardItem { })
label: if muted { "Muted" } else { "Not muted" }.into(), .collect();
enabled: false, let exit = StandardItem {
..Default::default() label: "Exit".into(),
}.into()); icon_name: "application-exit".into(),
} activate: Box::new(|_| std::process::exit(0)),
if let Some(mic_connected) = self.mic_connected { ..Default::default()
items.insert(2, StandardItem { };
label: if mic_connected { "Microphone connected" } else { "Microphone not connected" }.into(), state_items.push(exit.into());
enabled: false, state_items
..Default::default()
}.into());
}
items
} }
} }

View File

@@ -1,78 +1,48 @@
use std::sync::{Arc, RwLock};
use std::time::Duration; use std::time::Duration;
use hyper_x_cloud_ii_wireless::{Device, DeviceError};
mod battery_tray; mod battery_tray;
use battery_tray::{TrayHandler, BatteryTray}; use battery_tray::{BatteryTray, TrayHandler};
use hyper_x_cloud_ii_wireless::devices::{connect_compatible_device, Device, DeviceError};
fn pair_device() -> Device { fn handle_error(error: DeviceError) -> String {
loop {
match Device::new() {
Ok(device) => break device,
Err(error) => {
eprintln!("{error}");
}
};
std::thread::sleep(std::time::Duration::from_secs(1));
}
}
fn handle_error(error: DeviceError, device: &mut Device, tray_handler: &mut TrayHandler) {
match error { match error {
DeviceError::HidError(hidapi::HidError::HidApiError { message }) => { DeviceError::HidError(hidapi::HidError::HidApiError { message }) => {
if message == "No such device" { if message == "No such device" {
eprintln!("No device found."); eprintln!("No device found.");
tray_handler.set_status("No device found."); "No such device".to_string()
*device = pair_device();
} else { } else {
eprintln!("{message}"); eprintln!("{message}");
message
} }
} }
DeviceError::NoDeviceFound() => {
eprintln!("{}", DeviceError::NoDeviceFound());
device.clear_state();
tray_handler.update(device);
tray_handler.set_status( &DeviceError::NoDeviceFound().to_string());
}
DeviceError::HeadSetOff() => {
eprintln!("{}", DeviceError::HeadSetOff());
device.clear_state();
tray_handler.update(device);
tray_handler.set_status(&DeviceError::HeadSetOff().to_string());
}
error => { error => {
eprintln!("{error}"); eprintln!("{error}");
error.to_string()
} }
} }
} }
fn main() { fn main() {
let mut tray_handler = TrayHandler::new(BatteryTray::new()); let mut device = loop {
let mut device = pair_device(); match connect_compatible_device() {
tray_handler.update(&device); Ok(d) => break d,
Err(e) => println!("Connecting failed with error: {e}"),
}
std::thread::sleep(Duration::from_secs(1));
};
let tray_handler = TrayHandler::new(BatteryTray::new());
// Run loop // Run loop
loop { loop {
std::thread::sleep(std::time::Duration::from_secs(1)); std::thread::sleep(Duration::from_secs(1));
match device.update_battery_level() { match device.refresh_state() {
Ok(_) => { Ok(()) => (),
tray_handler.clear_status();
tray_handler.update(&device);
},
Err(error) => { Err(error) => {
handle_error(error, &mut device, &mut tray_handler); eprintln!("{}", error);
continue;
},
};
match device.wait_for_updates(Duration::from_secs(60)) {
Ok(_) => {
tray_handler.clear_status();
tray_handler.update(&device)
},
Err(DeviceError::NoResponse()) => (),
Err(error) => {
handle_error(error, &mut device, &mut tray_handler);
continue;
} }
} };
tray_handler.update(device.get_device_state())
} }
} }