#![cfg_attr(target_os = "windows", windows_subsystem = "windows")] #[cfg(target_os = "linux")] mod status_tray; #[cfg(not(target_os = "linux"))] mod status_tray_not_linux; mod tray_battery_icon_state; #[cfg(not(target_os = "linux"))] fn main() { use clap::ArgAction; use std::sync::mpsc; use hyper_headset::devices::{DeviceEvent, DeviceProperties}; use hyper_headset::VERBOSE; use winit::event_loop::{ControlFlow, EventLoop, EventLoopProxy}; use crate::status_tray_not_linux::TrayApp; let event_loop: EventLoop> = EventLoop::with_user_event().build().unwrap(); let proxy: EventLoopProxy> = event_loop.create_proxy(); event_loop.set_control_flow(ControlFlow::Wait); let (tx, rx) = mpsc::channel::(); std::thread::spawn(move || { use std::time::Duration; use clap::{Arg, Command}; use enigo::{Direction, Enigo, Key, Keyboard, Settings}; use hyper_headset::devices::connect_compatible_device; let matches = Command::new(env!("CARGO_PKG_NAME")) .version(env!("CARGO_PKG_VERSION")) .disable_version_flag(false) .author(env!("CARGO_PKG_AUTHORS")) .about("A tray application for monitoring HyperX headsets.") .arg( Arg::new("refresh_interval") .long("refresh_interval") .required(false) .help("Set the refresh interval (in seconds)") .default_value("3") .value_parser(clap::value_parser!(u64)), ) .arg( Arg::new("press_mute_key") .long("press_mute_key") .required(false) .help("The app will simulate pressing the microphone mute key whoever the headsets is muted or unmuted.") .default_value("true") .value_parser(clap::value_parser!(bool)), ) .arg(Arg::new("verbose") .long("verbose") .short('v') .action(ArgAction::SetTrue) .required(false) .help("Use verbose output ") ) .get_matches(); VERBOSE.set(matches.get_flag("verbose")).unwrap(); let press_mute_key = *matches.get_one::("press_mute_key").unwrap_or(&true); let mut enigo = if press_mute_key { match Enigo::new(&Settings::default()) { Ok(enigo) => Some(enigo), Err(e) => { eprintln!("Virtual mute key failed to initialize: {e}"); None } } } else { None }; let refresh_interval = *matches.get_one::("refresh_interval").unwrap_or(&3); let refresh_interval = Duration::from_secs(refresh_interval); loop { let mut device = loop { match connect_compatible_device() { Ok(d) => break d, Err(e) => { let _ = proxy.send_event(None); eprintln!("Connecting failed with error: {e}") } } std::thread::sleep(Duration::from_secs(1)); }; // Run loop let mut run_counter = 0; loop { let mute_state = device.get_device_state().device_properties.muted; match if run_counter % 30 == 0 { device.active_refresh_state() } else { device.passive_refresh_state() } { Ok(()) => (), Err(error) => { eprintln!("{error}"); let _ = proxy .send_event(Some(device.get_device_state().device_properties.clone())); break; // try to reconnect } }; if mute_state.is_some() && mute_state != device.get_device_state().device_properties.muted { if let Some(enigo) = &mut enigo { if let Err(e) = enigo.key(Key::F20, Direction::Click) { eprintln!("Failed to press key on mute: {e}"); } } } // with the default refresh_interval the state is only actively queried every 3min // querying the device to frequently can lead to instability let first = rx.recv_timeout(refresh_interval); for command in first.into_iter().chain(rx.try_iter()) { let _ = device.try_apply(command); std::thread::sleep(hyper_headset::devices::RESPONSE_DELAY); let _ = device.active_refresh_state(); } let _ = proxy.send_event(Some(device.get_device_state().device_properties.clone())); run_counter += 1; } } }); event_loop.run_app(&mut TrayApp::new(tx)).unwrap(); } #[cfg(target_os = "linux")] fn main() { use clap::ArgAction; use clap::{Arg, Command}; use enigo::{Direction, Enigo, Key, Keyboard, Settings}; use std::sync::mpsc; use std::time::Duration; use hyper_headset::devices::connect_compatible_device; use status_tray::{StatusTray, TrayHandler}; use hyper_headset::prompt_user_for_udev_rule; use hyper_headset::{act_as_askpass_handler, VERBOSE}; if let Ok(name) = std::env::current_exe() { if let Some(name) = name.to_str() { if let Ok(askpass) = std::env::var("SUDO_ASKPASS") { if name == askpass { act_as_askpass_handler(); } } } } prompt_user_for_udev_rule(); let matches = Command::new(env!("CARGO_PKG_NAME")) .version(env!("CARGO_PKG_VERSION")) .disable_version_flag(false) .author(env!("CARGO_PKG_AUTHORS")) .about("A tray application for monitoring HyperX headsets.") .arg( Arg::new("refresh_interval") .long("refresh_interval") .required(false) .help("Set the refresh interval (in seconds)") .default_value("3") .value_parser(clap::value_parser!(u64)), ) .arg( Arg::new("press_mute_key") .long("press_mute_key") .required(false) .help("The app will simulate pressing the microphone mute key whoever the headsets is muted or unmuted.") .default_value("true") .value_parser(clap::value_parser!(bool)), ) .arg(Arg::new("verbose") .long("verbose") .short('v') .action(ArgAction::SetTrue) .required(false) .help("Use verbose output ") ) .get_matches(); let press_mute_key = *matches.get_one::("press_mute_key").unwrap_or(&true); let mut enigo = if press_mute_key { match Enigo::new(&Settings::default()) { Ok(enigo) => Some(enigo), Err(e) => { eprintln!("Virtual mute key failed to initialize: {e}"); None } } } else { None }; VERBOSE.set(matches.get_flag("verbose")).unwrap(); let refresh_interval = *matches.get_one::("refresh_interval").unwrap_or(&3); let refresh_interval = Duration::from_secs(refresh_interval); let (tx, rx) = mpsc::channel(); let tray_handler = TrayHandler::new(StatusTray::new(tx)); loop { let mut device = loop { match connect_compatible_device() { Ok(d) => break d, Err(e) => { tray_handler.clear_state(); eprintln!("Connecting failed with error: {e}") } } std::thread::sleep(Duration::from_secs(1)); }; // Run loop let mut run_counter = 0; loop { let mute_state = device.get_device_state().device_properties.muted; match if run_counter % 30 == 0 { device.active_refresh_state() } else { device.passive_refresh_state() } { Ok(()) => (), Err(error) => { eprintln!("{error}"); tray_handler.update(device.get_device_state()); break; // try to reconnect } }; if mute_state.is_some() && mute_state != device.get_device_state().device_properties.muted { if let Some(enigo) = &mut enigo { if let Err(e) = enigo.key(Key::MicMute, Direction::Click) { eprintln!("Failed to press key on mute: {e}"); } } } // with the default refresh_interval the state is only actively queried every 3min // querying the device to frequently can lead to instability let first = rx.recv_timeout(refresh_interval); for command in first.into_iter().chain(rx.try_iter()) { let _ = device.try_apply(command); std::thread::sleep(hyper_headset::devices::RESPONSE_DELAY); let _ = device.active_refresh_state(); } tray_handler.update(device.get_device_state()); run_counter += 1; } } }