Files
plezy/windows/runner/mpv/display_mode_manager.cpp
T

1365 lines
56 KiB
C++

#include "display_mode_manager.h"
#include <algorithm>
#include <cmath>
#include <mutex>
#include <vector>
#include "sdk_26100.h"
namespace mpv {
namespace {
constexpr wchar_t kRegistryPath[] = L"Software\\Plezy\\DisplayModeOverride";
constexpr wchar_t kRegVersion[] = L"Version";
constexpr DWORD kRecoveryVersion = 1;
constexpr wchar_t kRegModeDeviceName[] = L"ModeDeviceName";
constexpr wchar_t kRegLegacyDeviceName[] = L"DeviceName";
constexpr wchar_t kRegHDRDeviceName[] = L"HDRDeviceName";
constexpr wchar_t kRegOriginalRefreshRate[] = L"OriginalRefreshRate";
constexpr wchar_t kRegOriginalWidth[] = L"OriginalWidth";
constexpr wchar_t kRegOriginalHeight[] = L"OriginalHeight";
constexpr wchar_t kRegOriginalHDR[] = L"OriginalHDREnabled";
constexpr wchar_t kRegModeChanged[] = L"ModeChanged";
constexpr wchar_t kRegHDRChanged[] = L"HDRChanged";
constexpr wchar_t kRegModeTakeoverEligible[] = L"ModeTakeoverEligible";
constexpr wchar_t kRegHDRTakeoverEligible[] = L"HDRTakeoverEligible";
constexpr wchar_t kRegModeRecoverySlot[] = L"ModeRecoverySlot";
constexpr wchar_t kRegHDRRecoverySlot[] = L"HDRRecoverySlot";
constexpr wchar_t kRegModeDeviceNameAlternate[] = L"ModeDeviceNameAlternate";
constexpr wchar_t kRegHDRDeviceNameAlternate[] = L"HDRDeviceNameAlternate";
constexpr wchar_t kRegOriginalRefreshRateAlternate[] = L"OriginalRefreshRateAlternate";
constexpr wchar_t kRegOriginalWidthAlternate[] = L"OriginalWidthAlternate";
constexpr wchar_t kRegOriginalHeightAlternate[] = L"OriginalHeightAlternate";
constexpr wchar_t kRegOriginalHDRAlternate[] = L"OriginalHDREnabledAlternate";
constexpr wchar_t kRegModeHandoffPending[] = L"ModeHandoffPending";
constexpr wchar_t kRegHDRHandoffPending[] = L"HDRHandoffPending";
constexpr wchar_t kRegModePreviousRecoverySlot[] = L"ModePreviousRecoverySlot";
constexpr wchar_t kRegHDRPreviousRecoverySlot[] = L"HDRPreviousRecoverySlot";
std::recursive_mutex g_display_override_mutex;
bool g_live_mode_recovery_record = false;
bool g_live_hdr_recovery_record = false;
bool g_recovery_in_progress = false;
class RecoveryRunGuard {
public:
RecoveryRunGuard() : acquired_(!g_recovery_in_progress) {
if (acquired_) g_recovery_in_progress = true;
}
~RecoveryRunGuard() {
if (acquired_) g_recovery_in_progress = false;
}
bool acquired() const { return acquired_; }
private:
bool acquired_;
};
// Query display paths and modes with the given QDC flags, retrying on
// ERROR_INSUFFICIENT_BUFFER (Kodi pattern). Clears the outputs on failure.
bool QueryDisplayConfigPathsAndModes(
UINT32 flags, std::vector<DISPLAYCONFIG_PATH_INFO>& paths, std::vector<DISPLAYCONFIG_MODE_INFO>& modes) {
UINT32 path_count = 0;
UINT32 mode_count = 0;
LONG result;
do {
if (GetDisplayConfigBufferSizes(flags, &path_count, &mode_count) != ERROR_SUCCESS) {
paths.clear();
modes.clear();
return false;
}
paths.resize(path_count);
modes.resize(mode_count);
result = QueryDisplayConfig(flags, &path_count, paths.data(), &mode_count, modes.data(), nullptr);
} while (result == ERROR_INSUFFICIENT_BUFFER);
if (result != ERROR_SUCCESS) {
paths.clear();
modes.clear();
return false;
}
paths.resize(path_count);
modes.resize(mode_count);
return true;
}
// Capture the complete active topology. Prefer a virtual-refresh-rate-aware
// snapshot so a Dynamic Refresh Rate configuration
// (DISPLAYCONFIG_PATH_BOOST_REFRESH_RATE) survives the round trip; retry
// without that flag for OS versions that reject it (pre-Win11-22H2).
DisplayConfigSnapshot CaptureDisplayConfigSnapshot() {
DisplayConfigSnapshot snapshot;
constexpr UINT32 base_flags = QDC_ONLY_ACTIVE_PATHS | QDC_VIRTUAL_MODE_AWARE;
snapshot.vrr_aware = true;
if (QueryDisplayConfigPathsAndModes(base_flags | QDC_VIRTUAL_REFRESH_RATE_AWARE, snapshot.paths, snapshot.modes)) {
return snapshot;
}
snapshot.vrr_aware = false;
QueryDisplayConfigPathsAndModes(base_flags, snapshot.paths, snapshot.modes);
return snapshot;
}
// Re-apply a captured topology through SetDisplayConfig. Unlike the legacy
// ChangeDisplaySettingsExW restore, this preserves CCD-level path state such
// as the Dynamic Refresh Rate boost flag, which a DEVMODEW cannot express: an
// explicit DEVMODE restore turns a "Dynamic" refresh-rate selection into a
// fixed rate pinned at the DRR base rate. save_to_database additionally
// repairs the persisted display database in case the 24/48/60Hz registry
// workaround in SetDisplayMode overwrote it.
bool ApplyDisplayConfigSnapshot(const DisplayConfigSnapshot& snapshot, bool save_to_database) {
if (!snapshot.valid()) return false;
// SetDisplayConfig may adjust the supplied arrays (SDC_ALLOW_CHANGES); keep
// the caller's snapshot intact for later retries.
std::vector<DISPLAYCONFIG_PATH_INFO> paths = snapshot.paths;
std::vector<DISPLAYCONFIG_MODE_INFO> modes = snapshot.modes;
UINT32 flags = SDC_APPLY | SDC_USE_SUPPLIED_DISPLAY_CONFIG | SDC_ALLOW_CHANGES | SDC_VIRTUAL_MODE_AWARE;
if (snapshot.vrr_aware) flags |= SDC_VIRTUAL_REFRESH_RATE_AWARE;
if (save_to_database) flags |= SDC_SAVE_TO_DATABASE;
return SetDisplayConfig(
static_cast<UINT32>(paths.size()), paths.data(), static_cast<UINT32>(modes.size()), modes.data(), flags) ==
ERROR_SUCCESS;
}
// Re-apply the user's persisted display configuration for the current
// topology -- the documented "return from a temporary mode to the last saved
// display configuration" SetDisplayConfig scenario. The database entry keeps
// CCD-level state such as a Dynamic Refresh Rate selection that the recovery
// record's DEVMODE cannot express, and Plezy's overrides never replace it
// (restores only ever save the pre-override snapshot). The awareness flags
// are documented modifiers to supplied-config calls only, so they are
// deliberately absent here. Used by crash recovery, which has no in-memory
// snapshot.
bool ApplyDatabaseCurrentConfig() {
return SetDisplayConfig(0, nullptr, 0, nullptr, SDC_APPLY | SDC_USE_DATABASE_CURRENT) == ERROR_SUCCESS;
}
bool PrepareModeRecoveryAtRegistry(const std::wstring& device_name, DWORD width, DWORD height, DWORD refresh_rate);
bool PrepareHDRRecoveryAtRegistry(const std::wstring& device_name, bool enabled);
bool CompleteRecoveryOperationAtRegistry(
const wchar_t* marker, const wchar_t* takeover_disposition, const wchar_t* handoff_pending);
bool FinalizeModeRecoveryAtRegistry(bool os_apply_succeeded);
bool FinalizeHDRRecoveryAtRegistry(bool os_apply_succeeded);
bool MarkTakeoverEligibleAtRegistry(const wchar_t* takeover_disposition);
} // namespace
DisplayModeManager::DisplayModeManager() {}
DisplayModeManager::~DisplayModeManager() {}
std::wstring DisplayModeManager::GetMonitorDeviceName(HWND window) {
HMONITOR monitor = MonitorFromWindow(window, MONITOR_DEFAULTTONEAREST);
if (!monitor) return {};
MONITORINFOEXW mi = {};
mi.cbSize = sizeof(mi);
if (!GetMonitorInfoW(monitor, &mi)) return {};
return mi.szDevice;
}
std::vector<DISPLAYCONFIG_PATH_INFO> DisplayModeManager::GetDisplayConfigPaths() {
std::vector<DISPLAYCONFIG_PATH_INFO> paths;
std::vector<DISPLAYCONFIG_MODE_INFO> modes;
QueryDisplayConfigPathsAndModes(QDC_ONLY_ACTIVE_PATHS, paths, modes);
return paths;
}
std::optional<DisplayConfigId> DisplayModeManager::GetDisplayTargetId(const std::wstring& gdi_device_name) {
// Follows Kodi's GetDisplayTargetId: iterate QueryDisplayConfig paths,
// match via DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME.viewGdiDeviceName.
DISPLAYCONFIG_SOURCE_DEVICE_NAME source = {};
source.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME;
source.header.size = sizeof(source);
for (const auto& path : GetDisplayConfigPaths()) {
source.header.adapterId = path.sourceInfo.adapterId;
source.header.id = path.sourceInfo.id;
if (DisplayConfigGetDeviceInfo(&source.header) == ERROR_SUCCESS && gdi_device_name == source.viewGdiDeviceName) {
return DisplayConfigId{path.targetInfo.adapterId, path.targetInfo.id};
}
}
return std::nullopt;
}
bool DisplayModeManager::IsWin11_24H2OrNewer() {
// Win11 24H2 = build 26100+
OSVERSIONINFOEXW osvi = {};
osvi.dwOSVersionInfoSize = sizeof(osvi);
osvi.dwBuildNumber = 26100;
DWORDLONG condition_mask = 0;
VER_SET_CONDITION(condition_mask, VER_BUILDNUMBER, VER_GREATER_EQUAL);
return VerifyVersionInfoW(&osvi, VER_BUILDNUMBER, condition_mask) != FALSE;
}
std::vector<DisplayMode> DisplayModeManager::EnumerateDisplayModes(HWND window) {
std::wstring device_name = GetMonitorDeviceName(window);
if (device_name.empty()) return {};
std::vector<DisplayMode> modes;
DEVMODEW dm = {};
dm.dmSize = sizeof(dm);
for (DWORD i = 0; EnumDisplaySettingsW(device_name.c_str(), i, &dm); i++) {
DisplayMode mode;
mode.width = dm.dmPelsWidth;
mode.height = dm.dmPelsHeight;
mode.refresh_rate = dm.dmDisplayFrequency;
modes.push_back(mode);
}
std::sort(modes.begin(), modes.end(), [](const DisplayMode& a, const DisplayMode& b) {
if (a.width != b.width) return a.width < b.width;
if (a.height != b.height) return a.height < b.height;
return a.refresh_rate < b.refresh_rate;
});
modes.erase(
std::unique(
modes.begin(), modes.end(),
[](const DisplayMode& a, const DisplayMode& b) {
return a.width == b.width && a.height == b.height && a.refresh_rate == b.refresh_rate;
}),
modes.end());
return modes;
}
DisplayMode DisplayModeManager::GetCurrentMode(HWND window) {
std::wstring device_name = GetMonitorDeviceName(window);
DisplayMode mode = {};
if (device_name.empty()) return mode;
DEVMODEW dm = {};
dm.dmSize = sizeof(dm);
if (EnumDisplaySettingsW(device_name.c_str(), ENUM_CURRENT_SETTINGS, &dm)) {
mode.width = dm.dmPelsWidth;
mode.height = dm.dmPelsHeight;
mode.refresh_rate = dm.dmDisplayFrequency;
}
return mode;
}
void DisplayModeManager::SaveOriginalMode(HWND window) {
// Snapshot the CCD topology first, before any Windows mutation, so restores
// can put back state the legacy DEVMODE cannot express (Dynamic Refresh
// Rate). On a DRR display ENUM_CURRENT_SETTINGS reports only the fixed DRR
// base rate (issue #2055).
original_config_ = CaptureDisplayConfigSnapshot();
original_device_name_ = GetMonitorDeviceName(window);
if (original_device_name_.empty()) return;
original_devmode_ = {};
original_devmode_.dmSize = sizeof(original_devmode_);
EnumDisplaySettingsW(original_device_name_.c_str(), ENUM_CURRENT_SETTINGS, &original_devmode_);
}
bool DisplayModeManager::SetDisplayMode(HWND window, DWORD width, DWORD height, DWORD refresh_rate) {
std::lock_guard<std::recursive_mutex> transaction_lock(g_display_override_mutex);
const std::wstring device_name = GetMonitorDeviceName(window);
if (device_name.empty()) return false;
const bool mode_was_changed = mode_changed_;
if (!mode_changed_) SaveOriginalMode(window);
if (original_device_name_.empty() || original_devmode_.dmPelsWidth == 0 || original_devmode_.dmPelsHeight == 0 ||
original_devmode_.dmDisplayFrequency == 0) {
return false;
}
if (!PrepareModeRecoveryAtRegistry(
original_device_name_, original_devmode_.dmPelsWidth, original_devmode_.dmPelsHeight,
original_devmode_.dmDisplayFrequency)) {
return false;
}
// Mark the record live before calling Windows. ChangeDisplaySettingsExW can
// synchronously deliver WM_DISPLAYCHANGE; that event must not recover the
// override that is currently being applied.
g_live_mode_recovery_record = true;
DEVMODEW dm = {};
dm.dmSize = sizeof(dm);
dm.dmPelsWidth = width;
dm.dmPelsHeight = height;
dm.dmDisplayFrequency = refresh_rate;
dm.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFREQUENCY;
bool changed = false;
// Kodi's Win8+ workaround for exact integer refresh rates (24, 48, 60 Hz).
// Write desired mode to registry, apply from registry, restore registry.
// Source: xbmc/windowing/windows/WinSystemWin32.cpp:940-970.
if (refresh_rate == 24 || refresh_rate == 48 || refresh_rate == 60) {
DEVMODEW registry_dm = {};
registry_dm.dmSize = sizeof(registry_dm);
if (EnumDisplaySettingsW(device_name.c_str(), ENUM_REGISTRY_SETTINGS, &registry_dm)) {
LONG rc = ChangeDisplaySettingsExW(device_name.c_str(), &dm, nullptr, CDS_UPDATEREGISTRY | CDS_NORESET, nullptr);
if (rc == DISP_CHANGE_SUCCESSFUL) {
rc = ChangeDisplaySettingsExW(device_name.c_str(), nullptr, nullptr, CDS_FULLSCREEN, nullptr);
if (rc == DISP_CHANGE_SUCCESSFUL) changed = true;
// Restore original registry settings.
registry_dm.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFREQUENCY | DM_DISPLAYFLAGS;
ChangeDisplaySettingsExW(device_name.c_str(), &registry_dm, nullptr, CDS_UPDATEREGISTRY | CDS_NORESET, nullptr);
}
}
}
// Standard path / fallback.
if (!changed) {
const LONG rc = ChangeDisplaySettingsExW(device_name.c_str(), &dm, nullptr, CDS_FULLSCREEN, nullptr);
changed = rc == DISP_CHANGE_SUCCESSFUL;
}
if (changed) {
FinalizeModeRecoveryAtRegistry(true);
mode_changed_ = true;
} else if (!mode_was_changed) {
// A failed takeover rolls back to its prior marked original. A fresh
// failed operation still clears only its own marker.
const bool recovery_completed = FinalizeModeRecoveryAtRegistry(false);
g_live_mode_recovery_record = false;
if (!recovery_completed) MarkTakeoverEligibleAtRegistry(kRegModeTakeoverEligible);
}
return changed;
}
bool DisplayModeManager::RestoreOriginalMode(HWND) {
std::lock_guard<std::recursive_mutex> transaction_lock(g_display_override_mutex);
if (!mode_changed_) return false;
if (original_device_name_.empty()) {
g_live_mode_recovery_record = false;
MarkTakeoverEligibleAtRegistry(kRegModeTakeoverEligible);
return false;
}
// Prefer re-applying the pre-override topology through SetDisplayConfig: it
// restores a "Dynamic" refresh-rate selection that the DEVMODE fallback
// would pin to a fixed rate at the DRR base frequency (issue #2055).
bool restored = ApplyDisplayConfigSnapshot(original_config_, /*save_to_database=*/true);
if (!restored) {
original_devmode_.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFREQUENCY | DM_DISPLAYFLAGS;
LONG rc =
ChangeDisplaySettingsExW(original_device_name_.c_str(), &original_devmode_, nullptr, CDS_FULLSCREEN, nullptr);
if (rc != DISP_CHANGE_SUCCESSFUL) {
// Fallback: restore registry defaults.
rc = ChangeDisplaySettingsExW(original_device_name_.c_str(), nullptr, nullptr, 0, nullptr);
}
restored = rc == DISP_CHANGE_SUCCESSFUL;
}
if (!restored) {
// The explicit owner has given up. Keep the durable marker, but release it
// so a later topology notification can restore a reconnected target or a
// conflicting mode request can consume the failed recovery disposition.
g_live_mode_recovery_record = false;
MarkTakeoverEligibleAtRegistry(kRegModeTakeoverEligible);
return false;
}
mode_changed_ = false;
const bool recovery_completed =
CompleteRecoveryOperationAtRegistry(kRegModeChanged, kRegModeTakeoverEligible, kRegModeHandoffPending);
g_live_mode_recovery_record = false;
if (!recovery_completed) MarkTakeoverEligibleAtRegistry(kRegModeTakeoverEligible);
return true;
}
// --- HDR ---
bool DisplayModeManager::IsHDRSupported(HWND window) {
std::wstring device_name = GetMonitorDeviceName(window);
if (device_name.empty()) return false;
auto target_id = GetDisplayTargetId(device_name);
if (!target_id) return false;
// Follows Kodi's GetDisplayHDRStatus pattern.
if (IsWin11_24H2OrNewer()) {
DISPLAYCONFIG_GET_ADVANCED_COLOR_INFO_2 info = {};
info.header.type = static_cast<DISPLAYCONFIG_DEVICE_INFO_TYPE>(DISPLAYCONFIG_DEVICE_INFO_GET_ADVANCED_COLOR_INFO_2);
info.header.size = sizeof(info);
info.header.adapterId = target_id->adapter_id;
info.header.id = target_id->id;
if (DisplayConfigGetDeviceInfo(&info.header) == ERROR_SUCCESS) {
return info.highDynamicRangeSupported == TRUE;
}
} else {
DISPLAYCONFIG_GET_ADVANCED_COLOR_INFO info = {};
info.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_ADVANCED_COLOR_INFO;
info.header.size = sizeof(info);
info.header.adapterId = target_id->adapter_id;
info.header.id = target_id->id;
if (DisplayConfigGetDeviceInfo(&info.header) == ERROR_SUCCESS) {
// advancedColorSupported=1 && wideColorEnforced=0 => true HDR screen.
// advancedColorSupported=1 && wideColorEnforced=1 => SDR screen with ACM (Win11 22H2+).
// Source: Kodi DisplayUtilsWin32.cpp:157-172.
return info.advancedColorSupported && !info.wideColorEnforced;
}
}
return false;
}
bool DisplayModeManager::IsHDREnabled(HWND window) {
std::wstring device_name = GetMonitorDeviceName(window);
if (device_name.empty()) return false;
auto target_id = GetDisplayTargetId(device_name);
if (!target_id) return false;
return IsHDREnabledForTarget(*target_id);
}
bool DisplayModeManager::IsHDREnabledForTarget(const DisplayConfigId& target) {
if (IsWin11_24H2OrNewer()) {
DISPLAYCONFIG_GET_ADVANCED_COLOR_INFO_2 info = {};
info.header.type = static_cast<DISPLAYCONFIG_DEVICE_INFO_TYPE>(DISPLAYCONFIG_DEVICE_INFO_GET_ADVANCED_COLOR_INFO_2);
info.header.size = sizeof(info);
info.header.adapterId = target.adapter_id;
info.header.id = target.id;
if (DisplayConfigGetDeviceInfo(&info.header) == ERROR_SUCCESS) {
return info.activeColorMode == DISPLAYCONFIG_ADVANCED_COLOR_MODE_HDR;
}
} else {
DISPLAYCONFIG_GET_ADVANCED_COLOR_INFO info = {};
info.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_ADVANCED_COLOR_INFO;
info.header.size = sizeof(info);
info.header.adapterId = target.adapter_id;
info.header.id = target.id;
if (DisplayConfigGetDeviceInfo(&info.header) == ERROR_SUCCESS) {
bool hdr_supported = info.advancedColorSupported && !info.wideColorEnforced;
return hdr_supported && info.advancedColorEnabled;
}
}
return false;
}
void DisplayModeManager::SaveOriginalHDRState(HWND window) {
original_hdr_device_name_ = GetMonitorDeviceName(window);
original_hdr_enabled_ = IsHDREnabled(window);
}
bool DisplayModeManager::SetHDREnabled(HWND window, bool enabled) {
std::lock_guard<std::recursive_mutex> transaction_lock(g_display_override_mutex);
// While an HDR change is live, a repeat toggle stays tied to the recorded
// display so the persisted record and the toggled target always name the
// same display. When the window has verifiably moved to another monitor,
// hand off: restore the recorded display through the normal restore path
// (which retires its recovery record), then run this request as a fresh
// change on the current monitor. If that restore fails, refuse the new
// operation so the retained record still names the only diverged display.
// If the current monitor cannot be determined, stay pinned to the recorded
// display rather than retarget.
const std::wstring current_device_name = GetMonitorDeviceName(window);
if (hdr_changed_ && !current_device_name.empty() && current_device_name != original_hdr_device_name_ &&
!RestoreOriginalHDRState(window)) {
return false;
}
const bool hdr_was_changed = hdr_changed_;
const std::wstring device_name = hdr_was_changed ? original_hdr_device_name_ : current_device_name;
if (device_name.empty()) return false;
const auto target_id = GetDisplayTargetId(device_name);
if (!target_id) return false;
if (!hdr_was_changed) SaveOriginalHDRState(window);
if (original_hdr_device_name_.empty() ||
!PrepareHDRRecoveryAtRegistry(original_hdr_device_name_, original_hdr_enabled_)) {
return false;
}
// See SetDisplayMode: keep synchronous topology notifications from treating
// this process's just-persisted marker as crash recovery.
g_live_hdr_recovery_record = true;
// Save the display topology before the toggle — Windows changes display
// mode on HDR state change (Kodi WIN32Util.cpp:1252-1257). The DEVMODEW is
// the fallback when no CCD snapshot is available.
const DisplayConfigSnapshot pre_toggle_config = CaptureDisplayConfigSnapshot();
DEVMODEW pre_toggle_dm = {};
pre_toggle_dm.dmSize = sizeof(pre_toggle_dm);
EnumDisplaySettingsW(device_name.c_str(), ENUM_CURRENT_SETTINGS, &pre_toggle_dm);
const LONG result = SetHDRStateForTarget(*target_id, enabled);
if (result != ERROR_SUCCESS) {
if (!hdr_was_changed) {
const bool recovery_completed = FinalizeHDRRecoveryAtRegistry(false);
g_live_hdr_recovery_record = false;
if (!recovery_completed) MarkTakeoverEligibleAtRegistry(kRegHDRTakeoverEligible);
}
return false;
}
// Restore the display mode after the toggle — Windows may have changed it
// (Kodi WIN32Util.cpp:1276-1288). Re-applying the CCD snapshot keeps a
// Dynamic Refresh Rate selection intact; the DEVMODE re-apply would pin the
// DRR base rate (issue #2055).
if (!ApplyDisplayConfigSnapshot(pre_toggle_config, /*save_to_database=*/false) &&
pre_toggle_dm.dmDisplayFrequency != 0) {
pre_toggle_dm.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFREQUENCY | DM_DISPLAYFLAGS;
ChangeDisplaySettingsExW(device_name.c_str(), &pre_toggle_dm, nullptr, CDS_FULLSCREEN, nullptr);
}
FinalizeHDRRecoveryAtRegistry(true);
hdr_changed_ = true;
return true;
}
bool DisplayModeManager::RestoreOriginalHDRState(HWND) {
std::lock_guard<std::recursive_mutex> transaction_lock(g_display_override_mutex);
if (!hdr_changed_) return false;
if (original_hdr_device_name_.empty()) {
g_live_hdr_recovery_record = false;
MarkTakeoverEligibleAtRegistry(kRegHDRTakeoverEligible);
return false;
}
const auto target_id = GetDisplayTargetId(original_hdr_device_name_);
if (!target_id) {
g_live_hdr_recovery_record = false;
MarkTakeoverEligibleAtRegistry(kRegHDRTakeoverEligible);
return false;
}
if (IsHDREnabledForTarget(*target_id) != original_hdr_enabled_) {
// The gate queries the recorded target, not the window's current monitor,
// so moving the window to another display after the HDR change cannot
// skip the restore. The target was resolved above even when the observed
// state already matches, so a disconnected target cannot be mistaken for
// a successful restore.
// Save the display topology before the restore toggle; see SetHDREnabled.
const DisplayConfigSnapshot pre_toggle_config = CaptureDisplayConfigSnapshot();
DEVMODEW pre_toggle_dm = {};
pre_toggle_dm.dmSize = sizeof(pre_toggle_dm);
EnumDisplaySettingsW(original_hdr_device_name_.c_str(), ENUM_CURRENT_SETTINGS, &pre_toggle_dm);
if (SetHDRStateForTarget(*target_id, original_hdr_enabled_) != ERROR_SUCCESS) {
g_live_hdr_recovery_record = false;
MarkTakeoverEligibleAtRegistry(kRegHDRTakeoverEligible);
return false;
}
// Restore the display mode after the toggle; see SetHDREnabled.
if (!ApplyDisplayConfigSnapshot(pre_toggle_config, /*save_to_database=*/false) &&
pre_toggle_dm.dmDisplayFrequency != 0) {
pre_toggle_dm.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFREQUENCY | DM_DISPLAYFLAGS;
ChangeDisplaySettingsExW(original_hdr_device_name_.c_str(), &pre_toggle_dm, nullptr, CDS_FULLSCREEN, nullptr);
}
}
hdr_changed_ = false;
const bool recovery_completed =
CompleteRecoveryOperationAtRegistry(kRegHDRChanged, kRegHDRTakeoverEligible, kRegHDRHandoffPending);
g_live_hdr_recovery_record = false;
if (!recovery_completed) MarkTakeoverEligibleAtRegistry(kRegHDRTakeoverEligible);
return true;
}
// --- Crash recovery (Windows Registry) ---
LONG DisplayModeManager::SetHDRStateForTarget(const DisplayConfigId& target, bool enabled) {
if (IsWin11_24H2OrNewer()) {
DISPLAYCONFIG_SET_HDR_STATE state = {};
state.header.type = static_cast<DISPLAYCONFIG_DEVICE_INFO_TYPE>(DISPLAYCONFIG_DEVICE_INFO_SET_HDR_STATE);
state.header.size = sizeof(state);
state.header.adapterId = target.adapter_id;
state.header.id = target.id;
state.enableHdr = enabled ? TRUE : FALSE;
return DisplayConfigSetDeviceInfo(&state.header);
} else {
DISPLAYCONFIG_SET_ADVANCED_COLOR_STATE state = {};
state.header.type = DISPLAYCONFIG_DEVICE_INFO_SET_ADVANCED_COLOR_STATE;
state.header.size = sizeof(state);
state.header.adapterId = target.adapter_id;
state.header.id = target.id;
state.enableAdvancedColor = enabled ? TRUE : FALSE;
return DisplayConfigSetDeviceInfo(&state.header);
}
}
namespace {
bool WriteRegistryDWORD(const wchar_t* value_name, DWORD value) {
HKEY key;
if (RegCreateKeyExW(HKEY_CURRENT_USER, kRegistryPath, 0, nullptr, 0, KEY_WRITE, nullptr, &key, nullptr) !=
ERROR_SUCCESS) {
return false;
}
const LONG result =
RegSetValueExW(key, value_name, 0, REG_DWORD, reinterpret_cast<const BYTE*>(&value), sizeof(value));
RegCloseKey(key);
return result == ERROR_SUCCESS;
}
bool WriteRegistryString(const wchar_t* value_name, const std::wstring& value) {
HKEY key;
if (RegCreateKeyExW(HKEY_CURRENT_USER, kRegistryPath, 0, nullptr, 0, KEY_WRITE, nullptr, &key, nullptr) !=
ERROR_SUCCESS) {
return false;
}
const LONG result = RegSetValueExW(
key, value_name, 0, REG_SZ, reinterpret_cast<const BYTE*>(value.c_str()),
static_cast<DWORD>((value.size() + 1) * sizeof(wchar_t)));
RegCloseKey(key);
return result == ERROR_SUCCESS;
}
bool ReadRegistryDWORD(const wchar_t* value_name, DWORD& value) {
HKEY key;
if (RegOpenKeyExW(HKEY_CURRENT_USER, kRegistryPath, 0, KEY_READ, &key) != ERROR_SUCCESS) return false;
DWORD size = sizeof(value);
DWORD type = 0;
const LONG result = RegQueryValueExW(key, value_name, nullptr, &type, reinterpret_cast<BYTE*>(&value), &size);
RegCloseKey(key);
return result == ERROR_SUCCESS && type == REG_DWORD && size == sizeof(value);
}
bool ReadRegistryString(const wchar_t* value_name, std::wstring& value) {
HKEY key;
if (RegOpenKeyExW(HKEY_CURRENT_USER, kRegistryPath, 0, KEY_READ, &key) != ERROR_SUCCESS) return false;
DWORD size = 0;
DWORD type = 0;
const LONG size_result = RegQueryValueExW(key, value_name, nullptr, &type, nullptr, &size);
if (size_result != ERROR_SUCCESS || type != REG_SZ || size == 0 || size % sizeof(wchar_t) != 0) {
RegCloseKey(key);
return false;
}
value.resize(size / sizeof(wchar_t));
const LONG result = RegQueryValueExW(key, value_name, nullptr, nullptr, reinterpret_cast<BYTE*>(value.data()), &size);
RegCloseKey(key);
if (result != ERROR_SUCCESS) return false;
while (!value.empty() && value.back() == L'\0') value.pop_back();
return !value.empty();
}
bool RecoveryRecordExists() {
HKEY key;
if (RegOpenKeyExW(HKEY_CURRENT_USER, kRegistryPath, 0, KEY_QUERY_VALUE, &key) != ERROR_SUCCESS) {
return false;
}
bool exists = false;
for (const wchar_t* value_name :
{kRegVersion,
kRegModeDeviceName,
kRegLegacyDeviceName,
kRegHDRDeviceName,
kRegOriginalRefreshRate,
kRegOriginalWidth,
kRegOriginalHeight,
kRegOriginalHDR,
kRegModeChanged,
kRegHDRChanged,
kRegModeTakeoverEligible,
kRegHDRTakeoverEligible,
kRegModeRecoverySlot,
kRegHDRRecoverySlot,
kRegModeDeviceNameAlternate,
kRegHDRDeviceNameAlternate,
kRegOriginalRefreshRateAlternate,
kRegOriginalWidthAlternate,
kRegOriginalHeightAlternate,
kRegOriginalHDRAlternate,
kRegModeHandoffPending,
kRegHDRHandoffPending,
kRegModePreviousRecoverySlot,
kRegHDRPreviousRecoverySlot}) {
DWORD size = 0;
const LONG result = RegQueryValueExW(key, value_name, nullptr, nullptr, nullptr, &size);
if (result == ERROR_SUCCESS || result == ERROR_MORE_DATA) {
exists = true;
break;
}
}
RegCloseKey(key);
return exists;
}
} // namespace
class Win32DisplayRecoveryBackend final : public DisplayRecoveryBackend {
public:
bool RecordExists() const override { return RecoveryRecordExists(); }
bool ReadDWORD(const wchar_t* value_name, DWORD& value) override { return ReadRegistryDWORD(value_name, value); }
bool ReadString(const wchar_t* value_name, std::wstring& value) override {
return ReadRegistryString(value_name, value);
}
bool WriteDWORD(const wchar_t* value_name, DWORD value) override { return WriteRegistryDWORD(value_name, value); }
bool WriteString(const wchar_t* value_name, const std::wstring& value) override {
return WriteRegistryString(value_name, value);
}
bool IsDevicePresent(const std::wstring& device_name) const override {
DEVMODEW mode = {};
mode.dmSize = sizeof(mode);
return EnumDisplaySettingsW(device_name.c_str(), ENUM_CURRENT_SETTINGS, &mode) != FALSE;
}
bool RestoreMode(const std::wstring& device_name, DWORD width, DWORD height, DWORD refresh_rate) override {
// The recorded DEVMODE cannot express a "Dynamic" refresh-rate selection,
// so prefer re-applying the persisted display configuration, which the
// overrides never replace. On a DRR display the recorded rate is the DRR
// base rate — exactly what the restored configuration reports through the
// legacy API — so the verification below accepts it. A mismatch means the
// database no longer holds the recorded original (e.g. a crash inside the
// 24/48/60Hz registry-workaround window) and the explicit legacy restore
// takes over.
if (ApplyDatabaseCurrentConfig()) {
DEVMODEW current = {};
current.dmSize = sizeof(current);
if (EnumDisplaySettingsW(device_name.c_str(), ENUM_CURRENT_SETTINGS, &current) && current.dmPelsWidth == width &&
current.dmPelsHeight == height && current.dmDisplayFrequency == refresh_rate) {
return true;
}
}
DEVMODEW dm = {};
dm.dmSize = sizeof(dm);
dm.dmPelsWidth = width;
dm.dmPelsHeight = height;
dm.dmDisplayFrequency = refresh_rate;
dm.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFREQUENCY;
return ChangeDisplaySettingsExW(device_name.c_str(), &dm, nullptr, CDS_FULLSCREEN, nullptr) ==
DISP_CHANGE_SUCCESSFUL;
}
bool RestoreHDR(const std::wstring& device_name, bool enabled) override {
const auto target_id = DisplayModeManager::GetDisplayTargetId(device_name);
if (!target_id) return false;
// Save the display topology before the toggle; the CCD re-apply keeps a
// Dynamic Refresh Rate selection intact where the DEVMODE re-apply would
// pin the DRR base rate (issue #2055).
const DisplayConfigSnapshot pre_toggle_config = CaptureDisplayConfigSnapshot();
DEVMODEW pre_toggle_mode = {};
pre_toggle_mode.dmSize = sizeof(pre_toggle_mode);
EnumDisplaySettingsW(device_name.c_str(), ENUM_CURRENT_SETTINGS, &pre_toggle_mode);
if (DisplayModeManager::SetHDRStateForTarget(*target_id, enabled) != ERROR_SUCCESS) return false;
if (ApplyDisplayConfigSnapshot(pre_toggle_config, /*save_to_database=*/false)) return true;
if (pre_toggle_mode.dmDisplayFrequency != 0) {
pre_toggle_mode.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFREQUENCY | DM_DISPLAYFLAGS;
if (ChangeDisplaySettingsExW(device_name.c_str(), &pre_toggle_mode, nullptr, CDS_FULLSCREEN, nullptr) !=
DISP_CHANGE_SUCCESSFUL) {
return false;
}
}
return true;
}
bool ClearMarker(const wchar_t* value_name) override { return WriteRegistryDWORD(value_name, 0); }
bool DeleteRecord() override {
HKEY key;
if (RegOpenKeyExW(HKEY_CURRENT_USER, kRegistryPath, 0, KEY_SET_VALUE, &key) != ERROR_SUCCESS) {
return !RecordExists();
}
bool deleted = true;
for (const wchar_t* value_name :
{kRegVersion,
kRegModeDeviceName,
kRegLegacyDeviceName,
kRegHDRDeviceName,
kRegOriginalRefreshRate,
kRegOriginalWidth,
kRegOriginalHeight,
kRegOriginalHDR,
kRegModeChanged,
kRegHDRChanged,
kRegModeTakeoverEligible,
kRegHDRTakeoverEligible,
kRegModeRecoverySlot,
kRegHDRRecoverySlot,
kRegModeDeviceNameAlternate,
kRegHDRDeviceNameAlternate,
kRegOriginalRefreshRateAlternate,
kRegOriginalWidthAlternate,
kRegOriginalHeightAlternate,
kRegOriginalHDRAlternate,
kRegModeHandoffPending,
kRegHDRHandoffPending,
kRegModePreviousRecoverySlot,
kRegHDRPreviousRecoverySlot}) {
const LONG result = RegDeleteValueW(key, value_name);
deleted = deleted && (result == ERROR_SUCCESS || result == ERROR_FILE_NOT_FOUND);
}
RegCloseKey(key);
return deleted;
}
};
namespace {
bool ReadValidModeValues(
DisplayRecoveryBackend& backend, const wchar_t* device_value_name, std::wstring& device_name, DWORD& width,
DWORD& height, DWORD& refresh_rate, bool alternate = false) {
const wchar_t* width_value_name = alternate ? kRegOriginalWidthAlternate : kRegOriginalWidth;
const wchar_t* height_value_name = alternate ? kRegOriginalHeightAlternate : kRegOriginalHeight;
const wchar_t* refresh_value_name = alternate ? kRegOriginalRefreshRateAlternate : kRegOriginalRefreshRate;
return backend.ReadString(device_value_name, device_name) && backend.ReadDWORD(width_value_name, width) &&
width > 0 && backend.ReadDWORD(height_value_name, height) && height > 0 &&
backend.ReadDWORD(refresh_value_name, refresh_rate) && refresh_rate > 0;
}
bool ReadValidHDRValues(
DisplayRecoveryBackend& backend, const wchar_t* device_value_name, std::wstring& device_name, DWORD& original_hdr,
bool alternate = false) {
const wchar_t* original_value_name = alternate ? kRegOriginalHDRAlternate : kRegOriginalHDR;
return backend.ReadString(device_value_name, device_name) && backend.ReadDWORD(original_value_name, original_hdr) &&
original_hdr <= 1;
}
bool ReadRecoverySlot(DisplayRecoveryBackend& backend, const wchar_t* slot_value_name, bool& alternate) {
DWORD slot = 0;
if (!backend.ReadDWORD(slot_value_name, slot)) {
// Version-1 records created before alternate slots implicitly use the
// original value set.
alternate = false;
return true;
}
if (slot > 1) return false;
alternate = slot == 1;
return true;
}
bool ReadValidModeSlot(
DisplayRecoveryBackend& backend, bool alternate, std::wstring& device_name, DWORD& width, DWORD& height,
DWORD& refresh_rate) {
return ReadValidModeValues(
backend, alternate ? kRegModeDeviceNameAlternate : kRegModeDeviceName, device_name, width, height, refresh_rate,
alternate);
}
bool ReadValidHDRSlot(DisplayRecoveryBackend& backend, bool alternate, std::wstring& device_name, DWORD& original_hdr) {
return ReadValidHDRValues(
backend, alternate ? kRegHDRDeviceNameAlternate : kRegHDRDeviceName, device_name, original_hdr, alternate);
}
bool ReadValidMarkedMode(
DisplayRecoveryBackend& backend, std::wstring& device_name, DWORD& width, DWORD& height, DWORD& refresh_rate,
bool* alternate_slot = nullptr) {
DWORD version = 0;
DWORD marker = 0;
bool alternate = false;
if (!backend.ReadDWORD(kRegVersion, version) || version != kRecoveryVersion ||
!backend.ReadDWORD(kRegModeChanged, marker) || marker != 1 ||
!ReadRecoverySlot(backend, kRegModeRecoverySlot, alternate) ||
!ReadValidModeSlot(backend, alternate, device_name, width, height, refresh_rate)) {
return false;
}
if (alternate_slot) *alternate_slot = alternate;
return true;
}
bool ReadValidMarkedHDR(
DisplayRecoveryBackend& backend, std::wstring& device_name, DWORD& original_hdr, bool* alternate_slot = nullptr) {
DWORD version = 0;
DWORD marker = 0;
bool alternate = false;
if (!backend.ReadDWORD(kRegVersion, version) || version != kRecoveryVersion ||
!backend.ReadDWORD(kRegHDRChanged, marker) || marker != 1 ||
!ReadRecoverySlot(backend, kRegHDRRecoverySlot, alternate) ||
!ReadValidHDRSlot(backend, alternate, device_name, original_hdr)) {
return false;
}
if (alternate_slot) *alternate_slot = alternate;
return true;
}
bool DeleteRecordIfNoMarkedOperations(DisplayRecoveryBackend& backend) {
DWORD mode_marker = 0;
DWORD hdr_marker = 0;
if (!backend.ReadDWORD(kRegModeChanged, mode_marker) || !backend.ReadDWORD(kRegHDRChanged, hdr_marker) ||
mode_marker != 0 || hdr_marker != 0) {
// Missing, malformed, or active evidence is retained conservatively.
return false;
}
// Deletion is best effort after both operation markers are durably clear.
backend.DeleteRecord();
return true;
}
bool IsTakeoverEligible(DisplayRecoveryBackend& backend, const wchar_t* takeover_disposition) {
DWORD value = 0;
return backend.ReadDWORD(takeover_disposition, value) && value == 1;
}
bool MarkTakeoverEligible(DisplayRecoveryBackend& backend, const wchar_t* takeover_disposition) {
return backend.WriteDWORD(takeover_disposition, 1);
}
bool ResetTakeoverEligibility(DisplayRecoveryBackend& backend, const wchar_t* takeover_disposition) {
return backend.WriteDWORD(takeover_disposition, 0);
}
bool ReadHandoffPending(DisplayRecoveryBackend& backend, const wchar_t* handoff_value_name, bool& pending) {
DWORD value = 0;
if (!backend.ReadDWORD(handoff_value_name, value)) {
pending = false;
return true;
}
if (value > 1) return false;
pending = value == 1;
return true;
}
bool ReadStoredRecoverySlot(DisplayRecoveryBackend& backend, const wchar_t* slot_value_name, bool& alternate) {
DWORD value = 0;
if (!backend.ReadDWORD(slot_value_name, value) || value > 1) return false;
alternate = value == 1;
return true;
}
bool CompleteRecoveryOperation(
DisplayRecoveryBackend& backend, const wchar_t* marker, const wchar_t* takeover_disposition,
const wchar_t* handoff_pending) {
// Handoff metadata must be inactive before its shared marker disappears.
if (!backend.WriteDWORD(handoff_pending, 0) || !ResetTakeoverEligibility(backend, takeover_disposition) ||
!backend.ClearMarker(marker)) {
return false;
}
DeleteRecordIfNoMarkedOperations(backend);
return true;
}
bool ConfirmPreparedRecovery(DisplayRecoveryBackend& backend, const wchar_t* handoff_pending) {
bool pending = false;
if (!ReadHandoffPending(backend, handoff_pending, pending)) return false;
return !pending || backend.WriteDWORD(handoff_pending, 0);
}
bool RollbackRecoveryHandoff(
DisplayRecoveryBackend& backend, const wchar_t* takeover_disposition, const wchar_t* recovery_slot,
const wchar_t* previous_recovery_slot, const wchar_t* handoff_pending) {
bool previous_alternate = false;
if (!ReadStoredRecoverySlot(backend, previous_recovery_slot, previous_alternate) ||
!backend.WriteDWORD(recovery_slot, previous_alternate ? 1 : 0) ||
!MarkTakeoverEligible(backend, takeover_disposition) || !backend.WriteDWORD(handoff_pending, 0)) {
return false;
}
return true;
}
bool FinalizePreparedRecovery(
DisplayRecoveryBackend& backend, bool os_apply_succeeded, const wchar_t* marker,
const wchar_t* takeover_disposition, const wchar_t* recovery_slot, const wchar_t* previous_recovery_slot,
const wchar_t* handoff_pending) {
if (os_apply_succeeded) return ConfirmPreparedRecovery(backend, handoff_pending);
bool pending = false;
if (!ReadHandoffPending(backend, handoff_pending, pending)) return false;
if (pending) {
return RollbackRecoveryHandoff(
backend, takeover_disposition, recovery_slot, previous_recovery_slot, handoff_pending);
}
return CompleteRecoveryOperation(backend, marker, takeover_disposition, handoff_pending);
}
bool PreserveValidModeSiblingOrClear(DisplayRecoveryBackend& backend) {
DWORD marker = 0;
if (!backend.ReadDWORD(kRegModeChanged, marker)) {
return backend.WriteDWORD(kRegModeChanged, 0);
}
if (marker == 0) return true;
std::wstring device_name;
DWORD width = 0;
DWORD height = 0;
DWORD refresh_rate = 0;
if (marker == 1 && ReadValidMarkedMode(backend, device_name, width, height, refresh_rate)) {
return true;
}
return backend.ClearMarker(kRegModeChanged);
}
bool PreserveValidHDRSiblingOrClear(DisplayRecoveryBackend& backend) {
DWORD marker = 0;
if (!backend.ReadDWORD(kRegHDRChanged, marker)) {
return backend.WriteDWORD(kRegHDRChanged, 0);
}
if (marker == 0) return true;
std::wstring device_name;
DWORD original_hdr = 0;
if (marker == 1 && ReadValidMarkedHDR(backend, device_name, original_hdr)) {
return true;
}
return backend.ClearMarker(kRegHDRChanged);
}
} // namespace
bool DisplayModeManager::PrepareModeRecovery(
DisplayRecoveryBackend& backend, const std::wstring& device_name, DWORD width, DWORD height, DWORD refresh_rate) {
if (device_name.empty() || width == 0 || height == 0 || refresh_rate == 0) return false;
if (!PreserveValidHDRSiblingOrClear(backend)) return false;
DWORD existing_width = 0;
DWORD existing_height = 0;
DWORD existing_refresh_rate = 0;
std::wstring existing_device_name;
bool existing_alternate = false;
if (ReadValidMarkedMode(
backend, existing_device_name, existing_width, existing_height, existing_refresh_rate, &existing_alternate)) {
bool handoff_pending = false;
if (!ReadHandoffPending(backend, kRegModeHandoffPending, handoff_pending) || handoff_pending) return false;
const bool same_original = existing_device_name == device_name && existing_width == width &&
existing_height == height && existing_refresh_rate == refresh_rate;
if (same_original) {
// Reusing the restoration point makes it live again, so stale takeover
// permission must be durably revoked before the Windows mutation.
return ResetTakeoverEligibility(backend, kRegModeTakeoverEligible);
}
if (!IsTakeoverEligible(backend, kRegModeTakeoverEligible)) return false;
// Stage the replacement in the inactive slot. Handoff metadata keeps both
// originals recoverable from the selector switch until the OS apply is
// confirmed.
const bool replacement_alternate = !existing_alternate;
const wchar_t* replacement_device_name = replacement_alternate ? kRegModeDeviceNameAlternate : kRegModeDeviceName;
const wchar_t* replacement_width = replacement_alternate ? kRegOriginalWidthAlternate : kRegOriginalWidth;
const wchar_t* replacement_height = replacement_alternate ? kRegOriginalHeightAlternate : kRegOriginalHeight;
const wchar_t* replacement_refresh =
replacement_alternate ? kRegOriginalRefreshRateAlternate : kRegOriginalRefreshRate;
if (!backend.WriteDWORD(kRegVersion, kRecoveryVersion) ||
!backend.WriteString(replacement_device_name, device_name) || !backend.WriteDWORD(replacement_width, width) ||
!backend.WriteDWORD(replacement_height, height) || !backend.WriteDWORD(replacement_refresh, refresh_rate) ||
!backend.WriteDWORD(kRegModePreviousRecoverySlot, existing_alternate ? 1 : 0) ||
!ResetTakeoverEligibility(backend, kRegModeTakeoverEligible)) {
return false;
}
if (!backend.WriteDWORD(kRegModeHandoffPending, 1)) {
MarkTakeoverEligible(backend, kRegModeTakeoverEligible);
return false;
}
if (!backend.WriteDWORD(kRegModeRecoverySlot, replacement_alternate ? 1 : 0)) {
RollbackRecoveryHandoff(
backend, kRegModeTakeoverEligible, kRegModeRecoverySlot, kRegModePreviousRecoverySlot,
kRegModeHandoffPending);
return false;
}
return true;
}
// An incomplete operation has no authoritative original to preserve. Keep
// the existing marker-last preparation sequence and select the primary slot
// before publishing the new operation.
if (!backend.ClearMarker(kRegModeChanged)) return false;
return backend.WriteDWORD(kRegVersion, kRecoveryVersion) && backend.WriteString(kRegModeDeviceName, device_name) &&
backend.WriteDWORD(kRegOriginalWidth, width) && backend.WriteDWORD(kRegOriginalHeight, height) &&
backend.WriteDWORD(kRegOriginalRefreshRate, refresh_rate) && backend.WriteDWORD(kRegModeHandoffPending, 0) &&
ResetTakeoverEligibility(backend, kRegModeTakeoverEligible) && backend.WriteDWORD(kRegModeRecoverySlot, 0) &&
backend.WriteDWORD(kRegModeChanged, 1);
}
bool DisplayModeManager::PrepareHDRRecovery(
DisplayRecoveryBackend& backend, const std::wstring& device_name, bool enabled) {
if (device_name.empty()) return false;
if (!PreserveValidModeSiblingOrClear(backend)) return false;
DWORD existing_original = 0;
std::wstring existing_device_name;
bool existing_alternate = false;
if (ReadValidMarkedHDR(backend, existing_device_name, existing_original, &existing_alternate)) {
bool handoff_pending = false;
if (!ReadHandoffPending(backend, kRegHDRHandoffPending, handoff_pending) || handoff_pending) return false;
const bool same_original = existing_device_name == device_name && existing_original == (enabled ? 1u : 0u);
if (same_original) return ResetTakeoverEligibility(backend, kRegHDRTakeoverEligible);
if (!IsTakeoverEligible(backend, kRegHDRTakeoverEligible)) return false;
const bool replacement_alternate = !existing_alternate;
const wchar_t* replacement_device_name = replacement_alternate ? kRegHDRDeviceNameAlternate : kRegHDRDeviceName;
const wchar_t* replacement_original = replacement_alternate ? kRegOriginalHDRAlternate : kRegOriginalHDR;
if (!backend.WriteDWORD(kRegVersion, kRecoveryVersion) ||
!backend.WriteString(replacement_device_name, device_name) ||
!backend.WriteDWORD(replacement_original, enabled ? 1 : 0) ||
!backend.WriteDWORD(kRegHDRPreviousRecoverySlot, existing_alternate ? 1 : 0) ||
!ResetTakeoverEligibility(backend, kRegHDRTakeoverEligible)) {
return false;
}
if (!backend.WriteDWORD(kRegHDRHandoffPending, 1)) {
MarkTakeoverEligible(backend, kRegHDRTakeoverEligible);
return false;
}
if (!backend.WriteDWORD(kRegHDRRecoverySlot, replacement_alternate ? 1 : 0)) {
RollbackRecoveryHandoff(
backend, kRegHDRTakeoverEligible, kRegHDRRecoverySlot, kRegHDRPreviousRecoverySlot, kRegHDRHandoffPending);
return false;
}
return true;
}
if (!backend.ClearMarker(kRegHDRChanged)) return false;
return backend.WriteDWORD(kRegVersion, kRecoveryVersion) && backend.WriteString(kRegHDRDeviceName, device_name) &&
backend.WriteDWORD(kRegOriginalHDR, enabled ? 1 : 0) && backend.WriteDWORD(kRegHDRHandoffPending, 0) &&
ResetTakeoverEligibility(backend, kRegHDRTakeoverEligible) && backend.WriteDWORD(kRegHDRRecoverySlot, 0) &&
backend.WriteDWORD(kRegHDRChanged, 1);
}
namespace {
bool PrepareModeRecoveryAtRegistry(const std::wstring& device_name, DWORD width, DWORD height, DWORD refresh_rate) {
Win32DisplayRecoveryBackend backend;
return DisplayModeManager::PrepareModeRecovery(backend, device_name, width, height, refresh_rate);
}
bool PrepareHDRRecoveryAtRegistry(const std::wstring& device_name, bool enabled) {
Win32DisplayRecoveryBackend backend;
return DisplayModeManager::PrepareHDRRecovery(backend, device_name, enabled);
}
bool CompleteRecoveryOperationAtRegistry(
const wchar_t* marker, const wchar_t* takeover_disposition, const wchar_t* handoff_pending) {
Win32DisplayRecoveryBackend backend;
return CompleteRecoveryOperation(backend, marker, takeover_disposition, handoff_pending);
}
bool FinalizeModeRecoveryAtRegistry(bool os_apply_succeeded) {
Win32DisplayRecoveryBackend backend;
return FinalizePreparedRecovery(
backend, os_apply_succeeded, kRegModeChanged, kRegModeTakeoverEligible, kRegModeRecoverySlot,
kRegModePreviousRecoverySlot, kRegModeHandoffPending);
}
bool FinalizeHDRRecoveryAtRegistry(bool os_apply_succeeded) {
Win32DisplayRecoveryBackend backend;
return FinalizePreparedRecovery(
backend, os_apply_succeeded, kRegHDRChanged, kRegHDRTakeoverEligible, kRegHDRRecoverySlot,
kRegHDRPreviousRecoverySlot, kRegHDRHandoffPending);
}
bool MarkTakeoverEligibleAtRegistry(const wchar_t* takeover_disposition) {
Win32DisplayRecoveryBackend backend;
return MarkTakeoverEligible(backend, takeover_disposition);
}
bool RecoverRecord(DisplayRecoveryBackend& backend, bool mode_is_live, bool hdr_is_live) {
if (!backend.RecordExists()) return false;
DWORD version = 0;
const bool has_version = backend.ReadDWORD(kRegVersion, version);
const wchar_t* mode_device_value_name = kRegModeDeviceName;
const wchar_t* hdr_device_value_name = kRegHDRDeviceName;
if (has_version) {
if (version != kRecoveryVersion) {
backend.DeleteRecord();
return false;
}
} else {
// The released layout had no Version and shared one DeviceName between
// mode and HDR. Require that discriminator before interpreting any
// versionless values as recovery evidence.
std::wstring legacy_device_name;
if (!backend.ReadString(kRegLegacyDeviceName, legacy_device_name)) {
backend.DeleteRecord();
return false;
}
mode_device_value_name = kRegLegacyDeviceName;
hdr_device_value_name = kRegLegacyDeviceName;
}
DWORD mode_marker = 0;
const bool mode_marker_read = backend.ReadDWORD(kRegModeChanged, mode_marker);
std::wstring mode_device_name;
DWORD width = 0;
DWORD height = 0;
DWORD refresh_rate = 0;
bool mode_handoff_pending = false;
std::wstring previous_mode_device_name;
DWORD previous_width = 0;
DWORD previous_height = 0;
DWORD previous_refresh_rate = 0;
bool mode_requested = false;
if (mode_marker_read && mode_marker == 1) {
if (has_version) {
bool handoff_state_valid = ReadHandoffPending(backend, kRegModeHandoffPending, mode_handoff_pending);
if (handoff_state_valid && mode_handoff_pending) {
bool previous_alternate = false;
handoff_state_valid =
ReadStoredRecoverySlot(backend, kRegModePreviousRecoverySlot, previous_alternate) &&
ReadValidModeSlot(backend, !previous_alternate, mode_device_name, width, height, refresh_rate) &&
ReadValidModeSlot(
backend, previous_alternate, previous_mode_device_name, previous_width, previous_height,
previous_refresh_rate);
} else if (handoff_state_valid) {
handoff_state_valid = ReadValidMarkedMode(backend, mode_device_name, width, height, refresh_rate);
}
mode_requested = handoff_state_valid;
} else {
mode_requested =
ReadValidModeValues(backend, mode_device_value_name, mode_device_name, width, height, refresh_rate);
}
}
if (!mode_is_live && (!mode_marker_read || mode_marker > 1 || (mode_marker == 1 && !mode_requested))) {
// Malformation in one operation does not erase a valid or live sibling.
backend.ClearMarker(kRegModeChanged);
}
DWORD hdr_marker = 0;
const bool hdr_marker_read = backend.ReadDWORD(kRegHDRChanged, hdr_marker);
std::wstring hdr_device_name;
DWORD original_hdr = 0;
bool hdr_handoff_pending = false;
std::wstring previous_hdr_device_name;
DWORD previous_original_hdr = 0;
bool hdr_requested = false;
if (hdr_marker_read && hdr_marker == 1) {
if (has_version) {
bool handoff_state_valid = ReadHandoffPending(backend, kRegHDRHandoffPending, hdr_handoff_pending);
if (handoff_state_valid && hdr_handoff_pending) {
bool previous_alternate = false;
handoff_state_valid =
ReadStoredRecoverySlot(backend, kRegHDRPreviousRecoverySlot, previous_alternate) &&
ReadValidHDRSlot(backend, !previous_alternate, hdr_device_name, original_hdr) &&
ReadValidHDRSlot(backend, previous_alternate, previous_hdr_device_name, previous_original_hdr);
} else if (handoff_state_valid) {
handoff_state_valid = ReadValidMarkedHDR(backend, hdr_device_name, original_hdr);
}
hdr_requested = handoff_state_valid;
} else {
hdr_requested = ReadValidHDRValues(backend, hdr_device_value_name, hdr_device_name, original_hdr);
}
}
if (!hdr_is_live && (!hdr_marker_read || hdr_marker > 1 || (hdr_marker == 1 && !hdr_requested))) {
backend.ClearMarker(kRegHDRChanged);
}
const bool recover_mode = mode_requested && !mode_is_live;
const bool recover_hdr = hdr_requested && !hdr_is_live;
if (!recover_mode && !recover_hdr) {
DeleteRecordIfNoMarkedOperations(backend);
return false;
}
bool completed = true;
if (recover_mode) {
const bool restored =
backend.IsDevicePresent(mode_device_name) && backend.RestoreMode(mode_device_name, width, height, refresh_rate);
if (mode_handoff_pending) {
// Restore the replacement first and the pre-handoff original last. If
// either restore or cleanup fails, collapse authority back to the prior
// slot so topology retries do not keep forcing the surviving replacement.
const bool previous_restored =
backend.IsDevicePresent(previous_mode_device_name) &&
backend.RestoreMode(previous_mode_device_name, previous_width, previous_height, previous_refresh_rate);
if (!restored || !previous_restored ||
!CompleteRecoveryOperation(backend, kRegModeChanged, kRegModeTakeoverEligible, kRegModeHandoffPending)) {
RollbackRecoveryHandoff(
backend, kRegModeTakeoverEligible, kRegModeRecoverySlot, kRegModePreviousRecoverySlot,
kRegModeHandoffPending);
completed = false;
}
} else if (
!restored ||
!CompleteRecoveryOperation(backend, kRegModeChanged, kRegModeTakeoverEligible, kRegModeHandoffPending)) {
// Retain the restoration point for topology retries, but remember that
// one real non-live recovery attempt failed so a later conflicting mode
// request may deliberately replace it.
MarkTakeoverEligible(backend, kRegModeTakeoverEligible);
completed = false;
}
}
if (recover_hdr) {
const bool restored =
backend.IsDevicePresent(hdr_device_name) && backend.RestoreHDR(hdr_device_name, original_hdr != 0);
if (hdr_handoff_pending) {
const bool previous_restored = backend.IsDevicePresent(previous_hdr_device_name) &&
backend.RestoreHDR(previous_hdr_device_name, previous_original_hdr != 0);
if (!restored || !previous_restored ||
!CompleteRecoveryOperation(backend, kRegHDRChanged, kRegHDRTakeoverEligible, kRegHDRHandoffPending)) {
RollbackRecoveryHandoff(
backend, kRegHDRTakeoverEligible, kRegHDRRecoverySlot, kRegHDRPreviousRecoverySlot, kRegHDRHandoffPending);
completed = false;
}
} else if (
!restored ||
!CompleteRecoveryOperation(backend, kRegHDRChanged, kRegHDRTakeoverEligible, kRegHDRHandoffPending)) {
MarkTakeoverEligible(backend, kRegHDRTakeoverEligible);
completed = false;
}
}
return completed;
}
} // namespace
bool DisplayModeManager::RecoverIfNeeded() {
std::lock_guard<std::recursive_mutex> transaction_lock(g_display_override_mutex);
RecoveryRunGuard run;
if (!run.acquired()) return false;
Win32DisplayRecoveryBackend backend;
return RecoverRecord(backend, g_live_mode_recovery_record, g_live_hdr_recovery_record);
}
bool DisplayModeManager::RecoverIfNeeded(DisplayRecoveryBackend& backend) {
std::lock_guard<std::recursive_mutex> transaction_lock(g_display_override_mutex);
RecoveryRunGuard run;
if (!run.acquired()) return false;
return RecoverRecord(backend, false, false);
}
#if defined(PLEZY_DISPLAY_MODE_MANAGER_TESTING)
bool DisplayModeManager::CompleteRecoveryOperationForTesting(DisplayRecoveryBackend& backend, bool mode) {
std::lock_guard<std::recursive_mutex> transaction_lock(g_display_override_mutex);
return CompleteRecoveryOperation(
backend, mode ? kRegModeChanged : kRegHDRChanged, mode ? kRegModeTakeoverEligible : kRegHDRTakeoverEligible,
mode ? kRegModeHandoffPending : kRegHDRHandoffPending);
}
bool DisplayModeManager::FinalizePreparedRecoveryForTesting(
DisplayRecoveryBackend& backend, bool mode, bool os_apply_succeeded) {
std::lock_guard<std::recursive_mutex> transaction_lock(g_display_override_mutex);
return FinalizePreparedRecovery(
backend, os_apply_succeeded, mode ? kRegModeChanged : kRegHDRChanged,
mode ? kRegModeTakeoverEligible : kRegHDRTakeoverEligible, mode ? kRegModeRecoverySlot : kRegHDRRecoverySlot,
mode ? kRegModePreviousRecoverySlot : kRegHDRPreviousRecoverySlot,
mode ? kRegModeHandoffPending : kRegHDRHandoffPending);
}
bool DisplayModeManager::RecoverIfNeededForTesting(
DisplayRecoveryBackend& backend, bool mode_is_live, bool hdr_is_live) {
std::lock_guard<std::recursive_mutex> transaction_lock(g_display_override_mutex);
RecoveryRunGuard run;
if (!run.acquired()) return false;
return RecoverRecord(backend, mode_is_live, hdr_is_live);
}
#endif
} // namespace mpv