feat(host,web): experimental DDC/CI monitor power-off for Exclusive sessions
ci / web (push) Successful in 47s
ci / docs-site (push) Successful in 1m7s
decky / build-publish (push) Successful in 19s
apple / swift (push) Successful in 1m10s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 15s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 12s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 30s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 11s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 9s
apple / screenshots (push) Successful in 5m28s
ci / bench (push) Successful in 6m40s
docker / deploy-docs (push) Successful in 20s
windows-host / package (push) Successful in 8m45s
android / android (push) Successful in 12m43s
deb / build-publish (push) Successful in 13m1s
arch / build-publish (push) Successful in 14m36s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 13m5s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 16m20s
ci / rust (push) Successful in 22m12s
ci / web (push) Successful in 47s
ci / docs-site (push) Successful in 1m7s
decky / build-publish (push) Successful in 19s
apple / swift (push) Successful in 1m10s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 15s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 12s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 30s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 11s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 9s
apple / screenshots (push) Successful in 5m28s
ci / bench (push) Successful in 6m40s
docker / deploy-docs (push) Successful in 20s
windows-host / package (push) Successful in 8m45s
android / android (push) Successful in 12m43s
deb / build-publish (push) Successful in 13m1s
arch / build-publish (push) Successful in 14m36s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 13m5s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 16m20s
ci / rust (push) Successful in 22m12s
The sole-virtual-display stutter investigation's active experiment: when the Exclusive isolate deactivates a physical monitor, the dark-but-connected head keeps getting serviced (monitor standby auto-input-scan / DP link churn) at a seconds-scale cadence — the leading suspect for the periodic double-jolt. A panel commanded off over DDC/CI (the VESA monitor-control channel in the video cable) believes it has an owner and, on cooperating firmware, stops probing. - New `ddc_power_off` display-policy axis (default off): orthogonal to presets like game_session, stored in display-settings.json, surfaced in GET/PUT /display/settings + the enforced list, carried through the layout transform. - windows/ddc.rs: VCP 0xD6 power-mode control via the dxva2 Physical Monitor API. Deliberately DPMS-off (0x04, DDC stays responsive, signal return wakes) and never power-button-off (0x05, bricks-until-button on many monitors). Probe-before-write; every failure is skip-and-log — monitors without DDC/CI, OSD-disabled, or behind docks/KVMs degrade to a logged no-op. - Manager wiring: panels commanded off immediately BEFORE the Exclusive CCD isolate (an HMONITOR — and with it the DDC channel — only exists while the display is active); teardown wakes them right after the CCD restore, where returning signal alone already wakes most firmware. - Web console: an Experimental-badged on/off control on the display card, applied immediately like the game-session axis and preserved across preset switches; EN/DE strings incl. the wake-failure escape hatch (press the monitor's power button once, turn the option off). Diagnostic value on top of the fix: if this kills a reporter's stutter, the churn is monitor-firmware-initiated; if only topology=primary/extend does, the driver services dark heads regardless — the two remaining root-cause classes. Verified: Linux 258 tests + clippy + fmt clean; Windows (RTX box) 220/220 + clippy clean; web tsc + production build clean; openapi.json regenerated. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -165,8 +165,9 @@ const DisplayForm: FC<{
|
||||
identity: effective.identity,
|
||||
layout: effective.layout,
|
||||
max_displays: effective.max_displays,
|
||||
// Game-session is orthogonal to the preset — carry it through the Custom switch.
|
||||
// Game-session + DDC are orthogonal to the preset — carry them through the Custom switch.
|
||||
game_session: draft.game_session ?? "auto",
|
||||
ddc_power_off: draft.ddc_power_off ?? false,
|
||||
});
|
||||
} else {
|
||||
apply({ ...draft, preset: id as Preset });
|
||||
@@ -181,6 +182,8 @@ const DisplayForm: FC<{
|
||||
preset: "custom",
|
||||
...p.fields,
|
||||
game_session: p.game_session ?? "auto",
|
||||
// The experimental DDC axis isn't part of a preset — keep the current setting.
|
||||
ddc_power_off: draft.ddc_power_off ?? false,
|
||||
});
|
||||
|
||||
// A custom card is "current" when the in-force policy is a Custom one whose fields + game-session
|
||||
@@ -464,6 +467,37 @@ const DisplayForm: FC<{
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* EXPERIMENTAL: DDC/CI panel power-off — orthogonal like game-session (survives preset
|
||||
switches, applies immediately). Windows-only in effect, acted on at the Exclusive isolate. */}
|
||||
<div className="border-t pt-4">
|
||||
<div className="space-y-3">
|
||||
<div className="flex items-center gap-2">
|
||||
<Label className="block">{m.display_ddc()}</Label>
|
||||
<Badge variant="outline" className="text-amber-600 dark:text-amber-500">
|
||||
{m.display_experimental()}
|
||||
</Badge>
|
||||
</div>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{([false, true] as const).map((on) => (
|
||||
<Button
|
||||
key={String(on)}
|
||||
size="sm"
|
||||
variant={(draft.ddc_power_off ?? false) === on ? "default" : "outline"}
|
||||
disabled={busy}
|
||||
onClick={() => {
|
||||
const next = { ...draft, ddc_power_off: on };
|
||||
setDraft(next);
|
||||
apply(next);
|
||||
}}
|
||||
>
|
||||
{on ? m.display_ddc_enabled() : m.display_ddc_disabled()}
|
||||
</Button>
|
||||
))}
|
||||
</div>
|
||||
<p className="max-w-prose text-xs text-muted-foreground">{m.display_ddc_help()}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* What's in force right now */}
|
||||
<div className="flex flex-wrap items-center gap-2 border-t pt-3">
|
||||
<span className="text-sm text-muted-foreground">{m.display_effective()}:</span>
|
||||
@@ -476,6 +510,9 @@ const DisplayForm: FC<{
|
||||
{(draft.game_session ?? "auto") === "dedicated" && (
|
||||
<Badge variant="secondary">{m.display_game_session_dedicated()}</Badge>
|
||||
)}
|
||||
{(draft.ddc_power_off ?? false) && (
|
||||
<Badge variant="outline">{m.display_ddc_badge()}</Badge>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<p className="max-w-prose text-xs text-muted-foreground">{m.display_pending_note()}</p>
|
||||
|
||||
Reference in New Issue
Block a user