refactor(host/W6.1): extract the config() global into the pf-host-config leaf crate
Third de-coupling for the host crate carve (plan §W6.1 leaf). HostConfig + the config() OnceLock (config.rs, pure std, zero deps) move to a new pf-host-config leaf so every subsystem crate (pf-encode/pf-capture/pf-vdisplay/pf-gpu) can read process config WITHOUT depending on the orchestrator. 34 crate::config::config() call sites across 19 files repoint to pf_host_config::config(). thread_qos stays in the host for now (it calls session_tuning::on_hot_thread — its own leaf-ification rides the encode carve). Granular-crate decision (supersedes the plan's single pf-media): split capture/encode/ vdisplay into separate crates rather than one broad crate — the capture↔encode cycle is broken by a shared frame-types leaf, and vdisplay→encode (can_open_another_session) is a legal one-way edge since encode never references vdisplay. Verified: Linux (home-worker-5) clippy -p pf-host-config -p punktfunk-host --all-targets -D warnings; Windows (192.168.1.158) clippy --features nvenc,amf-qsv --all-targets green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Generated
+5
@@ -2808,6 +2808,10 @@ dependencies = [
|
||||
"pkg-config",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pf-host-config"
|
||||
version = "0.12.0"
|
||||
|
||||
[[package]]
|
||||
name = "pf-paths"
|
||||
version = "0.12.0"
|
||||
@@ -3129,6 +3133,7 @@ dependencies = [
|
||||
"opus",
|
||||
"parking_lot",
|
||||
"pf-driver-proto",
|
||||
"pf-host-config",
|
||||
"pf-paths",
|
||||
"pipewire",
|
||||
"punktfunk-core",
|
||||
|
||||
@@ -11,6 +11,7 @@ members = [
|
||||
"crates/pf-ffvk",
|
||||
"crates/pf-driver-proto",
|
||||
"crates/pf-paths",
|
||||
"crates/pf-host-config",
|
||||
"crates/pyrowave-sys",
|
||||
"clients/probe",
|
||||
"clients/linux",
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
# The process-wide host configuration global (HostConfig + the config() OnceLock), extracted into a
|
||||
# leaf crate so every subsystem crate (pf-encode, pf-capture, pf-vdisplay, pf-gpu) can read config
|
||||
# WITHOUT depending on the orchestrator (plan §W6 — config parked above its consumers). Pure std.
|
||||
[package]
|
||||
name = "pf-host-config"
|
||||
version = "0.12.0"
|
||||
edition = "2021"
|
||||
license = "MIT OR Apache-2.0"
|
||||
description = "Process-wide punktfunk host configuration (env-parsed HostConfig behind a OnceLock)."
|
||||
publish = false
|
||||
|
||||
[dependencies]
|
||||
@@ -12,7 +12,7 @@
|
||||
//! capture/topology/encoder decision.
|
||||
//!
|
||||
//! **What is deliberately NOT here (and must stay a live `env::var` read):**
|
||||
//! - **Runtime-mutated session vars.** On Linux, [`crate::vdisplay::apply_session_env`] rewrites the process
|
||||
//! - **Runtime-mutated session vars.** On Linux, `crate::vdisplay::apply_session_env` rewrites the process
|
||||
//! env on *every connect* so one host follows a Bazzite box across Gaming↔Desktop: `WAYLAND_DISPLAY`,
|
||||
//! `XDG_CURRENT_DESKTOP`, `XDG_RUNTIME_DIR`, `DBUS_SESSION_BUS_ADDRESS`, and the *derived* `PUNKTFUNK_*`
|
||||
//! vars `INPUT_BACKEND`, `GAMESCOPE_SESSION`/`GAMESCOPE_NODE`, `KWIN_VIRTUAL_PRIMARY`,
|
||||
@@ -12,6 +12,8 @@ repository.workspace = true
|
||||
punktfunk-core = { path = "../punktfunk-core", features = ["quic"] }
|
||||
# Config-dir + owner-private file helpers (moved out of the gamestream junk drawer, plan §W6).
|
||||
pf-paths = { path = "../pf-paths" }
|
||||
# Process-wide host config global, extracted to a leaf crate (plan §W6).
|
||||
pf-host-config = { path = "../pf-host-config" }
|
||||
# M3 native control plane (the `punktfunk/1` QUIC handshake; data plane stays native-thread UDP).
|
||||
quinn = "0.11"
|
||||
anyhow = "1"
|
||||
|
||||
@@ -1626,7 +1626,7 @@ mod pipewire {
|
||||
// advertisement with every modifier its device samples from, so compositors that
|
||||
// never allocate LINEAR (Mutter+NVIDIA) still negotiate zero-copy dmabufs.
|
||||
#[cfg(feature = "pyrowave")]
|
||||
if vaapi_passthrough && crate::config::config().encoder_pref.as_str() == "pyrowave" {
|
||||
if vaapi_passthrough && pf_host_config::config().encoder_pref.as_str() == "pyrowave" {
|
||||
for m in crate::encode::pyrowave_capture_modifiers(
|
||||
crate::zerocopy::drm_fourcc(PixelFormat::Bgrx).unwrap(),
|
||||
) {
|
||||
|
||||
@@ -1522,7 +1522,7 @@ impl Capturer for IddPushCapturer {
|
||||
// NVENC encodes N on the ASIC. We hand a rotating `OUT_RING` of output textures, so this is safe.
|
||||
// `PUNKTFUNK_IDD_DEPTH` overrides (1 disables pipelining; clamp to ≤ OUT_RING so a frame in flight
|
||||
// always has its own texture).
|
||||
crate::config::config().idd_depth.clamp(1, OUT_RING)
|
||||
pf_host_config::config().idd_depth.clamp(1, OUT_RING)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ impl Codec {
|
||||
// keeps the bit off (no Vulkan device to open).
|
||||
#[cfg(all(target_os = "linux", feature = "pyrowave"))]
|
||||
let pyro = if !matches!(
|
||||
crate::config::config().encoder_pref.as_str(),
|
||||
pf_host_config::config().encoder_pref.as_str(),
|
||||
"software" | "sw" | "openh264"
|
||||
) {
|
||||
punktfunk_core::quic::CODEC_PYROWAVE
|
||||
@@ -53,7 +53,7 @@ impl Codec {
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
if matches!(
|
||||
crate::config::config().encoder_pref.as_str(),
|
||||
pf_host_config::config().encoder_pref.as_str(),
|
||||
"software" | "sw" | "openh264"
|
||||
) {
|
||||
return punktfunk_core::quic::CODEC_H264;
|
||||
@@ -83,7 +83,7 @@ impl Codec {
|
||||
#[cfg(not(any(target_os = "linux", target_os = "windows")))]
|
||||
{
|
||||
let _ = GPU_SUPERSET;
|
||||
match crate::config::config().encoder_pref.as_str() {
|
||||
match pf_host_config::config().encoder_pref.as_str() {
|
||||
"software" | "sw" | "openh264" => punktfunk_core::quic::CODEC_H264,
|
||||
_ => punktfunk_core::quic::CODEC_HEVC,
|
||||
}
|
||||
@@ -262,7 +262,7 @@ fn open_video_backend(
|
||||
// AMD/Intel → VAAPI (one libavcodec backend for both). Auto-detect by default so a single
|
||||
// Linux binary serves any GPU; `PUNKTFUNK_ENCODER` forces a specific backend (and surfaces
|
||||
// its errors crisply instead of silently trying the other).
|
||||
let pref = crate::config::config().encoder_pref.as_str();
|
||||
let pref = pf_host_config::config().encoder_pref.as_str();
|
||||
// AMD/Intel opener. Default = libav VAAPI. With `--features vulkan-encode` +
|
||||
// PUNKTFUNK_VULKAN_ENCODE, an HEVC session instead opens the raw Vulkan Video backend (real
|
||||
// RFI loss recovery the VAAPI path can't express); a failed open falls back to VAAPI so the
|
||||
@@ -708,7 +708,7 @@ pub(crate) fn pyrowave_capture_modifiers(fourcc: u32) -> Vec<u64> {
|
||||
/// passthrough for VAAPI vs the EGL→CUDA import for NVENC).
|
||||
#[cfg(target_os = "linux")]
|
||||
pub fn linux_zero_copy_is_vaapi() -> bool {
|
||||
match crate::config::config().encoder_pref.as_str() {
|
||||
match pf_host_config::config().encoder_pref.as_str() {
|
||||
"nvenc" | "nvidia" | "cuda" => false,
|
||||
"vaapi" | "amd" | "intel" => true,
|
||||
// PyroWave ingests the raw capture dmabuf itself (Vulkan import + compute CSC) on ANY
|
||||
@@ -945,7 +945,7 @@ enum GpuVendor {
|
||||
#[cfg(target_os = "windows")]
|
||||
pub(crate) fn windows_resolved_backend() -> WindowsBackend {
|
||||
// Resolved ONCE in HostConfig (Goal-1) — was re-read from PUNKTFUNK_ENCODER on every call.
|
||||
match crate::config::config().encoder_pref.as_str() {
|
||||
match pf_host_config::config().encoder_pref.as_str() {
|
||||
"nvenc" | "hw" | "nvidia" | "cuda" => WindowsBackend::Nvenc,
|
||||
"amf" | "amd" => WindowsBackend::Amf,
|
||||
"qsv" | "intel" => WindowsBackend::Qsv,
|
||||
@@ -974,7 +974,7 @@ pub(crate) fn resolved_backend_is_gpu() -> bool {
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
pub(crate) fn resolved_backend_is_gpu() -> bool {
|
||||
!matches!(
|
||||
crate::config::config().encoder_pref.as_str(),
|
||||
pf_host_config::config().encoder_pref.as_str(),
|
||||
"software" | "sw" | "openh264"
|
||||
)
|
||||
}
|
||||
|
||||
@@ -809,7 +809,7 @@ impl DmabufInner {
|
||||
// Sampled breakdown of this synchronous submit under PUNKTFUNK_PERF: push = descriptor
|
||||
// build + buffersrc (the per-frame DRM→VA import happens inside hwmap on the pull path),
|
||||
// pull = buffersink (VPP CSC + any sync), send = avcodec_send_frame. One line per ~2 s.
|
||||
let sample = crate::config::config().perf && self.frames % 120 == 0;
|
||||
let sample = pf_host_config::config().perf && self.frames % 120 == 0;
|
||||
self.frames += 1;
|
||||
let t0 = std::time::Instant::now();
|
||||
let t_push: std::time::Duration;
|
||||
|
||||
@@ -122,7 +122,7 @@ impl WinVendor {
|
||||
/// open-failure fallback only catches *setup* errors; a derive that opens but maps wrong would
|
||||
/// corrupt silently, so it stays opt-in per the probe-never-assume rule).
|
||||
fn zerocopy_enabled(vendor: WinVendor) -> bool {
|
||||
crate::config::config()
|
||||
pf_host_config::config()
|
||||
.zerocopy
|
||||
.unwrap_or(matches!(vendor, WinVendor::Amf))
|
||||
}
|
||||
|
||||
@@ -64,7 +64,7 @@ pub const SERVER_CODEC_MODE_SUPPORT: u32 = SCM_H264 | SCM_HEVC | SCM_AV1_MAIN8;
|
||||
/// whenever the desktop is HDR, and a client HDR request makes the GameStream video path proactively
|
||||
/// enable advanced color on the per-session virtual display so PQ flows even from an SDR desktop.
|
||||
pub fn host_hdr_capable() -> bool {
|
||||
cfg!(target_os = "windows") && crate::config::config().ten_bit
|
||||
cfg!(target_os = "windows") && pf_host_config::config().ten_bit
|
||||
}
|
||||
|
||||
/// Stable host identity + advertised capabilities, shared across control-plane handlers.
|
||||
|
||||
@@ -162,7 +162,7 @@ fn run(
|
||||
// request and capture it (no scaling). Self-contained — deliberately NOT pooled in
|
||||
// `video_cap`, since a reconnect at a different resolution needs a freshly-sized output; the
|
||||
// output is released when this capturer drops at stream end (RAII via its keepalive).
|
||||
if crate::config::config().video_source.as_deref() == Some("virtual") {
|
||||
if pf_host_config::config().video_source.as_deref() == Some("virtual") {
|
||||
// Per-app prep steps (RFC §6): the entry's own `prep` plus a custom library title's,
|
||||
// run synchronously BEFORE the virtual output opens or anything launches (an HDR
|
||||
// toggle / sink switch must land first — and gamescope's nested launch happens inside
|
||||
@@ -249,7 +249,7 @@ fn run(
|
||||
tracing::info!("video source: reusing capturer");
|
||||
c
|
||||
}
|
||||
None if crate::config::config().video_source.as_deref() == Some("portal") => {
|
||||
None if pf_host_config::config().video_source.as_deref() == Some("portal") => {
|
||||
tracing::info!("video source: portal desktop capture");
|
||||
capture::open_portal_monitor().context("open portal capturer")?
|
||||
}
|
||||
@@ -676,7 +676,7 @@ fn stream_body(
|
||||
|
||||
// Per-stage timing (PUNKTFUNK_PERF=1): max µs/stage per second + unique vs re-encoded frames,
|
||||
// to pinpoint stalls. `unique` counts genuinely-new captured frames (vs re-encoded holds).
|
||||
let perf = crate::config::config().perf;
|
||||
let perf = pf_host_config::config().perf;
|
||||
let (mut mx_cap, mut mx_enc, mut mx_pkt, mut mx_send, mut uniq) =
|
||||
(0u128, 0u128, 0u128, 0u128, 0u32);
|
||||
// Web-console stats accumulation (active when `perf` OR a capture is armed): per-stage vectors
|
||||
|
||||
@@ -420,7 +420,7 @@ impl GpuPrefStore {
|
||||
}
|
||||
|
||||
/// The process-wide preference store (config-dir file), loaded once on first access — the same
|
||||
/// global-accessor shape as [`crate::config::config`], because selection happens deep inside
|
||||
/// global-accessor shape as [`pf_host_config::config`], because selection happens deep inside
|
||||
/// capture/encode setup where no app state is threaded.
|
||||
pub(crate) fn prefs() -> &'static GpuPrefStore {
|
||||
static STORE: OnceLock<GpuPrefStore> = OnceLock::new();
|
||||
@@ -535,7 +535,7 @@ pub(crate) fn pick(
|
||||
pub(crate) fn selected_gpu() -> Option<SelectedGpu> {
|
||||
let gpus = enumerate();
|
||||
let pref = prefs().get();
|
||||
let env = crate::config::config()
|
||||
let env = pf_host_config::config()
|
||||
.render_adapter
|
||||
.clone()
|
||||
.filter(|s| !s.is_empty());
|
||||
|
||||
@@ -298,8 +298,8 @@ fn dispatch(
|
||||
// The two env-var mirrors (`PUNKTFUNK_ON_CONNECT_CMD` / `PUNKTFUNK_ON_DISCONNECT_CMD`) —
|
||||
// the zero-config siblings of `PUNKTFUNK_RECOVER_SESSION_CMD` for the simplest cases.
|
||||
let mirror = match kind {
|
||||
"client.connected" => crate::config::config().on_connect_cmd.clone(),
|
||||
"client.disconnected" => crate::config::config().on_disconnect_cmd.clone(),
|
||||
"client.connected" => pf_host_config::config().on_connect_cmd.clone(),
|
||||
"client.disconnected" => pf_host_config::config().on_disconnect_cmd.clone(),
|
||||
_ => None,
|
||||
};
|
||||
if let Some(cmd) = mirror {
|
||||
|
||||
@@ -124,7 +124,7 @@ pub fn default_backend() -> Backend {
|
||||
}
|
||||
}
|
||||
// An explicit compositor pick (set per connect / mid-stream) is the strongest signal.
|
||||
let compositor = crate::config::config().compositor.clone();
|
||||
let compositor = pf_host_config::config().compositor.clone();
|
||||
if let Some(c) = compositor.as_deref() {
|
||||
let c = c.trim();
|
||||
if c.eq_ignore_ascii_case("gamescope") {
|
||||
@@ -176,7 +176,7 @@ pub(crate) use service::InjectorService;
|
||||
/// (`org.gnome.Mutter.RemoteDesktop`), the same direct API the Mutter video backend uses.
|
||||
#[cfg(target_os = "linux")]
|
||||
fn libei_ei_source() -> libei::EiSource {
|
||||
let gnome = crate::config::config()
|
||||
let gnome = pf_host_config::config()
|
||||
.compositor
|
||||
.as_deref()
|
||||
.is_some_and(|v| v.trim().eq_ignore_ascii_case("mutter"))
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
|
||||
mod audio;
|
||||
mod capture;
|
||||
mod config;
|
||||
mod detect;
|
||||
mod devtest;
|
||||
mod discovery;
|
||||
|
||||
@@ -139,7 +139,7 @@ pub(crate) fn gpu_state() -> GpuState {
|
||||
preferred_id,
|
||||
preferred_name,
|
||||
preferred_available,
|
||||
env_override: crate::config::config()
|
||||
env_override: pf_host_config::config()
|
||||
.render_adapter
|
||||
.clone()
|
||||
.filter(|s| !s.is_empty()),
|
||||
|
||||
@@ -57,7 +57,7 @@ pub(super) fn resolve_compositor(
|
||||
crate::vdisplay::cancel_pending_tv_restore();
|
||||
// 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 = crate::config::config().compositor.is_some();
|
||||
let overridden = pf_host_config::config().compositor.is_some();
|
||||
let detected = if overridden {
|
||||
crate::vdisplay::detect().ok()
|
||||
} else {
|
||||
|
||||
@@ -200,7 +200,7 @@ fn degrade_steam_on_conflict(chosen: GamepadPref) -> GamepadPref {
|
||||
/// Resolve the client's gamepad-backend preference (the env/logging shell around
|
||||
/// [`pick_gamepad`]). Always concrete — the `Welcome` reports what the session will drive.
|
||||
pub(super) fn resolve_gamepad(pref: GamepadPref) -> GamepadPref {
|
||||
let env = crate::config::config().gamepad.clone();
|
||||
let env = pf_host_config::config().gamepad.clone();
|
||||
let chosen = pick_gamepad(
|
||||
pref,
|
||||
env.as_deref(),
|
||||
|
||||
@@ -207,7 +207,7 @@ pub(super) async fn negotiate(
|
||||
// Welcome, exactly like the 4:4:4 gate below, so `color` reflects what we'll really emit —
|
||||
// the honest-downgrade channel: a GPU/backend that can't 10-bit yields 8-bit AND an SDR
|
||||
// label that matches the stream.
|
||||
let host_wants_10bit = crate::config::config().ten_bit;
|
||||
let host_wants_10bit = pf_host_config::config().ten_bit;
|
||||
let client_supports_10bit = hello.video_caps & punktfunk_core::quic::VIDEO_CAP_10BIT != 0;
|
||||
// The GPU probe may open a tiny encoder on first use, so run it off the reactor like the
|
||||
// 4:4:4 probe below (blocking probes → spawn_blocking), short-circuited behind the cheap
|
||||
@@ -239,7 +239,7 @@ pub(super) async fn negotiate(
|
||||
// what we'll really emit — the honest-downgrade channel: if any gate fails the client is
|
||||
// told 4:2:0 before it builds its decoder. The probe opens a tiny encoder; it runs only
|
||||
// when the earlier gates pass and is cached after the first.
|
||||
let host_wants_444 = crate::config::config().four_four_four;
|
||||
let host_wants_444 = pf_host_config::config().four_four_four;
|
||||
let client_supports_444 = hello.video_caps & punktfunk_core::quic::VIDEO_CAP_444 != 0;
|
||||
// The active capturer must be able to deliver a full-chroma (RGB) source — the honest-downgrade
|
||||
// gate. Linux's portal capturer can; the Windows IDD-push path delivers subsampled NV12/P010
|
||||
|
||||
@@ -873,7 +873,7 @@ pub(super) fn virtual_stream(ctx: SessionContext) -> Result<()> {
|
||||
#[cfg(not(any(target_os = "windows", target_os = "linux")))]
|
||||
let _ = &launch;
|
||||
|
||||
let perf = crate::config::config().perf;
|
||||
let perf = pf_host_config::config().perf;
|
||||
// Microburst cap (applied in send_loop/paced_submit): a frame ≤ the cap bursts out
|
||||
// immediately; only a bigger frame's overflow is spread. `None` = auto — max(128 KB, the
|
||||
// AU's wire bytes / 4), so the burst stays a bounded fraction of high-rate frames instead
|
||||
@@ -956,7 +956,7 @@ pub(super) fn virtual_stream(ctx: SessionContext) -> Result<()> {
|
||||
// place when the box flips Gaming↔Desktop. When not spawned, session_rx just stays empty.
|
||||
let mut compositor = compositor;
|
||||
let (session_tx, session_rx) = std::sync::mpsc::channel::<SessionSwitch>();
|
||||
let watch = session_watch_enabled() && crate::config::config().compositor.is_none();
|
||||
let watch = session_watch_enabled() && pf_host_config::config().compositor.is_none();
|
||||
let _watcher = if watch {
|
||||
tracing::info!("session watcher on — following a mid-stream Gaming↔Desktop switch");
|
||||
let stop = stop.clone();
|
||||
@@ -1461,7 +1461,7 @@ pub(super) fn virtual_stream(ctx: SessionContext) -> Result<()> {
|
||||
let (new_cap, new_enc, new_frame, new_interval, new_node_id, new_display_gen) = loop {
|
||||
// Follow the active session unless an explicit PUNKTFUNK_COMPOSITOR pin forbids
|
||||
// retargeting (then we stick to the pinned backend and just rebuild it).
|
||||
if crate::config::config().compositor.is_none() {
|
||||
if pf_host_config::config().compositor.is_none() {
|
||||
let active = crate::vdisplay::detect_active_session();
|
||||
// A4: fold any compositor-instance change into the epoch/invalidation before we
|
||||
// rebuild, so the rebuild's acquire won't reuse a dead-instance node.
|
||||
|
||||
@@ -205,7 +205,7 @@ fn resolve_encoder() -> EncoderBackend {
|
||||
// capture (`EncoderBackend::Software.is_gpu() == false` → `output_format().gpu = false`), so the
|
||||
// portal capturer delivers CPU RGB. Everything else stays `PlatformAuto` (NVENC/VAAPI resolved
|
||||
// inside `encode::open_video`).
|
||||
match crate::config::config().encoder_pref.as_str() {
|
||||
match pf_host_config::config().encoder_pref.as_str() {
|
||||
"software" | "sw" | "openh264" => EncoderBackend::Software,
|
||||
_ => EncoderBackend::PlatformAuto,
|
||||
}
|
||||
|
||||
@@ -186,7 +186,7 @@ pub fn with_env_lock<R>(f: impl FnOnce() -> R) -> R {
|
||||
/// a backend for a test), else the **live session** ([`detect_active_session`] — so a Bazzite box
|
||||
/// follows Gaming↔Desktop switches), else a last-resort `XDG_CURRENT_DESKTOP` read.
|
||||
pub fn detect() -> Result<Compositor> {
|
||||
if let Some(v) = crate::config::config().compositor.as_deref() {
|
||||
if let Some(v) = pf_host_config::config().compositor.as_deref() {
|
||||
return match v.trim().to_ascii_lowercase().as_str() {
|
||||
"kwin" | "kde" | "plasma" => Ok(Compositor::Kwin),
|
||||
// `hyprland` names the distinct backend (D1); `wlroots`/`sway`/`wlr` stay wlroots-proper.
|
||||
@@ -317,7 +317,7 @@ pub(crate) mod layout;
|
||||
pub fn resolve_topology(t: policy::Topology) -> policy::Topology {
|
||||
match t {
|
||||
policy::Topology::Auto => {
|
||||
if crate::config::config().compositor.is_some() {
|
||||
if pf_host_config::config().compositor.is_some() {
|
||||
policy::Topology::Extend
|
||||
} else {
|
||||
policy::Topology::Exclusive
|
||||
|
||||
@@ -1408,7 +1408,7 @@ fn spawn(w: u32, h: u32, hz: u32, cmd: Option<&str>, log: &std::path::Path) -> R
|
||||
let app = shape_dedicated_command(&app);
|
||||
let relay = ei_socket_file();
|
||||
let _ = std::fs::remove_file(&relay); // stale socket path from a previous session
|
||||
let steam_mode = crate::config::config().gamescope_steam;
|
||||
let steam_mode = pf_host_config::config().gamescope_steam;
|
||||
let mut cmd = Command::new("gamescope");
|
||||
add_bare_gamescope_args(&mut cmd, w, h, hz, steam_mode);
|
||||
cmd.args([
|
||||
|
||||
@@ -418,7 +418,7 @@ pub fn apply_session_env(_active: &ActiveSession) {}
|
||||
/// handshake error tell the client to simply retry.
|
||||
#[cfg(target_os = "linux")]
|
||||
pub fn try_recover_session() -> bool {
|
||||
let Some(cmd) = crate::config::config().recover_session_cmd.clone() else {
|
||||
let Some(cmd) = pf_host_config::config().recover_session_cmd.clone() else {
|
||||
return false;
|
||||
};
|
||||
static LAST_LAUNCH: std::sync::Mutex<Option<std::time::Instant>> = std::sync::Mutex::new(None);
|
||||
|
||||
Reference in New Issue
Block a user