diff --git a/Cargo.lock b/Cargo.lock index 4f652f9..c41a6a8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -85,7 +85,7 @@ dependencies = [ [[package]] name = "cloud-flight" -version = "0.1.0" +version = "0.1.1" dependencies = [ "clokwerk", "hidapi", diff --git a/Cargo.toml b/Cargo.toml index 486f1b8..f93ffcc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cloud-flight" -version = "0.1.0" +version = "0.1.1" edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/src/cloud_flight.rs b/src/cloud_flight.rs index 79da4cf..21f86d7 100644 --- a/src/cloud_flight.rs +++ b/src/cloud_flight.rs @@ -1,5 +1,6 @@ use hidapi::{HidApi, HidDevice}; -use log::debug; +use log::{debug, info}; +use std::cell::Cell; const VENDOR_ID: u16 = 0x0951; const PRODUCT_ID: u16 = 0x16c4; @@ -41,7 +42,7 @@ fn battery_percent(charge_state: u8, value: u8) -> u8 { } } -pub enum EventType { +pub enum Event { Battery { value: u8 }, BatteryCharging, VolumeUp, @@ -54,6 +55,10 @@ pub enum EventType { } pub struct CloudFlight { device: HidDevice, + pub powered: Cell, + pub muted: Cell, + pub charging: Cell, + pub battery: Cell, } impl CloudFlight { pub fn new() -> Self { @@ -61,9 +66,13 @@ impl CloudFlight { CloudFlight { device: api.open(VENDOR_ID, PRODUCT_ID).unwrap(), + powered: Cell::new(true), + muted: Cell::new(false), + charging: Cell::new(false), + battery: Cell::new(100), } } - pub fn read(&self) -> EventType { + pub fn read(&self) -> Event { let mut buf = [0u8; 32]; let bytes = self.device.read_timeout(&mut buf, 500).unwrap(); debug!("Read: {}, {:02x?}", bytes, buf); @@ -72,37 +81,54 @@ impl CloudFlight { if buf[0] == 0x64 { if buf[1] == 0x01 { self.battery(); - return EventType::PowerOn; + self.powered.set(true); + info!("Power on"); + return Event::PowerOn; } else if buf[1] == 0x03 { - return EventType::PowerOff; + self.powered.set(false); + info!("Power off"); + return Event::PowerOff; } } if buf[0] == 0x65 { if buf[1] == 0x04 { - return EventType::Muted; + self.muted.set(true); + info!("Muted"); + return Event::Muted; } else { - return EventType::Unmuted; + self.muted.set(false); + info!("Unmuted"); + return Event::Unmuted; } } - return EventType::Ignored; + return Event::Ignored; } 5 => { if buf[1] == 0x01 { - return EventType::VolumeUp; + info!("Volume up"); + return Event::VolumeUp; } else if buf[1] == 0x02 { - return EventType::VolumeDown; + info!("Volume down"); + return Event::VolumeDown; } - return EventType::Ignored; + return Event::Ignored; } 20 => { - if (buf[3] == 0x10 || buf[3] == 0x11) && buf[4] >= 20 { - return EventType::BatteryCharging; + if buf[3] == 0x10 || buf[3] == 0x11 { + info!("Battery charging"); + self.charging.set(true); + if buf[4] >= 20 { + return Event::BatteryCharging; + } + return Event::Battery { value: 100 }; } - return EventType::Battery { - value: battery_percent(buf[3], buf[4]), - }; + let b_percent = battery_percent(buf[3], buf[4]); + info!("Battery {}", b_percent); + self.charging.set(false); + self.battery.set(b_percent); + return Event::Battery { value: b_percent }; } - _ => return EventType::Ignored, + _ => return Event::Ignored, } } pub fn battery(&self) { diff --git a/src/main.rs b/src/main.rs index 9eccccf..9e71ae0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,4 @@ use clokwerk::{Scheduler, TimeUnits}; -use log::info; use simple_logger::SimpleLogger; use std::sync::Arc; use std::thread; @@ -28,37 +27,24 @@ fn main() { loop { scheduler.run_pending(); let event = cf.read(); - svc.update(|tray: &mut tray::Tray| { - match event { - cloud_flight::EventType::BatteryCharging => { - info!("Battery charging") + match event { + cloud_flight::Event::BatteryCharging => (), + cloud_flight::Event::Battery { value: _ } => (), + cloud_flight::Event::VolumeUp => (), + cloud_flight::Event::VolumeDown => (), + cloud_flight::Event::Muted => { + if BATTERY_REFRESH_ON_MUTE { + cf.battery(); } - cloud_flight::EventType::Battery { value } => { - tray.battery = value; - info!("Battery {}", value) - } - cloud_flight::EventType::VolumeUp => { - info!("Volume up") - } - cloud_flight::EventType::VolumeDown => { - info!("Volume down") - } - cloud_flight::EventType::Muted => { - tray.muted = true; - if BATTERY_REFRESH_ON_MUTE { - cf.battery(); - } - info!("Muted") - } - cloud_flight::EventType::Unmuted => { - tray.muted = false; - info!("Unmuted") - } - cloud_flight::EventType::PowerOff => info!("Power off"), - cloud_flight::EventType::PowerOn => info!("Power on"), - cloud_flight::EventType::Ignored => (), - }; - }); + } + cloud_flight::Event::Unmuted => (), + cloud_flight::Event::PowerOff => (), + cloud_flight::Event::PowerOn => (), + cloud_flight::Event::Ignored => (), + }; + if !matches!(event, cloud_flight::Event::Ignored) { + svc.update(); + } } }); handle.join().unwrap(); diff --git a/src/tray.rs b/src/tray.rs index 98fc017..b129ed9 100644 --- a/src/tray.rs +++ b/src/tray.rs @@ -1,3 +1,4 @@ +use ksni::menu::StandardItem; use ksni::ToolTip; use std::sync::Arc; use std::vec::Vec; @@ -14,29 +15,56 @@ const HEADPHONES_BATTERY_CAUTION: &str = "battery-010"; const HEADPHONES_BATTERY_EMPTY: &str = "battery-empty.svg"; pub struct Tray { - pub muted: bool, - pub battery: u8, cf: Arc, } impl ksni::Tray for Tray { fn icon_name(&self) -> String { - if self.muted { + if self.cf.muted.get() { HEADPHONES_MUTED.to_string() + } else if self.cf.charging.get() { + HEADPHONES_BATTERY_CHARGING.to_string() } else { - battery_icon(self.battery).into() + match self.cf.battery.get() { + 0..=19 => HEADPHONES_BATTERY_CAUTION, + 20..=39 => HEADPHONES_BATTERY_LOW, + 40..=59 => HEADPHONES_BATTERY_MEDIUM, + 60..=89 => HEADPHONES_BATTERY_GOOD, + 90..=100 => HEADPHONES_BATTERY_FULL, + _ => HEADPHONES_BATTERY_EMPTY, + } + .to_string() } } fn tool_tip(&self) -> ToolTip { + let description: String; + if self.cf.charging.get() { + description = format!("Charging battery"); + } else { + description = format!("Battery: {}%", self.cf.battery.get()); + } ToolTip { title: "HyperX Cloud Flight".into(), - description: format!("Battery: {}%", self.battery), + description: description, icon_name: "".into(), icon_pixmap: Vec::new(), } } fn menu(&self) -> Vec> { - use ksni::menu::*; + let muted_text: String; + if self.cf.muted.get() { + muted_text = "Yes".into(); + } else { + muted_text = "No".into(); + } + + let battery_text: String; + if self.cf.charging.get() { + battery_text = "Battery charging".into(); + } else { + battery_text = format!("Battery level: {}", self.cf.battery.get()); + } + vec![ StandardItem { label: "HyperX Cloud Flight".into(), @@ -44,12 +72,12 @@ impl ksni::Tray for Tray { } .into(), StandardItem { - label: format!("Muted: {}", self.muted), + label: format!("Muted: {}", muted_text), ..Default::default() } .into(), StandardItem { - label: format!("Battery level: {}", self.battery), + label: battery_text, activate: { let cf = self.cf.clone(); Box::new(move |_| cf.clone().battery()) @@ -73,29 +101,12 @@ pub struct TrayService { impl TrayService { pub fn new(cf: Arc) -> Self { - let svc = ksni::TrayService::new(Tray { - muted: false, - battery: 100, - cf: cf, - }); + let svc = ksni::TrayService::new(Tray { cf: cf }); let handle = svc.handle(); svc.spawn(); TrayService { handle: handle } } - pub fn update(&self, f: F) { - self.handle.update(f); - } -} - -fn battery_icon(battery: u8) -> String { - match battery { - 0..=19 => HEADPHONES_BATTERY_CAUTION, - 20..=39 => HEADPHONES_BATTERY_LOW, - 40..=59 => HEADPHONES_BATTERY_MEDIUM, - 60..=89 => HEADPHONES_BATTERY_GOOD, - 90..=100 => HEADPHONES_BATTERY_FULL, - 101 => HEADPHONES_BATTERY_CHARGING, - _ => HEADPHONES_BATTERY_EMPTY, + pub fn update(&self) { + self.handle.update(|_: &mut Tray| {}); } - .to_string() }