mod cli; mod common; #[cfg(feature = "gui")] mod gui; mod host; mod interactive; mod repair; mod viewer; use anyhow::Result; use clap::Parser; use cli::Cli; use iroh_tickets::endpoint::EndpointTicket; use tracing_subscriber::EnvFilter; #[tokio::main] async fn main() -> Result<()> { let cli = Cli::parse(); init_tracing(cli.verbose); if matches!(cli.output, Some(cli::OutputFormat::Json)) { common::output::set_json(true); } if cli.gui { #[cfg(feature = "gui")] { return gui::run(cli.relay); } #[cfg(not(feature = "gui"))] { anyhow::bail!( "this binary was built without GUI support. Rebuild with \ `cargo build --release --features gui` to use --gui." ); } } // libpipewire requires global init before any pw_* call. Idempotent; // safe to call even when the per-app audio thread never spawns. pipewire::init(); if cli.repair { return repair::run().await; } if cli.reconfigure { return interactive::run_reconfigure().await; } if cli.host { if cli.ticket.is_some() { anyhow::bail!( "--host and a ticket argument are mutually exclusive: --host shares your \ screen, a ticket views someone else's." ); } return host::run(cli.into_host_opts(false)).await; } match cli.ticket.as_deref() { Some(s) => { let ticket: EndpointTicket = s.parse().map_err(|e| { anyhow::anyhow!( "argument doesn't look like a pixelpass ticket ({e}).\n\ Run with no arguments for the interactive menu, or pass a ticket to view." ) })?; viewer::run(ticket, cli.into_viewer_opts(false)).await } None => interactive::run(cli).await, } } fn init_tracing(verbose: bool) { let default = if verbose { "pixelpass=trace,iroh=info" } else { "pixelpass=info,iroh=warn" }; let filter = EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new(default)); // Tracing MUST write to stderr. `tracing_subscriber::fmt()` defaults its // writer to stdout, but with `--output json` stdout carries the JSON event // stream the `--gui` front-end parses (see `common::output`) — logging there // interleaves log lines into that stream (corrupting events and starving the // GUI's stderr-tail diagnostics). Pin it to stderr to honor that contract. tracing_subscriber::fmt() .with_writer(std::io::stderr) .with_env_filter(filter) .with_target(false) .init(); }