From 04b76ebfc7af564acea3bb8f4b243110ccf3a8bf Mon Sep 17 00:00:00 2001 From: enricobuehler Date: Mon, 15 Jun 2026 01:15:51 +0000 Subject: [PATCH] feat(host/windows): run serve/m3-host on Windows (config paths + compositor) The punktfunk/1 control plane already compiled on Windows; these wire the last gaps so the host actually runs: config_dir falls back to %APPDATA% (HOME\.config when set), paired_path uses it, hostname from COMPUTERNAME, and resolve_compositor short-circuits the Linux session-detection on Windows (SudoVDA is the single backend; vdisplay::open ignores the compositor arg). Validated live on the VM: m3-host creates its identity, binds the QUIC endpoint (fingerprint logged), advertises mDNS (_punktfunk._udp, host from COMPUTERNAME), and accepts sessions. GPU-less validations green: m0 synthetic->openh264->core FEC loopback (120/120, 0 mismatches) and the m3 c_abi_connection_roundtrip control-plane test. Full session capture (SudoVDA->DXGI) + NVENC remain GPU-gated. Co-Authored-By: Claude Opus 4.8 (1M context) --- crates/punktfunk-host/src/gamestream/mod.rs | 12 +++++++++++- crates/punktfunk-host/src/m3.rs | 10 ++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/crates/punktfunk-host/src/gamestream/mod.rs b/crates/punktfunk-host/src/gamestream/mod.rs index be351c7..ea4023a 100644 --- a/crates/punktfunk-host/src/gamestream/mod.rs +++ b/crates/punktfunk-host/src/gamestream/mod.rs @@ -206,11 +206,20 @@ fn config_dir() -> PathBuf { let base = std::env::var_os("XDG_CONFIG_HOME") .map(PathBuf::from) .or_else(|| std::env::var_os("HOME").map(|h| PathBuf::from(h).join(".config"))) + // Windows: %APPDATA% (e.g. C:\Users\X\AppData\Roaming) — cert/key/paired/uniqueid persist there. + .or_else(|| std::env::var_os("APPDATA").map(PathBuf::from)) .unwrap_or_else(|| PathBuf::from(".")); base.join("punktfunk") } fn hostname_string() -> String { + #[cfg(target_os = "windows")] + if let Some(n) = std::env::var_os("COMPUTERNAME") { + let s = n.to_string_lossy().trim().to_string(); + if !s.is_empty() { + return s; + } + } std::fs::read_to_string("/proc/sys/kernel/hostname") .ok() .map(|s| s.trim().to_string()) @@ -245,7 +254,8 @@ fn primary_local_ip() -> Option { /// Where the paired-client allow-list persists (survives host restarts, like Sunshine). fn paired_path() -> Option { - Some(std::path::Path::new(&std::env::var("HOME").ok()?).join(".config/punktfunk/paired.json")) + // Same dir as the host identity (HOME/.config/punktfunk on Linux, %APPDATA%\punktfunk on Windows). + Some(config_dir().join("paired.json")) } /// Load the persisted paired-client certificate DERs (empty on first run / parse failure). diff --git a/crates/punktfunk-host/src/m3.rs b/crates/punktfunk-host/src/m3.rs index bb128bc..c251b50 100644 --- a/crates/punktfunk-host/src/m3.rs +++ b/crates/punktfunk-host/src/m3.rs @@ -1519,6 +1519,15 @@ fn pick_compositor( /// async reactor (`spawn_blocking`). fn resolve_compositor(pref: CompositorPref) -> Result { use crate::vdisplay::Compositor; + // Windows has a single virtual-display backend (SudoVDA); vdisplay::open ignores the compositor + // arg there, so short-circuit the Linux session-detection state machine with a placeholder. + #[cfg(target_os = "windows")] + { + let _ = pref; + return Ok(Compositor::Kwin); + } + #[cfg(not(target_os = "windows"))] + { // Explicit operator override (legacy / CI / forcing a backend for a test) wins and is assumed // to come with a hand-set env — don't retarget the process env in that case. let overridden = std::env::var_os("PUNKTFUNK_COMPOSITOR").is_some(); @@ -1565,6 +1574,7 @@ fn resolve_compositor(pref: CompositorPref) -> Result