mirror of
https://github.com/gnif/LookingGlass.git
synced 2026-08-22 07:01:30 +00:00
[idd] helper: wait for display mode enumeration
This commit is contained in:
@@ -36,6 +36,8 @@ namespace
|
|||||||
static const unsigned RECOVERY_PATH_ATTEMPTS = 20;
|
static const unsigned RECOVERY_PATH_ATTEMPTS = 20;
|
||||||
static const size_t RECOVERY_MAX_PATHS = 4;
|
static const size_t RECOVERY_MAX_PATHS = 4;
|
||||||
static const DWORD RECOVERY_VERIFY_DELAY_MS = 100;
|
static const DWORD RECOVERY_VERIFY_DELAY_MS = 100;
|
||||||
|
static const unsigned DISPLAY_MODE_ATTEMPTS = 20;
|
||||||
|
static const DWORD DISPLAY_MODE_DELAY_MS = 100;
|
||||||
|
|
||||||
struct DisplayState
|
struct DisplayState
|
||||||
{
|
{
|
||||||
@@ -194,6 +196,58 @@ namespace
|
|||||||
return lgIndex != SIZE_MAX;
|
return lgIndex != SIZE_MAX;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool GetLGDisplayState(DisplayState& state)
|
||||||
|
{
|
||||||
|
DISPLAY_DEVICE device = {};
|
||||||
|
device.cb = sizeof(device);
|
||||||
|
for (DWORD i = 0; EnumDisplayDevices(NULL, i, &device, 0); ++i)
|
||||||
|
{
|
||||||
|
if ((device.StateFlags & DISPLAY_DEVICE_ATTACHED_TO_DESKTOP) &&
|
||||||
|
!(device.StateFlags & DISPLAY_DEVICE_MIRRORING_DRIVER) &&
|
||||||
|
IsLGDisplay(device))
|
||||||
|
{
|
||||||
|
state = {};
|
||||||
|
state.device = device;
|
||||||
|
state.mode.dmSize = sizeof(state.mode);
|
||||||
|
state.isLG = true;
|
||||||
|
return EnumDisplaySettingsEx(device.DeviceName, ENUM_CURRENT_SETTINGS,
|
||||||
|
&state.mode, 0) != FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
device = {};
|
||||||
|
device.cb = sizeof(device);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t DisplayModeRefresh(const LGPipeMsg& msg)
|
||||||
|
{
|
||||||
|
return (msg.displayMode.refresh100uHz + LG_REFRESH_RATE_SCALE / 2) /
|
||||||
|
LG_REFRESH_RATE_SCALE;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FindDisplayMode(const DisplayState& display, const LGPipeMsg& msg,
|
||||||
|
DEVMODE& mode)
|
||||||
|
{
|
||||||
|
const uint32_t refresh = DisplayModeRefresh(msg);
|
||||||
|
for (DWORD i = 0; ; ++i)
|
||||||
|
{
|
||||||
|
DEVMODE candidate = {};
|
||||||
|
candidate.dmSize = sizeof(candidate);
|
||||||
|
if (!EnumDisplaySettingsEx(display.device.DeviceName, i, &candidate, 0))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (candidate.dmPelsWidth == msg.displayMode.width &&
|
||||||
|
candidate.dmPelsHeight == msg.displayMode.height &&
|
||||||
|
candidate.dmDisplayFrequency == refresh)
|
||||||
|
{
|
||||||
|
mode = candidate;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool HasActiveDisplay(bool lg)
|
bool HasActiveDisplay(bool lg)
|
||||||
{
|
{
|
||||||
DISPLAY_DEVICE device = {};
|
DISPLAY_DEVICE device = {};
|
||||||
@@ -398,17 +452,33 @@ bool CPipeClient::Init()
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!StartDisplayModeThread())
|
||||||
|
{
|
||||||
|
CSRWExclusiveLock lock(m_clipboardSetupLock);
|
||||||
|
ResetClipboardSetupLocked();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
m_endpoint.SetHandler(this);
|
m_endpoint.SetHandler(this);
|
||||||
return m_endpoint.Start(
|
if (m_endpoint.Start(
|
||||||
LG_PIPE_NAME,
|
LG_PIPE_NAME,
|
||||||
CPipeEndpoint::Mode::Client,
|
CPipeEndpoint::Mode::Client,
|
||||||
sizeof(LGPipeMsg));
|
sizeof(LGPipeMsg)))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
StopDisplayModeThread();
|
||||||
|
{
|
||||||
|
CSRWExclusiveLock lock(m_clipboardSetupLock);
|
||||||
|
ResetClipboardSetupLocked();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPipeClient::DeInit()
|
void CPipeClient::DeInit()
|
||||||
{
|
{
|
||||||
// Stop first so no endpoint callback can race mapping teardown.
|
// Stop first so no endpoint callback can race mapping teardown.
|
||||||
m_endpoint.Stop();
|
m_endpoint.Stop();
|
||||||
|
StopDisplayModeThread();
|
||||||
|
|
||||||
CSRWExclusiveLock lock(m_clipboardSetupLock);
|
CSRWExclusiveLock lock(m_clipboardSetupLock);
|
||||||
ResetClipboardSetupLocked();
|
ResetClipboardSetupLocked();
|
||||||
@@ -484,6 +554,165 @@ void CPipeClient::WriteMsg(const LGPipeMsg& msg)
|
|||||||
m_endpoint.Send(&msg, sizeof(msg));
|
m_endpoint.Send(&msg, sizeof(msg));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DWORD WINAPI CPipeClient::DisplayModeThreadProc(void * context)
|
||||||
|
{
|
||||||
|
static_cast<CPipeClient *>(context)->DisplayModeThread();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CPipeClient::StartDisplayModeThread()
|
||||||
|
{
|
||||||
|
m_displayModeStop = CreateEventW(nullptr, TRUE, FALSE, nullptr);
|
||||||
|
m_displayModeWake = CreateEventW(nullptr, FALSE, FALSE, nullptr);
|
||||||
|
if (!m_displayModeStop || !m_displayModeWake)
|
||||||
|
{
|
||||||
|
DEBUG_ERROR_HR(GetLastError(), "Failed to create display mode events");
|
||||||
|
StopDisplayModeThread();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_displayModeThread = CreateThread(
|
||||||
|
nullptr, 0, DisplayModeThreadProc, this, 0, nullptr);
|
||||||
|
if (!m_displayModeThread)
|
||||||
|
{
|
||||||
|
DEBUG_ERROR_HR(GetLastError(), "Failed to create display mode worker");
|
||||||
|
StopDisplayModeThread();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPipeClient::StopDisplayModeThread()
|
||||||
|
{
|
||||||
|
if (m_displayModeStop)
|
||||||
|
SetEvent(m_displayModeStop);
|
||||||
|
if (m_displayModeWake)
|
||||||
|
SetEvent(m_displayModeWake);
|
||||||
|
if (m_displayModeThread)
|
||||||
|
{
|
||||||
|
WaitForSingleObject(m_displayModeThread, INFINITE);
|
||||||
|
CloseHandle(m_displayModeThread);
|
||||||
|
}
|
||||||
|
if (m_displayModeWake)
|
||||||
|
CloseHandle(m_displayModeWake);
|
||||||
|
if (m_displayModeStop)
|
||||||
|
CloseHandle(m_displayModeStop);
|
||||||
|
|
||||||
|
m_displayModeThread = nullptr;
|
||||||
|
m_displayModeWake = nullptr;
|
||||||
|
m_displayModeStop = nullptr;
|
||||||
|
|
||||||
|
CSRWExclusiveLock lock(m_displayModeLock);
|
||||||
|
m_displayMode = {};
|
||||||
|
m_displayModeSerial = 0;
|
||||||
|
m_hasDisplayMode = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CPipeClient::ApplyDisplayMode(const LGPipeMsg& msg, LONG& result)
|
||||||
|
{
|
||||||
|
CSRWExclusiveLock lock(m_displayLock);
|
||||||
|
|
||||||
|
DisplayState display = {};
|
||||||
|
if (!GetLGDisplayState(display))
|
||||||
|
{
|
||||||
|
result = DISP_CHANGE_FAILED;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEVMODE mode = {};
|
||||||
|
if (!FindDisplayMode(display, msg, mode))
|
||||||
|
{
|
||||||
|
result = DISP_CHANGE_BADMODE;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
mode.dmFields =
|
||||||
|
DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFREQUENCY;
|
||||||
|
result = ChangeDisplaySettingsEx(display.device.DeviceName,
|
||||||
|
&mode, NULL, CDS_UPDATEREGISTRY, NULL);
|
||||||
|
return result == DISP_CHANGE_SUCCESSFUL;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CPipeClient::VerifyDisplayMode(const LGPipeMsg& msg)
|
||||||
|
{
|
||||||
|
CSRWExclusiveLock lock(m_displayLock);
|
||||||
|
|
||||||
|
DisplayState display = {};
|
||||||
|
return GetLGDisplayState(display) &&
|
||||||
|
display.mode.dmPelsWidth == msg.displayMode.width &&
|
||||||
|
display.mode.dmPelsHeight == msg.displayMode.height &&
|
||||||
|
display.mode.dmDisplayFrequency == DisplayModeRefresh(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPipeClient::DisplayModeThread()
|
||||||
|
{
|
||||||
|
const HANDLE handles[] = { m_displayModeStop, m_displayModeWake };
|
||||||
|
uint64_t serial = 0;
|
||||||
|
unsigned int attempts = 0;
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
const DWORD wait = WaitForMultipleObjects(_countof(handles), handles,
|
||||||
|
FALSE, attempts ? DISPLAY_MODE_DELAY_MS : INFINITE);
|
||||||
|
if (wait == WAIT_OBJECT_0)
|
||||||
|
break;
|
||||||
|
if (wait == WAIT_FAILED)
|
||||||
|
{
|
||||||
|
DEBUG_ERROR_HR(GetLastError(), "Display mode worker wait failed");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
LGPipeMsg msg = {};
|
||||||
|
{
|
||||||
|
CSRWSharedLock lock(m_displayModeLock);
|
||||||
|
if (!m_hasDisplayMode)
|
||||||
|
{
|
||||||
|
attempts = 0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (serial != m_displayModeSerial)
|
||||||
|
{
|
||||||
|
serial = m_displayModeSerial;
|
||||||
|
attempts = 0;
|
||||||
|
}
|
||||||
|
msg = m_displayMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
LONG result = DISP_CHANGE_FAILED;
|
||||||
|
if (ApplyDisplayMode(msg, result))
|
||||||
|
{
|
||||||
|
if (EnsureOnlyDisplay() && VerifyDisplayMode(msg))
|
||||||
|
{
|
||||||
|
CSRWExclusiveLock lock(m_displayModeLock);
|
||||||
|
if (serial == m_displayModeSerial)
|
||||||
|
m_hasDisplayMode = false;
|
||||||
|
attempts = 0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = DISP_CHANGE_FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
|
++attempts;
|
||||||
|
if (attempts < DISPLAY_MODE_ATTEMPTS)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
CSRWExclusiveLock lock(m_displayModeLock);
|
||||||
|
if (serial == m_displayModeSerial)
|
||||||
|
{
|
||||||
|
DEBUG_ERROR("Failed to apply Looking Glass display mode %ux%u@%u (%ld)",
|
||||||
|
msg.displayMode.width,
|
||||||
|
msg.displayMode.height,
|
||||||
|
DisplayModeRefresh(msg),
|
||||||
|
result);
|
||||||
|
m_hasDisplayMode = false;
|
||||||
|
}
|
||||||
|
attempts = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool CPipeClient::PipeServerIsAuthorized(HANDLE pipe)
|
bool CPipeClient::PipeServerIsAuthorized(HANDLE pipe)
|
||||||
{
|
{
|
||||||
DWORD session = 0xFFFFFFFFU;
|
DWORD session = 0xFFFFFFFFU;
|
||||||
@@ -996,35 +1225,17 @@ void CPipeClient::HandleSetCursorPos(const LGPipeMsg& msg)
|
|||||||
|
|
||||||
void CPipeClient::HandleSetDisplayMode(const LGPipeMsg& msg)
|
void CPipeClient::HandleSetDisplayMode(const LGPipeMsg& msg)
|
||||||
{
|
{
|
||||||
LONG result;
|
// The IDD reaches swap-chain readiness before Win32 has necessarily
|
||||||
|
// enumerated the replugged monitor's complete mode list. Latch the latest
|
||||||
|
// request and let the worker apply it once that list is ready.
|
||||||
{
|
{
|
||||||
CSRWExclusiveLock lock(m_displayLock);
|
CSRWExclusiveLock lock(m_displayModeLock);
|
||||||
|
m_displayMode = msg;
|
||||||
std::vector<DisplayState> displays;
|
++m_displayModeSerial;
|
||||||
size_t lgIndex;
|
m_hasDisplayMode = true;
|
||||||
if (!GetDisplayStates(displays, lgIndex))
|
|
||||||
{
|
|
||||||
DEBUG_ERROR("Looking Glass display not found while setting its mode");
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DEVMODE dm = displays[lgIndex].mode;
|
SetEvent(m_displayModeWake);
|
||||||
dm.dmPelsWidth = msg.displayMode.width;
|
|
||||||
dm.dmPelsHeight = msg.displayMode.height;
|
|
||||||
dm.dmDisplayFrequency =
|
|
||||||
(msg.displayMode.refresh100uHz + LG_REFRESH_RATE_SCALE / 2) /
|
|
||||||
LG_REFRESH_RATE_SCALE;
|
|
||||||
dm.dmFields =
|
|
||||||
DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFREQUENCY;
|
|
||||||
|
|
||||||
result = ChangeDisplaySettingsEx(displays[lgIndex].device.DeviceName,
|
|
||||||
&dm, NULL, CDS_UPDATEREGISTRY, NULL);
|
|
||||||
if (result != DISP_CHANGE_SUCCESSFUL)
|
|
||||||
DEBUG_ERROR("ChangeDisplaySettingsEx Failed (0x%08x)", result);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (result == DISP_CHANGE_SUCCESSFUL)
|
|
||||||
EnsureOnlyDisplay();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPipeClient::HandleGPUStatus(const LGPipeMsg& msg)
|
void CPipeClient::HandleGPUStatus(const LGPipeMsg& msg)
|
||||||
|
|||||||
@@ -43,6 +43,14 @@ private:
|
|||||||
bool m_clipboardEnabled = false;
|
bool m_clipboardEnabled = false;
|
||||||
CSRWLock m_displayLock;
|
CSRWLock m_displayLock;
|
||||||
|
|
||||||
|
CSRWLock m_displayModeLock;
|
||||||
|
HANDLE m_displayModeStop = nullptr;
|
||||||
|
HANDLE m_displayModeWake = nullptr;
|
||||||
|
HANDLE m_displayModeThread = nullptr;
|
||||||
|
LGPipeMsg m_displayMode = {};
|
||||||
|
uint64_t m_displayModeSerial = 0;
|
||||||
|
bool m_hasDisplayMode = false;
|
||||||
|
|
||||||
bool m_recoveryActive = false;
|
bool m_recoveryActive = false;
|
||||||
bool m_hasRecoveryStatus = false;
|
bool m_hasRecoveryStatus = false;
|
||||||
LGPipeMsg m_recoveryStatus = {};
|
LGPipeMsg m_recoveryStatus = {};
|
||||||
@@ -51,6 +59,13 @@ private:
|
|||||||
|
|
||||||
void SetActiveDesktop();
|
void SetActiveDesktop();
|
||||||
|
|
||||||
|
static DWORD WINAPI DisplayModeThreadProc(void * context);
|
||||||
|
void DisplayModeThread();
|
||||||
|
bool StartDisplayModeThread();
|
||||||
|
void StopDisplayModeThread();
|
||||||
|
bool ApplyDisplayMode(const LGPipeMsg& msg, LONG& result);
|
||||||
|
bool VerifyDisplayMode(const LGPipeMsg& msg);
|
||||||
|
|
||||||
bool EnsureOnlyDisplayLocked();
|
bool EnsureOnlyDisplayLocked();
|
||||||
uint32_t RestoreSavedTopologyLocked() const;
|
uint32_t RestoreSavedTopologyLocked() const;
|
||||||
uint32_t RestoreLGTopologyLocked();
|
uint32_t RestoreLGTopologyLocked();
|
||||||
|
|||||||
Reference in New Issue
Block a user