fix(tray): drop the conflicting-host warning — the tray is the wrong place for it
ci / web (push) Successful in 59s
ci / docs-site (push) Successful in 1m6s
ci / bench (push) Successful in 7m0s
deb / build-publish (push) Failing after 7m35s
decky / build-publish (push) Successful in 22s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 10s
deb / build-publish-host (push) Failing after 8m6s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 10s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 9s
docker / build-push (ci, ci/rust-ci-noble.Dockerfile, punktfunk-rust-ci-noble) (push) Successful in 8s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 9s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 8s
android / android (push) Successful in 13m38s
ci / rust-arm64 (push) Successful in 12m28s
docker / deploy-docs (push) Successful in 24s
arch / build-publish (push) Successful in 14m10s
windows-host / package (push) Successful in 10m56s
deb / build-publish-client-arm64 (push) Successful in 8m36s
docker / build-push-arm64cross (push) Successful in 4m37s
apple / swift (push) Successful in 5m28s
ci / rust (push) Successful in 22m58s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Failing after 13m9s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 17m0s
apple / screenshots (push) Successful in 23m51s
ci / web (push) Successful in 59s
ci / docs-site (push) Successful in 1m6s
ci / bench (push) Successful in 7m0s
deb / build-publish (push) Failing after 7m35s
decky / build-publish (push) Successful in 22s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 10s
deb / build-publish-host (push) Failing after 8m6s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 10s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 9s
docker / build-push (ci, ci/rust-ci-noble.Dockerfile, punktfunk-rust-ci-noble) (push) Successful in 8s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 9s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 8s
android / android (push) Successful in 13m38s
ci / rust-arm64 (push) Successful in 12m28s
docker / deploy-docs (push) Successful in 24s
arch / build-publish (push) Successful in 14m10s
windows-host / package (push) Successful in 10m56s
deb / build-publish-client-arm64 (push) Successful in 8m36s
docker / build-push-arm64cross (push) Successful in 4m37s
apple / swift (push) Successful in 5m28s
ci / rust (push) Successful in 22m58s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Failing after 13m9s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 17m0s
apple / screenshots (push) Successful in 23m51s
The tray led its tooltip with "⚠ conflicting host: Sunshine" and (on Linux) went NeedsAttention for a Sunshine that was merely *installed* — never started, never listening, harmless. An always-on warning over a dormant package is a false alarm on every poll, and the tray is the wrong surface for a one-shot install-time observation anyway. Removes the `conflicts` field from the tray's `Summary`, the tooltip prefix, `has_conflicts()`, and the ksni attention branch that fed on it. The host still detects and reports conflicts where that belongs — the startup `punktfunk::detect` warning, the `detect-conflicts` subcommand, and the `/api/v1/local/summary` field, which the tray must (and does, per the new test) keep tolerating on the wire. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -53,7 +53,7 @@ impl ksni::Tray for HostTray {
|
||||
fn status(&self) -> ksni::Status {
|
||||
match &self.status {
|
||||
TrayStatus::Error(_) => ksni::Status::NeedsAttention,
|
||||
s if s.pairing_attention() || s.has_conflicts() => ksni::Status::NeedsAttention,
|
||||
s if s.pairing_attention() => ksni::Status::NeedsAttention,
|
||||
_ => ksni::Status::Active,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,11 +37,6 @@ 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)]
|
||||
@@ -74,45 +69,26 @@ 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) => {
|
||||
let base = match (&s.session, self.is_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(", "))
|
||||
}
|
||||
}
|
||||
TrayStatus::Running(s) => match (&s.session, self.is_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),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// The host detected another Moonlight-compatible host (Sunshine/Apollo/…) on this machine —
|
||||
/// unsupported side-by-side. Drives the Linux (ksni) backend's `NeedsAttention` state; the
|
||||
/// Windows backend surfaces the same conflict through the tooltip `headline()` instead (it has
|
||||
/// no distinct attention icon), so this accessor is unused there — allow it per-platform rather
|
||||
/// than gate the shared API out.
|
||||
#[cfg_attr(not(target_os = "linux"), allow(dead_code))]
|
||||
pub fn has_conflicts(&self) -> bool {
|
||||
matches!(self, TrayStatus::Running(s) if !s.conflicts.is_empty())
|
||||
}
|
||||
|
||||
/// A client is streaming: the host's flag, OR a live session in the summary.
|
||||
///
|
||||
/// The session is checked too because a host from before the `get_local_summary` fix raised
|
||||
@@ -433,7 +409,6 @@ mod tests {
|
||||
pin_pending: false,
|
||||
pending_approvals: 0,
|
||||
kept_displays: 0,
|
||||
conflicts: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -476,15 +451,20 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
/// Conflicting-host detection is deliberately NOT a tray concern: "installed" is not
|
||||
/// "running", and an always-on ⚠ over a merely-present Sunshine cried wolf. The host still
|
||||
/// detects and reports it (startup log, `detect-conflicts`, the mgmt summary) — the tray just
|
||||
/// ignores that field, which it must keep tolerating on the wire.
|
||||
#[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"));
|
||||
fn a_summary_carrying_conflicts_still_deserializes_and_is_ignored() {
|
||||
let json = r#"{"version":"0.5.1","video_streaming":false,"audio_streaming":false,
|
||||
"session":null,"paired_clients":1,"native_paired_clients":2,"pin_pending":false,
|
||||
"pending_approvals":0,"kept_displays":0,"conflicts":["Sunshine (installed)"]}"#;
|
||||
let s: Summary = serde_json::from_str(json).expect("unknown fields are ignored");
|
||||
assert_eq!(
|
||||
TrayStatus::Running(s).headline(),
|
||||
"punktfunk host 0.5.1 — idle"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user