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:
@@ -51,7 +51,7 @@ impl ksni::Tray for HostTray {
|
||||
fn status(&self) -> ksni::Status {
|
||||
match &self.status {
|
||||
TrayStatus::Error(_) => ksni::Status::NeedsAttention,
|
||||
s if s.pairing_attention() => ksni::Status::NeedsAttention,
|
||||
s if s.pairing_attention() || s.has_conflicts() => ksni::Status::NeedsAttention,
|
||||
_ => ksni::Status::Active,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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!(
|
||||
|
||||
Reference in New Issue
Block a user