[fix] avoid blocking Windows shutdown and restore display modes

This commit is contained in:
dijunkun committed 2026-11-11 04:28:45 +08:00
1 parent e47a3ac65c
commit 3b60a32ba7
13 files changed
+273 -221

No files matched your search

@@ -0,0 +1,72 @@
#include "session_end_observer.h"
#include "rd_log.h"
namespace crossdesk {
namespace {
constexpr wchar_t kSessionWindowClass[] = L"CrossDesk.SessionEndObserver";
}
SessionEndObserver::~SessionEndObserver() {
if (window_) DestroyWindow(window_);
}
bool SessionEndObserver::ReportError(const char* operation) {
const DWORD error = GetLastError();
const ULONGLONG now = GetTickCount64();
if (last_error_tick_ == 0 || now - last_error_tick_ >= 5000) {
LOG_WARN("Windows session end observer {} failed, error={}", operation, error);
last_error_tick_ = now;
}
SetLastError(error);
return false;
}
bool SessionEndObserver::Start() {
if (window_) return true;
WNDCLASSW klass{};
klass.lpfnWndProc = WindowProc;
klass.hInstance = GetModuleHandleW(nullptr);
klass.lpszClassName = kSessionWindowClass;
if (!RegisterClassW(&klass) && GetLastError() != ERROR_CLASS_ALREADY_EXISTS)
return ReportError("register window");
// A hidden top-level window receives session-end broadcasts even when the
// Slint windows are in the tray. HWND_MESSAGE does not receive them.
window_ = CreateWindowExW(WS_EX_TOOLWINDOW, kSessionWindowClass, L"CrossDesk",
WS_OVERLAPPED, 0, 0, 0, 0, nullptr, nullptr, klass.hInstance, this);
if (!window_) return ReportError("create window");
return true;
}
LRESULT CALLBACK SessionEndObserver::WindowProc(
HWND window, UINT message, WPARAM wparam, LPARAM lparam) {
if (message == WM_NCCREATE) {
const auto* create = reinterpret_cast<CREATESTRUCTW*>(lparam);
auto* observer = static_cast<SessionEndObserver*>(create->lpCreateParams);
observer->window_ = window;
SetWindowLongPtrW(window, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(observer));
}
auto* observer = reinterpret_cast<SessionEndObserver*>(
GetWindowLongPtrW(window, GWLP_USERDATA));
if (observer && message == WM_QUERYENDSESSION) {
// Never stop work or veto shutdown here, including logoff/Restart Manager.
LOG_INFO("Windows shutdown query accepted: flags=0x{:x}",
static_cast<DWORD>(lparam));
return TRUE;
}
if (observer && message == WM_ENDSESSION) {
LOG_INFO("Windows session end: confirmed={}, flags=0x{:x}",
wparam != FALSE, static_cast<DWORD>(lparam));
// Let the GUI timer begin its normal cleanup only after confirmation.
// Do not wait for network/device threads inside a Windows message handler.
if (wparam != FALSE) observer->ending_ = true;
return 0;
}
if (observer && message == WM_NCDESTROY) {
observer->window_ = nullptr;
SetWindowLongPtrW(window, GWLP_USERDATA, 0);
}
return DefWindowProcW(window, message, wparam, lparam);
}
} // namespace crossdesk
@@ -0,0 +1,39 @@
/*
* @Author: DI JUNKUN
* @Date: 2026-11-11
* Copyright (c) 2026 by DI JUNKUN, All Rights Reserved.
*/
#ifndef _SESSION_END_OBSERVER_H_
#define _SESSION_END_OBSERVER_H_
#include <Windows.h>
namespace crossdesk {
// Owned and pumped by the UI thread. A query is permission to end the session,
// not a request to tear down transport/capture: another application may still
// cancel shutdown. This observer never registers a shutdown-blocking reason.
class SessionEndObserver {
public:
SessionEndObserver() = default;
~SessionEndObserver();
SessionEndObserver(const SessionEndObserver&) = delete;
SessionEndObserver& operator=(const SessionEndObserver&) = delete;
bool Start();
bool Ending() const { return ending_; }
private:
bool ReportError(const char* operation);
static LRESULT CALLBACK WindowProc(HWND window, UINT message,
WPARAM wparam, LPARAM lparam);
HWND window_ = nullptr;
bool ending_ = false;
ULONGLONG last_error_tick_ = 0;
};
} // namespace crossdesk
#endif
@@ -1,121 +0,0 @@
#include "shutdown_session_guard.h"
#include <utility>
#include "rd_log.h"
namespace crossdesk {
namespace {
constexpr wchar_t kShutdownWindowClass[] = L"CrossDesk.ShutdownSessionGuard";
}
ShutdownSessionGuard::ShutdownSessionGuard(
std::shared_ptr<std::atomic<bool>> connected)
: connected_(std::move(connected)) {}
ShutdownSessionGuard::~ShutdownSessionGuard() {
ClearReason();
if (window_) DestroyWindow(window_);
}
bool ShutdownSessionGuard::ReportError(const char* operation) {
const DWORD error = GetLastError();
const ULONGLONG now = GetTickCount64();
if (last_error_tick_ == 0 || now - last_error_tick_ >= 5000) {
LOG_WARN("Windows shutdown session guard {} failed, error={}", operation, error);
last_error_tick_ = now;
}
SetLastError(error);
return false;
}
bool ShutdownSessionGuard::Start() {
if (window_) return true;
WNDCLASSW klass{};
klass.lpfnWndProc = WindowProc;
klass.hInstance = GetModuleHandleW(nullptr);
klass.lpszClassName = kShutdownWindowClass;
if (!RegisterClassW(&klass) && GetLastError() != ERROR_CLASS_ALREADY_EXISTS)
return ReportError("register window");
// A top-level window receives session-end broadcasts even when the Slint
// windows are hidden in the tray. HWND_MESSAGE does not receive them.
window_ = CreateWindowExW(WS_EX_TOOLWINDOW, kShutdownWindowClass, L"CrossDesk",
WS_OVERLAPPED, 0, 0, 0, 0, nullptr, nullptr, klass.hInstance, this);
if (!window_) return ReportError("create window");
return true;
}
bool ShutdownSessionGuard::ClearReason() {
if (!reason_registered_) return true;
if (window_ && !ShutdownBlockReasonDestroy(window_))
return ReportError("clear reason");
reason_registered_ = false;
LOG_INFO("Windows shutdown session guard released");
return true;
}
bool ShutdownSessionGuard::SyncReason() {
if (shutdown_allowed_ || !connected_ ||
!connected_->load(std::memory_order_relaxed)) {
return ClearReason();
}
if (reason_registered_) return true;
if (!ShutdownBlockReasonCreate(window_, reason_.c_str()))
return ReportError("register reason");
reason_registered_ = true;
LOG_INFO("Windows shutdown session guard armed for connected controller");
return true;
}
bool ShutdownSessionGuard::Update(std::wstring_view reason) {
if (!Start()) return false;
if (std::wstring_view(reason_) != reason) {
// Keep the old registration and text on failure so the next tick retries
// removal before registering the newly selected language.
if (!ClearReason()) return false;
reason_.assign(reason);
}
return SyncReason();
}
LRESULT CALLBACK ShutdownSessionGuard::WindowProc(
HWND window, UINT message, WPARAM wparam, LPARAM lparam) {
if (message == WM_NCCREATE) {
const auto* create = reinterpret_cast<CREATESTRUCTW*>(lparam);
auto* guard = static_cast<ShutdownSessionGuard*>(create->lpCreateParams);
guard->window_ = window;
SetWindowLongPtrW(window, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(guard));
}
auto* guard = reinterpret_cast<ShutdownSessionGuard*>(
GetWindowLongPtrW(window, GWLP_USERDATA));
if (guard && message == WM_QUERYENDSESSION) {
const auto flags = static_cast<DWORD>(lparam);
// "Shut down anyway", logoff and Restart Manager must remain available.
if ((flags & (ENDSESSION_CRITICAL | ENDSESSION_LOGOFF | ENDSESSION_CLOSEAPP)) != 0)
guard->shutdown_allowed_ = true;
guard->SyncReason();
const bool retain = !guard->shutdown_allowed_ && guard->reason_registered_ &&
guard->connected_ && guard->connected_->load(std::memory_order_relaxed);
LOG_INFO("Windows shutdown query: flags=0x{:x}, retain_remote_session={}", flags, retain);
return retain ? FALSE : TRUE;
}
if (guard && message == WM_ENDSESSION) {
LOG_INFO("Windows session end: confirmed={}, flags=0x{:x}",
wparam != FALSE, static_cast<DWORD>(lparam));
guard->shutdown_allowed_ = wparam != FALSE;
guard->SyncReason();
// A cancelled shutdown must leave transport, capture and input running.
return 0;
}
if (guard && message == WM_NCDESTROY) {
guard->ClearReason();
// Destruction removes the native registration even if explicit removal
// failed. A later window must not inherit the destroyed window's state.
guard->reason_registered_ = false;
guard->window_ = nullptr;
SetWindowLongPtrW(window, GWLP_USERDATA, 0);
}
return DefWindowProcW(window, message, wparam, lparam);
}
} // namespace crossdesk
@@ -1,48 +0,0 @@
/*
* @Author: DI JUNKUN
* @Date: 2026-10-22
* Copyright (c) 2026 by DI JUNKUN, All Rights Reserved.
*/
#ifndef _SHUTDOWN_SESSION_GUARD_H_
#define _SHUTDOWN_SESSION_GUARD_H_
#include <Windows.h>
#include <atomic>
#include <memory>
#include <string>
#include <string_view>
namespace crossdesk {
// Owned and pumped by the UI thread. The transport may revoke the shared
// connection flag from another thread, including between UI ticks.
class ShutdownSessionGuard {
public:
explicit ShutdownSessionGuard(std::shared_ptr<std::atomic<bool>> connected);
~ShutdownSessionGuard();
ShutdownSessionGuard(const ShutdownSessionGuard&) = delete;
ShutdownSessionGuard& operator=(const ShutdownSessionGuard&) = delete;
bool Update(std::wstring_view reason);
private:
bool Start();
bool SyncReason();
bool ClearReason();
bool ReportError(const char* operation);
static LRESULT CALLBACK WindowProc(HWND window, UINT message,
WPARAM wparam, LPARAM lparam);
std::shared_ptr<std::atomic<bool>> connected_;
HWND window_ = nullptr;
std::wstring reason_;
bool reason_registered_ = false;
bool shutdown_allowed_ = false;
ULONGLONG last_error_tick_ = 0;
};
} // namespace crossdesk
#endif
@@ -425,17 +425,22 @@ void ScreenCapturerWin::UpdateNativeCursor(ScreenCapturer* backend) {
}
bool ScreenCapturerWin::PrepareUnattendedDisplay() {
if (virtual_display_.active() || DetectHeadlessDesktop()) {
physical_mode_recovery_pending_ = true;
physical_mode_restore_attempts_ = 0;
}
if (!ForceHeadlessEnabled() && unattended_host_ && virtual_display_.active() &&
QueryAttachedPhysicalDisplayMode().width > 0) {
// The monitor may have returned between controller connections.
virtual_display_.Release();
RestorePhysicalDisplayMode();
return false;
}
if (!unattended_host_ ||
(!virtual_display_.active() && !DetectHeadlessDesktop())) return false;
virtual_display_.PrepareAcquire();
std::string error;
if (!virtual_display_.Acquire(ResolveVirtualDisplayMode(), &error)) {
if (!virtual_display_.Acquire(ResolveVirtualDisplayMode(false), &error)) {
LOG_WARN("Cannot prepare unattended capture display: {}", error);
return false;
}
@@ -778,7 +783,7 @@ int ScreenCapturerWin::Start(bool show_cursor) {
if (running_.load(std::memory_order_relaxed)) {
return 0;
}
ResolveVirtualDisplayMode();
ResolveVirtualDisplayMode(!physical_mode_recovery_pending_ && physical_mode_restore_attempts_ == 0);
native_cursor_enabled_.store(native_cursor_default_.load());
NotifyPrivacyCapture(false);
@@ -870,7 +875,7 @@ void ScreenCapturerWin::BeginVirtualDisplayProvisioning() {
// A usbmmidd virtual monitor turns the phantom desktop into a real
// display target, so the GPU backends and a chosen resolution apply.
std::string error;
if (virtual_display_.Acquire(ResolveVirtualDisplayMode(), &error)) {
if (virtual_display_.Acquire(ResolveVirtualDisplayMode(false), &error)) {
virtual_display_ready_.store(true, std::memory_order_release);
} else if (error != "cancelled") {
LOG_WARN("Windows capturer: no usbmmidd virtual display ({}); "
@@ -1169,6 +1174,10 @@ void ScreenCapturerWin::CheckDisplayPresence(ULONGLONG now) {
const bool headless = DetectHeadlessDesktop(stalled,
topology_changed);
const bool owns_display = virtual_display_.active();
if (headless || owns_display) {
physical_mode_recovery_pending_ = true;
physical_mode_restore_attempts_ = 0;
}
const bool provisioning =
virtual_display_busy_.load(std::memory_order_acquire);
if (!provisioning) {
@@ -1181,7 +1190,8 @@ void ScreenCapturerWin::CheckDisplayPresence(ULONGLONG now) {
physical_binding_changed = !replacement_display_name_.empty() &&
replacement_display_name_ != GetDisplayLabel(physical_device);
}
if (physical_present && (owns_display || physical_binding_changed)) {
if (physical_present && (owns_display || physical_binding_changed ||
physical_mode_recovery_pending_)) {
// Debounce a hotplug before removing the fallback that is still carrying
// the stream. This runs on the management thread, never a frame callback.
if (physical_display_since_ == 0) physical_display_since_ = now;
@@ -1194,7 +1204,7 @@ void ScreenCapturerWin::CheckDisplayPresence(ULONGLONG now) {
physical_display_since_ = 0;
// Track user resolution changes during capture, not only process startup.
// Resolve ignores phantom, RDP and virtual-display clone modes.
ResolveVirtualDisplayMode();
ResolveVirtualDisplayMode(!physical_mode_recovery_pending_ && physical_mode_restore_attempts_ == 0);
}
bool target_ready = false;
if (owns_display) {
@@ -1222,6 +1232,23 @@ void ScreenCapturerWin::CheckDisplayPresence(ULONGLONG now) {
BeginVirtualDisplayProvisioning();
}
void ScreenCapturerWin::RestorePhysicalDisplayMode() {
if (!physical_mode_recovery_pending_ || ForceHeadlessEnabled()) return;
std::string error;
if (RestoreAttachedPhysicalDisplayModes(&error)) {
physical_mode_recovery_pending_ = false;
physical_mode_restore_attempts_ = 0;
} else {
++physical_mode_restore_attempts_;
LOG_WARN("Physical display mode recovery attempt {} failed: {}",
physical_mode_restore_attempts_, error);
// Retry transient EDID/topology failures, but do not continually interrupt
// capture or fight a user setting if a driver cannot apply its native mode.
if (physical_mode_restore_attempts_ >= 3)
physical_mode_recovery_pending_ = false;
}
}
void ScreenCapturerWin::RestorePhysicalDisplay() {
JoinVirtualDisplayThread();
if (!running_.load(std::memory_order_relaxed)) return;
@@ -1231,6 +1258,7 @@ void ScreenCapturerWin::RestorePhysicalDisplay() {
StopSecureDesktopSharedCapture(secure_shared_session_id_);
virtual_display_ready_.store(false, std::memory_order_release);
virtual_display_.Release();
RestorePhysicalDisplayMode();
// Windows may renumber outputs after the unplug. Requery and bind the real
// screen to the same registered wire slot, including multi-monitor hotplugs.
std::wstring device;
@@ -117,6 +117,10 @@ class ScreenCapturerWin : public ScreenCapturer {
std::atomic<bool> virtual_display_busy_{false};
ULONGLONG last_virtual_adoption_tick_ = 0;
ULONGLONG physical_display_since_ = 0;
// A phantom desktop can return on the same DISPLAYn without ever acquiring
// usbmmidd. Remember that transition independently of virtual ownership.
bool physical_mode_recovery_pending_ = false;
unsigned physical_mode_restore_attempts_ = 0;
// Protected by alias_mutex_. The virtual screen and then the returning
// physical screen replace the same active wire slot explicitly.
std::string replacement_display_name_;
@@ -154,6 +158,7 @@ class ScreenCapturerWin : public ScreenCapturer {
void CheckCaptureProgress(ULONGLONG now);
void CheckDisplayPresence(ULONGLONG now);
void RestorePhysicalDisplay();
void RestorePhysicalDisplayMode();
// Virtual display provisioning runs off the caller's thread.
void JoinVirtualDisplayThread();
void BeginVirtualDisplayProvisioning();
@@ -22,8 +22,9 @@ inline constexpr DWORD WindowsShutdownLevel(WindowsShutdownRole role) {
}
inline DWORD ConfigureWindowsShutdownOrder(WindowsShutdownRole role) {
// Change only the order. The GUI's session guard separately defers shutdown
// while a controller is connected; a confirmed shutdown still ends CrossDesk.
// Change only the order, without vetoing shutdown or extending its timeout.
// SCM shutdown notifications still govern the service-owned host/helpers;
// these levels do not override an explicit stop from their owner.
if (SetProcessShutdownParameters(WindowsShutdownLevel(role), 0))
return ERROR_SUCCESS;
const DWORD error = GetLastError();
@@ -237,7 +237,14 @@ void SaveRememberedMode(HKEY root, const RememberedDisplayMode& record) {
RegCloseKey(key);
}
std::vector<std::wstring> ActivePhysicalDisplaySources(bool* queried) {
struct PhysicalDisplayTarget {
std::wstring device;
std::wstring monitor_path;
DISPLAYCONFIG_PATH_TARGET_INFO target;
bool cloned = false;
};
std::vector<PhysicalDisplayTarget> ActivePhysicalDisplays(bool* queried) {
*queried = false;
for (int attempt = 0; attempt < 4; ++attempt) {
UINT32 path_count = 0, mode_count = 0;
@@ -252,7 +259,7 @@ std::vector<std::wstring> ActivePhysicalDisplaySources(bool* queried) {
paths.data(), &mode_count, modes.data(), nullptr);
if (result == ERROR_INSUFFICIENT_BUFFER) continue;
if (result != ERROR_SUCCESS) break;
std::vector<std::wstring> sources;
std::vector<PhysicalDisplayTarget> displays;
for (UINT32 i = 0; i < path_count; ++i) {
const auto& path = paths[i];
if (!(path.flags & DISPLAYCONFIG_PATH_ACTIVE) ||
@@ -268,10 +275,18 @@ std::vector<std::wstring> ActivePhysicalDisplaySources(bool* queried) {
DISPLAYCONFIG_SOURCE_DEVICE_NAME source{};
source.header = {DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME, sizeof(source),
path.sourceInfo.adapterId, path.sourceInfo.id};
if (DisplayConfigGetDeviceInfo(&source.header) == ERROR_SUCCESS)
sources.emplace_back(source.viewGdiDeviceName);
if (DisplayConfigGetDeviceInfo(&source.header) != ERROR_SUCCESS) continue;
const bool cloned = std::count_if(paths.begin(), paths.begin() + path_count,
[&](const DISPLAYCONFIG_PATH_INFO& other) {
return (other.flags & DISPLAYCONFIG_PATH_ACTIVE) &&
other.sourceInfo.id == path.sourceInfo.id &&
other.sourceInfo.adapterId.LowPart == path.sourceInfo.adapterId.LowPart &&
other.sourceInfo.adapterId.HighPart == path.sourceInfo.adapterId.HighPart;
}) > 1;
displays.push_back({source.viewGdiDeviceName, target.monitorDevicePath,
path.targetInfo, cloned});
}
return sources;
return displays;
}
return {};
}
@@ -284,7 +299,7 @@ VirtualDisplayMode QueryAttachedPhysicalDisplayMode(std::wstring* device_name) {
// RDP creates a display mode for that client, not the console's next boot.
if (!GetSystemMetrics(SM_REMOTESESSION)) {
bool queried = false;
const auto sources = ActivePhysicalDisplaySources(&queried);
const auto sources = ActivePhysicalDisplays(&queried);
for (DWORD index = 0;; ++index) {
DISPLAY_DEVICEW adapter{sizeof(adapter)};
if (!EnumDisplayDevicesW(nullptr, index, &adapter, 0)) break;
@@ -296,8 +311,8 @@ VirtualDisplayMode QueryAttachedPhysicalDisplayMode(std::wstring* device_name) {
// real target disappears. Require the corresponding available CCD path
// when CCD is accessible; an IDD path must not validate a phantom GPU.
if (queried && std::none_of(sources.begin(), sources.end(),
[&](const std::wstring& source) {
return _wcsicmp(source.c_str(), adapter.DeviceName) == 0;
[&](const PhysicalDisplayTarget& source) {
return _wcsicmp(source.device.c_str(), adapter.DeviceName) == 0;
})) continue;
bool present = false;
for (DWORD child = 0; !present; ++child) {
@@ -325,7 +340,88 @@ VirtualDisplayMode QueryAttachedPhysicalDisplayMode(std::wstring* device_name) {
return attached;
}
VirtualDisplayMode ResolveVirtualDisplayMode() {
bool RestoreAttachedPhysicalDisplayModes(std::string* error) {
if (GetSystemMetrics(SM_REMOTESESSION) || CountUsbmmiddMonitors() != 0) {
SetError(error, "physical_display_topology_not_ready");
return false;
}
bool queried = false;
const auto displays = ActivePhysicalDisplays(&queried);
if (displays.empty()) {
SetError(error, "physical_display_not_ready");
return false;
}
bool restored = true;
for (const auto& display : displays) {
// One source mode affects every cloned target. Do not force a native 4K
// mode onto another real/third-party screen sharing a 1080p clone source.
if (display.cloned) {
LOG_INFO("Physical display '{}': keeping the shared clone mode", Narrow(display.device));
continue;
}
DISPLAYCONFIG_TARGET_PREFERRED_MODE preferred{};
preferred.header = {DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_PREFERRED_MODE,
sizeof(preferred), display.target.adapterId, display.target.id};
const LONG preferred_error = DisplayConfigGetDeviceInfo(&preferred.header);
DEVMODEW mode{};
mode.dmSize = sizeof(mode);
if (preferred_error != ERROR_SUCCESS ||
!ValidDisplayMode({static_cast<int>(preferred.width), static_cast<int>(preferred.height)}) ||
!EnumDisplaySettingsExW(display.device.c_str(), ENUM_CURRENT_SETTINGS, &mode, 0)) {
SetError(error, "physical_display_preferred_mode_unavailable:" + std::to_string(preferred_error));
restored = false;
continue;
}
DWORD width = preferred.width, height = preferred.height;
if (mode.dmDisplayOrientation == DMDO_90 || mode.dmDisplayOrientation == DMDO_270)
std::swap(width, height);
if (mode.dmPelsWidth == width && mode.dmPelsHeight == height) continue;
const DWORD old_width = mode.dmPelsWidth, old_height = mode.dmPelsHeight;
mode.dmPelsWidth = width;
mode.dmPelsHeight = height;
// Do not reuse the phantom's timing, change the primary screen/position,
// or write this temporary recovery into the user's persistent settings.
mode.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL | DM_DISPLAYORIENTATION;
LONG result = ChangeDisplaySettingsExW(display.device.c_str(), &mode, nullptr, CDS_TEST, nullptr);
if (result == DISP_CHANGE_SUCCESSFUL) {
// Display numbers may be recycled during hotplug. Validate the target
// identity again after CDS_TEST before changing this source's mode.
const auto current = ActivePhysicalDisplays(&queried);
const bool same_target = std::any_of(current.begin(), current.end(),
[&](const PhysicalDisplayTarget& target) {
return !target.cloned && target.device == display.device &&
target.monitor_path == display.monitor_path &&
target.target.id == display.target.id &&
target.target.adapterId.LowPart == display.target.adapterId.LowPart &&
target.target.adapterId.HighPart == display.target.adapterId.HighPart;
});
if (!same_target || CountUsbmmiddMonitors() != 0) {
SetError(error, "physical_display_changed_during_restore");
restored = false;
continue;
}
result = ChangeDisplaySettingsExW(display.device.c_str(), &mode, nullptr, 0, nullptr);
}
if (result != DISP_CHANGE_SUCCESSFUL) {
SetError(error, "physical_display_mode_change_failed:" + std::to_string(result));
restored = false;
continue;
}
DEVMODEW applied{};
applied.dmSize = sizeof(applied);
if (!EnumDisplaySettingsExW(display.device.c_str(), ENUM_CURRENT_SETTINGS, &applied, 0) ||
applied.dmPelsWidth != width || applied.dmPelsHeight != height) {
SetError(error, "physical_display_mode_not_applied");
restored = false;
continue;
}
LOG_INFO("Physical display '{}' restored from {}x{} to preferred {}x{}",
Narrow(display.device), old_width, old_height, width, height);
}
return restored;
}
VirtualDisplayMode ResolveVirtualDisplayMode(bool remember_attached) {
std::lock_guard lock(remembered_mode_mutex);
RememberedDisplayMode saved = last_observed_mode;
for (HKEY root : {HKEY_LOCAL_MACHINE, HKEY_CURRENT_USER}) {
@@ -336,7 +432,7 @@ VirtualDisplayMode ResolveVirtualDisplayMode() {
}
// A reattached physical screen may temporarily clone the fallback monitor.
// Do not let that temporary desktop mode replace the original observation.
const auto attached = CountUsbmmiddMonitors() == 0
const auto attached = remember_attached && CountUsbmmiddMonitors() == 0
? QueryAttachedPhysicalDisplayMode()
: VirtualDisplayMode{};
if (ValidDisplayMode(attached)) {
@@ -41,11 +41,15 @@ UsbmmiddStatus QueryUsbmmiddStatus();
// Remember an attached display's current pixel mode for GUI/SYSTEM startup.
// Phantom targets and our own fallback monitor must not overwrite that mode.
VirtualDisplayMode ResolveVirtualDisplayMode();
VirtualDisplayMode ResolveVirtualDisplayMode(bool remember_attached = true);
// A real active monitor's current mode, or {0, 0} for headless/RDP desktops.
// This probe does not update the remembered mode or change display settings.
VirtualDisplayMode QueryAttachedPhysicalDisplayMode(
std::wstring* device_name = nullptr);
// After a headless/virtual-display handoff, recover each real screen's native
// preferred pixel mode. Keep rotation and layout; never persist fallback modes.
// Returns false while topology/EDID is settling, so the owner can retry.
bool RestoreAttachedPhysicalDisplayModes(std::string* error = nullptr);
// Adapters attached to the desktop, i.e. monitors currently plugged in.
int CountUsbmmiddMonitors();