feat(kiosk): graphics tiers + Settings knob; network map kiosk mode
Demo images / Build & push demo images (push) Successful in 3m51s

The animated federation map froze the framework-pt 4K TV: the launcher
held every machine to the HD 5500-era choppy-audio flags (single raster
thread, GpuRasterization banned) while the map wrote SVG attrs at 60fps.

- Launcher: two flag tiers. legacy = the proven conservative set; modern
  (Intel gen8+, 'NNth Gen' models, AMD Ryzen) = default raster threads +
  GPU rasterization. Classified from /proc/cpuinfo (11 model strings
  covered by tests in-session); KIOSK_GRAPHICS=performance|quality in
  kiosk-display.conf overrides; headless unchanged. Reaches deployed
  kiosks via the include_str! self-heal, same as the vsync fix.
- system.kiosk-display.get/set: carries a 'graphics' field alongside
  'preset'; setting one no longer clobbers the other.
- Settings → Display: Graphics picker (Auto / Compatibility / Quality).
- NetworkMap3D: kiosks default to the 2D projection (remembered toggle
  still works) and tick at half rate with carried-over deltas — same
  spin speed, half the paint cost.
- Changelog: curated Unreleased notes for all of the above + the gate
  frame-embedding fix.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
archipelago
2026-08-13 09:23:07 -04:00
co-authored by Claude Fable 5
parent 6672d978f7
commit 9243babcdb
5 changed files with 203 additions and 30 deletions
+52 -13
View File
@@ -1268,7 +1268,14 @@ impl RpcHandler {
} else {
"auto"
};
Ok(serde_json::json!({ "has_kiosk": has_kiosk, "preset": preset }))
let graphics = if conf.contains("KIOSK_GRAPHICS=performance") {
"performance"
} else if conf.contains("KIOSK_GRAPHICS=quality") {
"quality"
} else {
"auto"
};
Ok(serde_json::json!({ "has_kiosk": has_kiosk, "preset": preset, "graphics": graphics }))
}
/// system.kiosk-display.set — Write the kiosk display preset and restart
@@ -1279,24 +1286,56 @@ impl RpcHandler {
params: Option<serde_json::Value>,
) -> Result<serde_json::Value> {
let params = params.ok_or_else(|| anyhow::anyhow!("Missing params"))?;
let preset = params
.get("preset")
.and_then(|v| v.as_str())
.ok_or_else(|| anyhow::anyhow!("Missing preset"))?;
let preset = params.get("preset").and_then(|v| v.as_str());
let graphics = params.get("graphics").and_then(|v| v.as_str());
if preset.is_none() && graphics.is_none() {
anyhow::bail!("Missing preset or graphics");
}
let conf = match preset {
// The conf carries two independent settings (display scale preset +
// graphics tier). A set of one must not clobber the other, so the
// half not being changed is carried over from the file as-is.
let existing = tokio::fs::read_to_string(KIOSK_DISPLAY_CONF)
.await
.unwrap_or_default();
let display_part = match preset {
// Resolution-derived default: 4K -> 2.0 (1920-wide layout),
// 1080p TV -> 1.5, laptop panels -> 1.0.
"auto" => String::new(),
Some("auto") => String::new(),
// Biggest UI: every panel targets a 1280-wide layout.
"large" => "ARCHIPELAGO_KIOSK_TARGET_CSS_WIDTH=1280\n".to_string(),
Some("large") => "ARCHIPELAGO_KIOSK_TARGET_CSS_WIDTH=1280\n".to_string(),
// Full-HD layout on any panel that can carry it.
"balanced" => "ARCHIPELAGO_KIOSK_TARGET_CSS_WIDTH=1920\n".to_string(),
Some("balanced") => "ARCHIPELAGO_KIOSK_TARGET_CSS_WIDTH=1920\n".to_string(),
// No scaling: native CSS viewport, most content, smallest UI.
"native" => "ARCHIPELAGO_KIOSK_SCALE=1\n".to_string(),
other => anyhow::bail!("Unknown display preset: {other}"),
Some("native") => "ARCHIPELAGO_KIOSK_SCALE=1\n".to_string(),
Some(other) => anyhow::bail!("Unknown display preset: {other}"),
None => existing
.lines()
.filter(|l| l.starts_with("ARCHIPELAGO_KIOSK_"))
.map(|l| format!("{l}\n"))
.collect(),
};
let graphics_part = match graphics {
// Auto: the launcher classifies the hardware itself (CPU/iGPU
// generation) — legacy boxes keep the choppy-audio-safe flags,
// modern iGPUs get GPU rasterization.
Some("auto") => String::new(),
// Force the conservative legacy flag set (troubleshooting).
Some("performance") => "KIOSK_GRAPHICS=performance\n".to_string(),
// Force the modern flag set even on unclassified hardware.
Some("quality") => "KIOSK_GRAPHICS=quality\n".to_string(),
Some(other) => anyhow::bail!("Unknown graphics mode: {other}"),
None => existing
.lines()
.find(|l| l.starts_with("KIOSK_GRAPHICS="))
.map(|l| format!("{l}\n"))
.unwrap_or_default(),
};
let conf = format!("{display_part}{graphics_part}");
host_sudo(&["/usr/bin/mkdir", "-p", "/etc/archipelago"]).await?;
if conf.is_empty() {
let _ = host_sudo(&["/usr/bin/rm", "-f", KIOSK_DISPLAY_CONF]).await;
@@ -1332,8 +1371,8 @@ impl RpcHandler {
])
.await;
info!(preset, "Kiosk display preset applied");
Ok(serde_json::json!({ "preset": preset, "applied": true }))
info!(?preset, ?graphics, "Kiosk display settings applied");
Ok(serde_json::json!({ "preset": preset, "graphics": graphics, "applied": true }))
}
}