276 lines
9.6 KiB
Rust
276 lines
9.6 KiB
Rust
use std::time::Duration;
|
|
|
|
use clap::{Arg, ArgAction, Command};
|
|
use hyper_headset::{
|
|
devices::{connect_compatible_device, Device, DeviceError, DeviceEvent},
|
|
VERBOSE,
|
|
};
|
|
|
|
const SHOW_ALL_OPTIONS: bool = false;
|
|
|
|
/// helper function to enable help messages
|
|
fn device_supports<F>(device: &Result<Box<dyn Device>, DeviceError>, f: F) -> bool
|
|
where
|
|
F: FnOnce(&Box<dyn Device>) -> bool,
|
|
{
|
|
device.as_ref().map(f).unwrap_or(false)
|
|
}
|
|
|
|
fn main() {
|
|
let pre = Command::new(env!("CARGO_PKG_NAME"))
|
|
.version(env!("CARGO_PKG_VERSION"))
|
|
.disable_version_flag(true) // we have to implement version manually
|
|
.disable_help_flag(true)
|
|
.arg(
|
|
Arg::new("verbose")
|
|
.long("verbose")
|
|
.short('v')
|
|
.action(ArgAction::SetTrue)
|
|
.required(false)
|
|
.help("Use verbose output "),
|
|
)
|
|
.arg(
|
|
Arg::new("version")
|
|
.long("version")
|
|
.short('V')
|
|
.action(ArgAction::SetTrue),
|
|
)
|
|
.allow_external_subcommands(true)
|
|
.try_get_matches();
|
|
|
|
if let Ok(pre) = pre {
|
|
VERBOSE.set(pre.get_flag("verbose")).unwrap();
|
|
if pre.get_flag("version") {
|
|
println!("{} {}", env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION"));
|
|
return;
|
|
}
|
|
}
|
|
|
|
#[cfg(target_os = "linux")]
|
|
{
|
|
use hyper_headset::act_as_askpass_handler;
|
|
use hyper_headset::prompt_user_for_udev_rule;
|
|
|
|
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 device = 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 CLI application for monitoring and managing HyperX headsets.")
|
|
.after_help("Help only lists commands supported by this headset.")
|
|
.arg(
|
|
Arg::new("automatic_shutdown")
|
|
.long("automatic_shutdown")
|
|
.required(false)
|
|
.help(
|
|
"Set the delay in minutes after which the headset will automatically shutdown.\n0 will disable automatic shutdown.",
|
|
)
|
|
.hide(!SHOW_ALL_OPTIONS
|
|
&& !device_supports(&device, |d| d.can_set_automatic_shutdown()))
|
|
.value_parser(clap::value_parser!(u8)),
|
|
)
|
|
.arg(
|
|
Arg::new("mute")
|
|
.long("mute")
|
|
.required(false)
|
|
.help("Mute or unmute the headset.")
|
|
.hide(!SHOW_ALL_OPTIONS
|
|
&& !device_supports(&device, |d| d.can_set_mute()))
|
|
.value_parser(clap::value_parser!(bool)),
|
|
)
|
|
.arg(
|
|
Arg::new("enable_side_tone")
|
|
.long("enable_side_tone")
|
|
.required(false)
|
|
.help("Enable or disable side tone.")
|
|
.hide(!SHOW_ALL_OPTIONS
|
|
&& !device_supports(&device, |d| d.can_set_side_tone()))
|
|
.value_parser(clap::value_parser!(bool)),
|
|
)
|
|
.arg(
|
|
Arg::new("side_tone_volume")
|
|
.long("side_tone_volume")
|
|
.required(false)
|
|
.help("Set the side tone volume.")
|
|
.hide(!SHOW_ALL_OPTIONS
|
|
&& !device_supports(&device, |d| d.can_set_side_tone_volume()))
|
|
.value_parser(clap::value_parser!(u8)),
|
|
)
|
|
.arg(
|
|
Arg::new("enable_voice_prompt")
|
|
.long("enable_voice_prompt")
|
|
.required(false)
|
|
.help("Enable voice prompt. This may not be supported on your device.")
|
|
.hide(!SHOW_ALL_OPTIONS
|
|
&& !device_supports(&device, |d| d.can_set_voice_prompt()))
|
|
.value_parser(clap::value_parser!(bool)),
|
|
)
|
|
.arg(
|
|
Arg::new("surround_sound")
|
|
.long("surround_sound")
|
|
.required(false)
|
|
.help("Enables surround sound. This may be on by default and cannot be changed on your device.")
|
|
.hide(!SHOW_ALL_OPTIONS
|
|
&& !device_supports(&device, |d| d.can_set_surround_sound()))
|
|
.value_parser(clap::value_parser!(bool)),
|
|
)
|
|
.arg(
|
|
Arg::new("mute_playback")
|
|
.long("mute_playback")
|
|
.required(false)
|
|
.help("Mute or unmute playback.")
|
|
.hide(!SHOW_ALL_OPTIONS
|
|
&& !device_supports(&device, |d| d.can_set_silent_mode()))
|
|
.value_parser(clap::value_parser!(bool)),
|
|
)
|
|
.arg(
|
|
Arg::new("activate_noise_gate")
|
|
.long("activate_noise_gate")
|
|
.required(false)
|
|
.help("Activates noise gate.")
|
|
.hide(!SHOW_ALL_OPTIONS
|
|
&& !device_supports(&device, |d| d.can_set_silent_mode()))
|
|
.value_parser(clap::value_parser!(bool)),
|
|
)
|
|
.arg(
|
|
Arg::new("verbose")
|
|
.long("verbose")
|
|
.short('v')
|
|
.action(ArgAction::SetTrue)
|
|
.required(false)
|
|
.help("Use verbose output "),
|
|
)
|
|
.arg(
|
|
Arg::new("json")
|
|
.long("json")
|
|
.default_value("false")
|
|
.action(ArgAction::SetTrue)
|
|
.required(false)
|
|
.help("Use JSON output. Time is in seconds."),
|
|
)
|
|
.get_matches();
|
|
|
|
let mut device = match device {
|
|
Ok(device) => device,
|
|
Err(e) => {
|
|
eprintln!("{e}");
|
|
std::process::exit(1)
|
|
}
|
|
};
|
|
|
|
let mut commands = Vec::new();
|
|
if let Some(delay) = matches.get_one::<u8>("automatic_shutdown") {
|
|
let delay = *delay as u64;
|
|
commands.push(DeviceEvent::AutomaticShutdownAfter(Duration::from_secs(
|
|
delay * 60u64,
|
|
)));
|
|
}
|
|
|
|
if let Some(mute) = matches.get_one::<bool>("mute") {
|
|
commands.push(DeviceEvent::Muted(*mute));
|
|
}
|
|
|
|
if let Some(enable) = matches.get_one::<bool>("enable_side_tone") {
|
|
commands.push(DeviceEvent::SideToneOn(*enable));
|
|
}
|
|
|
|
if let Some(volume) = matches.get_one::<u8>("side_tone_volume") {
|
|
commands.push(DeviceEvent::SideToneVolume(*volume));
|
|
}
|
|
|
|
if let Some(enable) = matches.get_one::<bool>("enable_voice_prompt") {
|
|
commands.push(DeviceEvent::VoicePrompt(*enable));
|
|
}
|
|
|
|
if let Some(surround_sound) = matches.get_one::<bool>("surround_sound") {
|
|
commands.push(DeviceEvent::SurroundSound(*surround_sound));
|
|
}
|
|
|
|
if let Some(mute_playback) = matches.get_one::<bool>("mute_playback") {
|
|
commands.push(DeviceEvent::Silent(*mute_playback));
|
|
}
|
|
|
|
if let Some(activate) = matches.get_one::<bool>("activate_noise_gate") {
|
|
commands.push(DeviceEvent::NoiseGateActive(*activate));
|
|
}
|
|
|
|
for command in commands {
|
|
if let Err(e) = device.try_apply(command) {
|
|
eprintln!("{e}");
|
|
std::process::exit(1);
|
|
}
|
|
}
|
|
|
|
std::thread::sleep(Duration::from_secs_f64(0.5));
|
|
|
|
// setting an option may cause a response form the headset
|
|
if device.allow_passive_refresh() {
|
|
if let Err(error) = device.passive_refresh_state() {
|
|
eprintln!("{error}");
|
|
std::process::exit(1);
|
|
};
|
|
}
|
|
|
|
if let Err(error) = device.active_refresh_state() {
|
|
eprintln!("{error}");
|
|
std::process::exit(1);
|
|
};
|
|
|
|
if let Some(output_json) = matches.get_one::<bool>("json") {
|
|
if *output_json {
|
|
let properties = &device.get_device_state().device_properties;
|
|
let mut headset_info_json = "{\n ".to_string();
|
|
|
|
let json_properties: Vec<String> = properties
|
|
.get_properties()
|
|
.iter()
|
|
.map(|property| match property {
|
|
hyper_headset::devices::PropertyDescriptorWrapper::Int(
|
|
property_descriptor,
|
|
_items,
|
|
) => match property_descriptor.data {
|
|
Some(data) => format!(" \"{}\": {},\n", property_descriptor.name, data),
|
|
_ => "".to_string(),
|
|
},
|
|
hyper_headset::devices::PropertyDescriptorWrapper::Bool(
|
|
property_descriptor,
|
|
) => match property_descriptor.data {
|
|
Some(data) => format!(" \"{}\": {},\n", property_descriptor.name, data),
|
|
_ => "".to_string(),
|
|
},
|
|
hyper_headset::devices::PropertyDescriptorWrapper::String(
|
|
property_descriptor,
|
|
) => match &property_descriptor.data {
|
|
Some(data) => {
|
|
format!("\"{}\": \"{}\"", property_descriptor.name, data)
|
|
}
|
|
_ => "".to_string(),
|
|
},
|
|
})
|
|
.collect();
|
|
|
|
headset_info_json += &json_properties.join(",\n ");
|
|
|
|
headset_info_json += "\n}";
|
|
println!("{}", headset_info_json);
|
|
} else {
|
|
println!("{}", device.get_device_state().device_properties);
|
|
}
|
|
} else {
|
|
println!("{}", device.get_device_state().device_properties);
|
|
}
|
|
}
|