chore: consolidate parallel-session WIP (HOLD — do not push)

Local snapshot of intermingled in-flight work, committed to unblock the encode
refactor (a clean ffmpeg_win.rs for the vbv-dedup follow-on). These hunks span
the same files and can't be cleanly split here; the commit bundles three
distinct workstreams that each belong in their own PR:

  - logging rework (~43 files: level re-tiering, structured fields, `?e`,
    hot-path flood latches)
  - conflicting-host detection (detect.rs + detect/{linux,windows}.rs + wiring
    in main.rs/mgmt.rs/Cargo.toml/docs/packaging)
  - standby-sink DWM-stall attribution (windows/display_events.rs + capture/
    vdisplay wiring)

NOT verified as a combination. NOT to be pushed until the refactor is done and
these are re-verified and reorganized into their proper per-workstream PRs.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-16 12:42:53 +02:00
parent d466e3e2b2
commit 11045a0f70
62 changed files with 1595 additions and 214 deletions
+49 -16
View File
@@ -37,6 +37,11 @@ pub struct Summary {
/// host that doesn't send it deserializes as 0.
#[serde(default)]
pub kept_displays: u32,
/// Other Moonlight-compatible hosts (Sunshine/Apollo/…) the host detected on this machine at
/// startup — side-by-side use is unsupported. `#[serde(default)]` so an older host omitting it
/// deserializes as empty.
#[serde(default)]
pub conflicts: Vec<String>,
}
#[derive(Clone, Copy, Debug, PartialEq, serde::Deserialize)]
@@ -69,25 +74,41 @@ impl TrayStatus {
TrayStatus::Starting => "punktfunk host — starting…".into(),
TrayStatus::Degraded => "punktfunk host — running (status unavailable)".into(),
TrayStatus::Error(e) => format!("punktfunk host — failed ({e})"),
TrayStatus::Running(s) => match (&s.session, s.video_streaming) {
(Some(sess), true) => format!(
"punktfunk host {} — streaming {}×{}@{}",
s.version, sess.width, sess.height, sess.fps
),
(_, true) => format!("punktfunk host {} — streaming", s.version),
// Idle, but surface a kept (lingering/pinned) display: it — and, under an exclusive
// topology, your physical monitors — is being held. Release it from the console.
_ if s.kept_displays > 0 => format!(
"punktfunk host {} — idle · {} display{} kept",
s.version,
s.kept_displays,
if s.kept_displays == 1 { "" } else { "s" }
),
_ => format!("punktfunk host {} — idle", s.version),
},
TrayStatus::Running(s) => {
let base = match (&s.session, s.video_streaming) {
(Some(sess), true) => format!(
"punktfunk host {} — streaming {}×{}@{}",
s.version, sess.width, sess.height, sess.fps
),
(_, true) => format!("punktfunk host {} — streaming", s.version),
// Idle, but surface a kept (lingering/pinned) display: it — and, under an
// exclusive topology, your physical monitors — is being held. Release it from
// the console.
_ if s.kept_displays > 0 => format!(
"punktfunk host {} — idle · {} display{} kept",
s.version,
s.kept_displays,
if s.kept_displays == 1 { "" } else { "s" }
),
_ => format!("punktfunk host {} — idle", s.version),
};
// A conflicting Moonlight host (Sunshine/Apollo/…) is the loudest thing to say —
// side-by-side use is unsupported, so lead the tooltip with it.
if s.conflicts.is_empty() {
base
} else {
format!("⚠ conflicting host: {}{base}", s.conflicts.join(", "))
}
}
}
}
/// The host detected another Moonlight-compatible host (Sunshine/Apollo/…) on this machine —
/// unsupported side-by-side. Drives the tray's attention state.
pub fn has_conflicts(&self) -> bool {
matches!(self, TrayStatus::Running(s) if !s.conflicts.is_empty())
}
pub fn is_streaming(&self) -> bool {
matches!(self, TrayStatus::Running(s) if s.video_streaming)
}
@@ -445,6 +466,7 @@ mod tests {
pin_pending: false,
pending_approvals: 0,
kept_displays: 0,
conflicts: Vec::new(),
}
}
@@ -487,6 +509,17 @@ mod tests {
}
}
#[test]
fn conflicts_drive_attention_and_lead_the_tooltip() {
let mut s = summary(false);
assert!(!TrayStatus::Running(s.clone()).has_conflicts());
s.conflicts = vec!["Sunshine (running)".into(), "Apollo".into()];
let st = TrayStatus::Running(s);
assert!(st.has_conflicts());
let head = st.headline();
assert!(head.starts_with("⚠ conflicting host: Sunshine (running), Apollo"));
}
#[test]
fn headline_shows_session_and_reason() {
assert_eq!(