This tracks the full portrait streaming orientation work for cosmic-pretzel-98 (and all multi-orientation bodies). Root cause: landscape kiosk overlay showing behind a portrait UE app when a portrait stream started.
Root causes:
kioskoverlay did not respond to WM_DISPLAYCHANGE — overlay stayed landscape when VDD switched to portrait
handleStreamStarted did not receive orientation from prep-cmd explicitly
handleStreamEnded was not implemented — Undo hook silently 404'd, leaving VDD at last stream resolution
Hardcoded resolution strings scattered across multiple files
No explicit pause between writing dd_manual_resolution and Sunshine beginning capture
After v1.11.34 the architecture changed: dd_resolution_option switched from manual to auto. Sunshine now owns layer 2 directly (switches VDD to the Moonlight-requested resolution at RTSP negotiation). The root cause for auto mode not working was --no-game-optimization in Moonlight suppressing optimizeGameSettings=true, which Sunshine requires to apply auto mode. Fixed in hydraheadflatscreen v2.0.41.
Changes shipped (v1.11.28 through v1.11.35)
cmd/kioskoverlay/main.go: Added WM_DISPLAYCHANGE handler — resizes overlay window to fill screen when VDD switches.
pkg/provider/experiences.go: Added Resolution field to liveExperience and experienceInstallInfo. Added streamResolution() as single source of truth for VDD resolution + UE args.
pkg/provider/sunshine_provider.go: Added desiredApp struct with orientation. Added buildOrientationHooks() — bakes orientation into prep-cmd JSON body at app registration. Added post-sync read-back verification.
pkg/provider/sunshine.go: Experience state loaded before head loop. Head apps resolve orientation from experience state cache. Uses streamResolution for UE launch args.
pkg/provider/httpserver.go: handleStreamStarted reads orientation from request, falls back to cache, logs mismatch, returns {"orientation":"..."} for layer 2 contract verification. handleStreamEnded resets VDD to landscape idle state.
pkg/provider/vdd_windows.go: writeVDDSettings now includes 60Hz mode variants. ensureVDDResolutions() tracks custom resolutions from experience library. Mutex on setSunshineConfig.
Full implementation description
What was done
This tracks the full portrait streaming orientation work for cosmic-pretzel-98 (and all multi-orientation bodies). Root cause: landscape kiosk overlay showing behind a portrait UE app when a portrait stream started.
Root causes:
After v1.11.34 the architecture changed: dd_resolution_option switched from manual to auto. Sunshine now owns layer 2 directly (switches VDD to the Moonlight-requested resolution at RTSP negotiation). The root cause for auto mode not working was --no-game-optimization in Moonlight suppressing optimizeGameSettings=true, which Sunshine requires to apply auto mode. Fixed in hydraheadflatscreen v2.0.41.
Changes shipped (v1.11.28 through v1.11.35)
cmd/kioskoverlay/main.go: Added WM_DISPLAYCHANGE handler — resizes overlay window to fill screen when VDD switches.
pkg/provider/experiences.go: Added Resolution field to liveExperience and experienceInstallInfo. Added streamResolution() as single source of truth for VDD resolution + UE args.
pkg/provider/sunshine_provider.go: Added desiredApp struct with orientation. Added buildOrientationHooks() — bakes orientation into prep-cmd JSON body at app registration. Added post-sync read-back verification.
pkg/provider/sunshine.go: Experience state loaded before head loop. Head apps resolve orientation from experience state cache. Uses streamResolution for UE launch args.
pkg/provider/httpserver.go: handleStreamStarted reads orientation from request, falls back to cache, logs mismatch, returns {"orientation":"..."} for layer 2 contract verification. handleStreamEnded resets VDD to landscape idle state.
pkg/provider/vdd_windows.go: writeVDDSettings now includes 60Hz mode variants. ensureVDDResolutions() tracks custom resolutions from experience library. Mutex on setSunshineConfig.
Orientation contract (three layers)
Layer 1 — Stream encoding: Resolution Moonlight client requests from Sunshine (head: hydraheadflatscreen / hydraneckwebrtc)
Layer 2 — Display resolution: dd_resolution_option=auto in sunshine.conf, Sunshine switches VDD to Moonlight-requested resolution
Layer 3 — App window: UE launch args -ResX=1080 -ResY=1920 -windowed -ForceRes registered in Sunshine app cmd
Verification (2026-05-14)
Sunshine log confirmed: Desktop resolution [1080x1920] on mercator-talks stream start.
hydrabody log: [stream] stream started: mercator-talks orientation=portrait (layer 2 via Sunshine auto)
Capture size: 1080x1920