feat(cli): add the binary that wires capture, slot and client together
Sets up logging, starts the network thread, runs the PipeWire loop in the main thread and shuts down in order so HyperHDR gets a Clear before exit.
This commit is contained in:
+107
@@ -0,0 +1,107 @@
|
|||||||
|
use std::io::IsTerminal;
|
||||||
|
use std::process::ExitCode;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use std::thread;
|
||||||
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
|
use anyhow::Context as _;
|
||||||
|
use clap::Parser;
|
||||||
|
use tracing::{error, info, warn};
|
||||||
|
use tracing_subscriber::EnvFilter;
|
||||||
|
|
||||||
|
use gamescope_hyperhdr::capture::{self, CaptureConfig};
|
||||||
|
use gamescope_hyperhdr::config::Config;
|
||||||
|
use gamescope_hyperhdr::hyperhdr::{client, ClientConfig};
|
||||||
|
use gamescope_hyperhdr::slot::FrameSlot;
|
||||||
|
use gamescope_hyperhdr::stats::Stats;
|
||||||
|
|
||||||
|
/// How long the network thread gets to send `Clear` and close on shutdown.
|
||||||
|
const NETWORK_SHUTDOWN_TIMEOUT: Duration = Duration::from_secs(1);
|
||||||
|
|
||||||
|
fn init_logging(level: &str) {
|
||||||
|
let filter = EnvFilter::try_new(level).unwrap_or_else(|e| {
|
||||||
|
eprintln!("invalid log level {level:?} ({e}), using info");
|
||||||
|
EnvFilter::new("info")
|
||||||
|
});
|
||||||
|
// Under systemd, journald adds its own timestamps.
|
||||||
|
let journal = std::env::var_os("JOURNAL_STREAM").is_some();
|
||||||
|
let builder = tracing_subscriber::fmt()
|
||||||
|
.with_writer(std::io::stderr)
|
||||||
|
.with_env_filter(filter)
|
||||||
|
.with_target(false)
|
||||||
|
.with_ansi(!journal && std::io::stderr().is_terminal());
|
||||||
|
if journal {
|
||||||
|
builder.without_time().init();
|
||||||
|
} else {
|
||||||
|
builder.init();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() -> ExitCode {
|
||||||
|
let cfg = Config::parse();
|
||||||
|
init_logging(&cfg.log_level);
|
||||||
|
|
||||||
|
match cfg.validate() {
|
||||||
|
Ok(warnings) => warnings.iter().for_each(|w| warn!("{w}")),
|
||||||
|
Err(e) => {
|
||||||
|
error!("invalid configuration: {e}");
|
||||||
|
return ExitCode::from(2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
info!(
|
||||||
|
host = %cfg.host,
|
||||||
|
port = cfg.port,
|
||||||
|
priority = cfg.priority,
|
||||||
|
origin = %cfg.origin,
|
||||||
|
size = %cfg.size,
|
||||||
|
supersample = cfg.supersample,
|
||||||
|
requested_size = %cfg.requested_size(),
|
||||||
|
max_fps = cfg.max_fps,
|
||||||
|
keepalive_ms = cfg.keepalive_ms,
|
||||||
|
idle_clear_secs = cfg.idle_clear_secs,
|
||||||
|
target = %cfg.target,
|
||||||
|
connect_timeout_ms = cfg.connect_timeout_ms,
|
||||||
|
write_timeout_ms = cfg.write_timeout_ms,
|
||||||
|
"gamescope-hyperhdr {} starting",
|
||||||
|
env!("CARGO_PKG_VERSION")
|
||||||
|
);
|
||||||
|
|
||||||
|
let slot = Arc::new(FrameSlot::new());
|
||||||
|
let stats = Arc::new(Stats::default());
|
||||||
|
let capture_cfg = CaptureConfig {
|
||||||
|
target: cfg.target.clone(),
|
||||||
|
requested: cfg.requested_size(),
|
||||||
|
output_max: cfg.size,
|
||||||
|
};
|
||||||
|
let mut network = None;
|
||||||
|
let result = capture::run(capture_cfg, slot.clone(), stats.clone(), || {
|
||||||
|
let handle = client::spawn(ClientConfig::from_config(&cfg), slot.clone(), stats)
|
||||||
|
.context("cannot start the network thread")?;
|
||||||
|
network = Some(handle);
|
||||||
|
Ok(())
|
||||||
|
});
|
||||||
|
|
||||||
|
slot.shutdown();
|
||||||
|
if let Some(network) = network {
|
||||||
|
let deadline = Instant::now() + NETWORK_SHUTDOWN_TIMEOUT;
|
||||||
|
while !network.is_finished() && Instant::now() < deadline {
|
||||||
|
thread::sleep(Duration::from_millis(5));
|
||||||
|
}
|
||||||
|
if network.is_finished() {
|
||||||
|
let _ = network.join();
|
||||||
|
} else {
|
||||||
|
warn!("network thread did not finish within {NETWORK_SHUTDOWN_TIMEOUT:?}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match result {
|
||||||
|
Ok(()) => {
|
||||||
|
info!("exiting");
|
||||||
|
ExitCode::SUCCESS
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
error!("{e:#}");
|
||||||
|
ExitCode::FAILURE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user