Windows v0.10.0: Kiosk, Settings-Parität, Performance, Multi-Monitor, Wallpaper¶
Datum: 2026-07-16 · Status: ratifiziert (Brainstorming mit Johannes; Block 1
explizit freigegeben, Block 2 per autonomem Mandat entschieden)
Scope: native/windows/host/ + Settings-Seite (src/settings/) + gezielte
Engine-Patches (kiosk-Param, renderScale, perfAdapt-Stufe, Scanline-Drift,
Power-Bridge). macOS-Native-Code unberührt; Web-Demo (index.html) unberührt.
Release-Schnitt: EIN Release v0.10.0 mit allen Blöcken (Wunsch Johannes,
statt der ursprünglich in der Phase-2-Spec angedachten Zweiteilung).
Baut auf docs/specs/2026-07-15-windows-micro-host-design.md auf (§3/§4 dort
= Phase 2; diese Spec ersetzt deren Wallpaper-MVP-Zuschnitt „nur primärer
Monitor" durch konfigurierbare Monitor-Modi).
Kontext / Befunde (Exploration 2026-07-16)¶
- Menüleiste im .scr = die Live-Control-Bar der Engine
(
controller.tsbuildControlBar()):controller.open()blendet sie bei jedem Start 30 s ein („orientation",controlBarAutoHideSec=30), kleine Mausbewegungen unter der 10-px-Exit-Schwelle re-triggern sie. Kein Query-Param kann sie heute abschalten. Unbedienbar, weil der Host-Watchdog bei >10 px Mausbewegung oder Taste den Prozess beendet. - Settings-Lücke: macOS-Config-Fenster bietet ~30 Settings (5 Looks, 7 CRT-Slider, Szene/Farbe/Tempo, Stadt-Höhe, Fog, Wetter, Flug-Bank, Reactive World, Terminal-Layout, HUD/Radar/Boot/Tag-Nacht/Ton, Auto-Szenenwechsel). Windows: 3 Dropdowns + 9 Toggles. Engine-seitig ist fast alles vorhanden — es fehlt nur die Verdrahtung.
- Performance (Johannes: durchgehend zähe FPS): Engine rendert in voller
Geräteauflösung (
setPixelRatio(min(dpr,2)), kein renderScale im Web-Pfad), Bloom ≈13 Fullscreen-Draws + CRT-Pass 9 Taps/Pixel auf HalfFloat-Targets;perfAdaptim Screensaver-Entry hart deaktiviert;antialias:truemit Composer wirkungslos; Scanline-Drift mutiertbackgroundPositionYpro Frame (Fullscreen-Re-Raster); Host setzt keine GPU-Präferenz (Hybrid-GPU). - Multi-Monitor heute: ein Fenster + eine WebView2 pro Monitor, identische URL, unsynchronisiert (scene=random würfelt pro Monitor).
/wexistiert nicht; unbekannte Flags fallen still inRunSaver().- Drift-Bug:
app.rcVERSIONINFO steht auf 0.9.0.0 (v0.9.1 released) —bump-version.shkennt die Datei nicht.
Ratifizierte Nutzer-Entscheidungen¶
- Performance-Symptomatik: durchgehend zäh → echtes GPU-Problem, nicht (nur) die absichtlichen CRT-Artefakte.
- Ein Release v0.10.0 mit allen fünf Blöcken.
- Settings: volle macOS-Parität (Look-Zeile + Sektionen, alle Slider).
- Multi-Monitor: zwei Modi für Saver und Wallpaper — Spanning (ein Bild über alle Monitore) und Je-Monitor (an mit Settings / aus / random). Je-Monitor-Overrides: Szene + Farb-Preset, Rest global. Ein Monitor ist Hochformat → echtes Monitor-Layout verwenden.
- Spanning-Bau: ein Fenster, eine WebView2, eine Engine über die virtuelle Desktop-Bounding-Box (keine 3-Instanzen-Synchronisation).
Design¶
§1 Kiosk-Param (Engine + Host)¶
Neues Query-Param kiosk=on in src/screensaver/params.ts:
hud.controlBar = false+hudPreset = 'custom'(Guard gegenapplyHudPreset-Clobbering, gleicher Mechanismus wie die bestehenden HUD-Overrides).- Unterdrückt das Cursor-Wiedereinblenden bei mousemove und setzt
cursor: noneaufs Overlay (deckt den Chromium-Cursor über dem WebView). - Überspringt den
requestFullscreen-Versuch (im WebView2 nutzlos).
Host: /s und /w hängen konstant &kiosk=on an die URL an — kein
Registry-Wert, sondern Betriebsmodus-Eigenschaft. macOS-App und Web-Dev-Loop
unverändert (Bar dort gewollt). Test: tests/screensaver-params.test.ts.
§2 Settings-Vollausbau¶
Dialog-Gliederung (settings.html, Vorlage macOS-ConfigWindow):
Look-Zeile (5 Bundles aus looks.ts: clean/heavy/broken/vapor/matrix;
manueller Regler-Tweak ⇒ „Eigene") + Sektionen:
| Sektion | Inhalte (Engine-Setting) |
|---|---|
| Bild | Szene (random+7), Farbe (13 Presets), Tempo (slow/norm/fast), Stadt-Höhe (cityAltitude), Sichtweite (fogMode), Wetter (weather, 5 Werte) |
| CRT-Effekte | Slider: Glitch-Intensität (crtSim.intensity 0–1), Krümmung (fx.curvature.amount 0–0.25), Lochmaske (fx.aperture.strength 0–0.5), Bloom (fx.bloom.strength 0–3), Nachleuchten (fx.trails off/on+damp), NTSC (fx.ntsc.amount 0–1), Halation (fx.halation.amount 0–0.6); Toggles: Scanlines, CRT-Sim an/aus |
| Bewegung & Story | Flug-Bank (bankStrength 0–2), Reactive World (narrativeReactiveWorld), Auto-Szenenwechsel (autoCycle.on) + Intervall (0.5–10 min, Clamp beidseitig), Story-Terminal (narrativeTerminal) |
| Terminal | Layout-Select „aus / unten / Fenster": mappt auf die ZWEI bestehenden Felder — aus ⇒ terminal=off (Bool bleibt im gepinnten Präfix), sonst terminal=on + termlayout=strip\|window (⇒ terminalLayout bottom-strip/center-window) |
| Anzeige & Automatik | HUD, Radar, Crosshair, Matrix-Rain, Boot an/aus + Geschwindigkeit (fast/normal/cinematic), Tag/Nacht (dayNightCycle.on), Audio |
| Monitore | §4-UI |
| Leistung | Render-Scale (50–100 %), Adaptive Qualität (perfAdapt) |
Bewusste Skips (macOS-only, kein Web-Pendant): flatHud, Terminal-Größe/ Band-Höhe, Idle-Minuten (regelt Windows), Login-Autostart (ersetzt durch Tray-Autostart §5).
Persistenz/Bridge — additiv, Alt-Format bleibt Präfix:
- Die 12 bestehenden Registry-Werte + Query-Reihenfolge bleiben byte-identisch als Präfix (Upgrade v0.9→v0.10 unsichtbar; fehlende neue Werte ⇒ Defaults).
- Neue REG_SZ-Werte (PascalCase) in fester Anhänge-Reihenfolge, Query-Keys
lowercase:
look,altitude,fog,weather,bank,reactive,autocycle,cyclemin,termlayout,boot,bootspeed,daynight,crtintensity,curvature,aperture,bloomstrength,trailsamount,ntsc,halation,scale,perfadapt. - Numerik als Dezimal-String mit Punkt; Host validiert Format
(
^[0-9]+(\.[0-9]+)?$) + Range,params.tsparst + clampt. Bools bleibenon/off. Enums Whitelist-validiert wie bisher (all-or-nothing-Reject). params.tserhält die neuen Mappings; scene/preset/audio bleiben wie gehabt im Entry (src/screensaver/main.ts) verdrahtet.- Beide Fixture-Tests (
options_test.cpp↔tests/settings-form.test.ts) werden auf das erweiterte Format gehoben und pinnen es wieder beidseitig (neuer Default-String, neuer Voll-Flip-String, Reject-Fälle für Range/Format).
Dialog-Fenster: ~720×640 logische Pixel, skaliert mit
GetDpiForMonitor des Ziel-Monitors (schließt HiDPI-Punkt der
v0.9-Abnahme-TaskNote). Abgelehnter Save schließt den Dialog nicht mehr still:
Host antwortet der Seite (PostWebMessageAsString saveerror), Seite zeigt
Fehlerzeile; Dialog bleibt offen.
§3 Performance-Paket¶
renderScale(Engine, neu): Settings-FeldrenderScale(0.25–1, Default 1) + Query-Paramscale=. Wirkt viasetPixelRatio(min(devicePixelRatio, 2) × renderScale); wird bei Resize re-appliziert. Golden-Harness unberührt (Default 1).- perfAdapt reaktivieren + Downscale-Stufe: Entry setzt nicht mehr hart
perfAdapt:false; neues Paramperfadapt=on/off, Windows-Default on. Der FPS-Wächter erhält oberhalb der Effekt-Drosselung eine Stufe, die die effektive PixelRatio schrittweise senkt (0.85/0.7/0.55 relativ), mit Hysterese zurück. antialias: falseim Renderer (Composer macht MSAA wirkungslos; Einzel-Commit, Golden-Check als Wächter).- Scanline-Drift compositor-freundlich: doppelt hoher Gradient-Layer +
CSS-
transform: translateY-Animation statt Per-Frame-backgroundPositionY-Mutation. - Host-Hygiene: WebView2-User-Data-Dir
%TEMP%\KuroScreensaverWV2→%LOCALAPPDATA%\KuroScreensaver\WebView2(Shader-Cache überlebt Temp-Cleaner). Recherche-Punkt (kein Versprechen): GPU-Präferenz viaICoreWebView2EnvironmentOptions/Chromium-Flag für Hybrid-GPU-Laptops.
Flankierend: CRT-Intensitäts-Slider (§2) zähmt die absichtlichen Glitch-Artefakte separat.
§4 Multi-Monitor (Saver)¶
MonitorMode(REG_SZ):per(Default, verhaltenskompatibel) |span.- Spanning: EIN Fenster über die virtuelle Desktop-Bounding-Box
(
SM_XVIRTUALSCREEN/SM_YVIRTUALSCREEN/SM_CXVIRTUALSCREEN/SM_CYVIRTUALSCREEN), eine WebView2, ein Canvas; jeder Monitor zeigt seinen Ausschnitt (Hochformat inklusive). Ein Input-Watch-Timer. Bekannte, akzeptierte Quirks (in Doku): Lücken der Anordnung werden mitgerendert, ein gemeinsamer Schärfegrad bei Misch-DPI, HUD/Terminal an den Rändern des Gesamtbilds. Empfehlung im Dialog: Render-Scale ≤66 % bei 3 Monitoren. - Je-Monitor: Fenster pro Monitor wie heute, aber pro Monitor
Registry-Subkey
HKCU\Software\KuroScreensaver\Monitors\<StableId>mitMode=on|off|random|scene,Scene(Slug, beiscene),Preset(Slug oder leer ⇒ global).off⇒ schwarzes Deck-Fenster OHNE WebView2 (spart die Chromium-Instanz, Input-Watch läuft trotzdem).random⇒scene=randomfür dieses Fenster. Unbekannte/neue Monitore ⇒onmit globalen Settings. - StableId: aus
QueryDisplayConfig+DISPLAYCONFIG_TARGET_DEVICE_NAME.monitorDevicePath(EDID-basiert, überlebt Docking/Umstecken), sanitized auf[A-Za-z0-9_-], FallbackGetMonitorInfo.szDevice. Kollisions-Fallback: numerischer Suffix. - Dialog-UI („Monitore"): Modus-Radio (Ein Bild über alle / Je Monitor)
- Karte pro erkanntem Monitor (Name, Auflösung, „Hochformat"-Badge,
„Hauptmonitor"-Badge) mit Mode-Select + Szene-/Preset-Selects. Host liefert
die Monitor-Liste als
monitors=<URL-encoded JSON>-Query-Param an settings.html (id, name, w, h, portrait, primary — NICHT Teil des Save-Formats; im Browser-Dev-Loop liefert ein Fake die Liste). - Save-Format: indexierte Keys im bestehenden
save:-Query:m0id,m0mode,m0scene,m0preset,m1id, … (Cap: 8 Monitore). Host validiert (Id-Charset, Mode-Enum, Slugs) all-or-nothing und schreibt die Subkeys transaktional.
§5 Wallpaper /w + Tray + PowerPolicy¶
- Dispatch: expliziter
/w-Zweig inmain.cpp(unbekannte Flags fallen weiterhin in den Saver — .scr-Konvention). - WorkerW-Einhängung: beide Strategien mit Feature-Detection —
klassisch (
SendMessageTimeout(Progman, 0x052C, …)→ WorkerW hinter dem Desktop-SHELLDLL_DefView suchen) und 24H2 (WorkerW direkt unter Progman). - Monitor-Modi wie §4, aber eigene Werte:
WallpaperMonitorMode(per|span, Defaultper) und pro Monitor-SubkeyWMode/WScene/WPreset(Default: Hauptmonitoron, alle anderenoff— Effizienz; an der Docking-Station umstellbar). - Wallpaper-Fenster: Kind des WorkerW,
kiosk=on,audioerzwungenoff, eigenerWallpaperScale(Default 0.66, macOS-Parität). - Tray (
Shell_NotifyIcon): Wallpaper an/aus · Einstellungen (öffnet das /c-Fenster in-process) · Autostart (HKCU-Run-KeyKuroWallpaper="<pfad>\KuroScreensaver.scr" /w) · Beenden. - PowerPolicy:
render_policy.{h,cpp}als pure Entscheidungstabelle (Port vonCore/RenderPolicy.swift; Zustände hidden/frozen/animating, acFps 30 / batteryFps 10, gleiche Prioritäten). Signalsammlung (power_signals.cpp): Session-Lock (WTSRegisterSessionNotification), Vollbild-/Präsentationsmodus (SHQueryUserNotificationState), Monitor-Occlusion (Foreground-Fenster-Bounds vs. Monitor-Rect, Poll im bestehenden Timer), Batterie/Stromsparen (GetSystemPowerStatus+ Power-Broadcast), Display-off (PBT_POWERSETTINGCHANGE, Console-Display-State). Kein Thermal-Signal (kein sauberes Win32-API) — dokumentierter Unterschied zu macOS. - Durchsetzung zweistufig: (1) Host→Page
PostWebMessageAsStringpower:<state>:<fps>→ neuer Engine-Hook im Screensaver-Entry pausiert den rAF-Loop bzw. capped die Framerate (frozen/hidden ⇒ pause, animating ⇒ Cap). (2) BeihiddenzusätzlichCoreWebView2.TrySuspend(),Resume()beim Zurückkehren. Der Vollbild-Saver (/s) ist strukturell ausgenommen (wie macOS).
§6 Build, CI, Tests, Migration¶
- Tests:
options_test.cpperweitert (neues Default-/Flip-Fixture, Range-/Format-Rejects, Monitor-Save-Parsing, Subkey-Roundtrip);render_policy-Tabelle bekommt eigene Testfälle im selben Test-Target (Parität zutests/main.swift-Fällen).tests/settings-form.test.tsspiegelt die neuen Fixtures. Engine-Patches: Typecheck + bestehende Param-Tests + neue kiosk-/scale-Fälle. - bump-version.sh schreibt zusätzlich
native/windows/host/app.rc(VERSIONINFO) — behebt den 0.9.0.0-Drift dauerhaft. - Packaging/CI unverändert:
package-windows.shbleibt die Single Source, die CI ruft; Installer zeigt weiter auf die Mini-exe. - Doku: README (inkl. der 3 Linter-Pflichtsektionen Features/Usage/
No build step),
docs/WINDOWS-INSTALL.md(Wallpaper-Kapitel, Monitor-Modi, Performance-Tipps), neue TaskNote „v0.10.0 on-device-Abnahme Windows" (übernimmt offene v0.9-Punkte: Upgrade-Pfad, Win10-MessageBox, SmartScreen). - Release:
bump-version.sh 0.10.0→ Tag → CI; macOS-dmg-Anhang nur bei Bedarf (macOS-App unverändert; Engine-Web-Änderungen betreffen die Metal-App nicht). Release-Publikation erst nach explizitem User-Go.
Risiken (benannt, akzeptiert)¶
- WorkerW/24H2: beide Strategien implementiert, Rest-Risiko on-device.
- Spanning-Framebuffer: 3 Monitore ≈ >10 MPixel bei Scale 1 — Gegenmittel Render-Scale + perfAdapt-Downscale; Empfehlung im Dialog dokumentiert.
- Ein großer Release-Schnitt: bewusste Nutzer-Entscheidung; die on-device-TaskNote deckt alle Blöcke einzeln ab, damit Fehler isolierbar bleiben.
- Kein lokaler MSVC-Build: Iteration über CI-Artefakte (unverändert).
- Misch-DPI im Spanning: ein gemeinsamer devicePixelRatio; minimal weicheres Bild auf dem schärfsten Display — dokumentierter Quirk.