mirror of
https://github.com/gnif/LookingGlass.git
synced 2026-08-22 07:01:30 +00:00
[idd] recovery: unplug monitor without helper
When LGIddHelper is unavailable, recovery requests could remain pending until timeout while the virtual monitor stayed connected. This could leave a logged-out guest without a usable display. Fall back to departing the IDD monitor for active recovery, then re-arrive it before normal recovery completes. Serialize monitor lifecycle changes and preserve recovery ordering across Helper reconnects, timeouts, and stale responses.
This commit is contained in:
@@ -385,16 +385,42 @@ void CDeviceContext::FinishAdapterInit(UINT connectorIndex)
|
||||
DEBUG_INFO("Preferred render adapter set");
|
||||
}
|
||||
|
||||
FinishInit(connectorIndex);
|
||||
bool monitorDisabled;
|
||||
bool arrivalPending;
|
||||
{
|
||||
CSRWExclusiveLock lock(m_localRecoveryLock);
|
||||
m_adapterReady = true;
|
||||
monitorDisabled = m_recoveryMonitorDisabled;
|
||||
arrivalPending = m_localRecoveryArrival;
|
||||
}
|
||||
|
||||
if (!monitorDisabled)
|
||||
{
|
||||
FinishInit(connectorIndex);
|
||||
return;
|
||||
}
|
||||
|
||||
if (arrivalPending)
|
||||
m_monitorManager.Enable();
|
||||
else
|
||||
{
|
||||
// Recovery can intentionally disable the monitor before the adapter's
|
||||
// first arrival. This is still a valid synchronization boundary: allow a
|
||||
// later NORMAL request to re-enable the monitor instead of waiting for an
|
||||
// arrival that ACTIVE deliberately suppressed.
|
||||
m_transport->SyncRecovery();
|
||||
}
|
||||
}
|
||||
|
||||
void CDeviceContext::FinishInit(UINT connectorIndex)
|
||||
{
|
||||
CDisplayConfiguration::Description description =
|
||||
m_displayConfiguration.GetDescription();
|
||||
if (m_monitorManager.Create(
|
||||
connectorIndex, m_adapter, std::move(description.edid), this))
|
||||
const bool arrived = m_monitorManager.Create(
|
||||
connectorIndex, m_adapter, std::move(description.edid), this);
|
||||
if (arrived)
|
||||
m_transport->SyncRecovery();
|
||||
CompleteRecoveryArrival(arrived);
|
||||
}
|
||||
|
||||
void CDeviceContext::ReplugMonitor()
|
||||
@@ -489,28 +515,80 @@ bool CDeviceContext::InitializeTransport()
|
||||
|
||||
g_pipe.SetRecoveryHandler(
|
||||
[](void * opaque, uint64_t route, uint64_t session,
|
||||
uint32_t serial, bool active, LGPipeMsg::Type result)
|
||||
uint32_t serial, bool active, CPipeServer::RecoveryResult result)
|
||||
{
|
||||
CDeviceContext * context =
|
||||
static_cast<CDeviceContext *>(opaque);
|
||||
|
||||
RecoveryAction action;
|
||||
action.route = route;
|
||||
action.session = session;
|
||||
action.serial = serial;
|
||||
action.active = active;
|
||||
|
||||
if (result == CPipeServer::RecoveryResult::HELPER_UNAVAILABLE)
|
||||
{
|
||||
// Pipe recovery messages do not carry the local operation deadline.
|
||||
// Recover it from the canonical action so a late monitor transition
|
||||
// cannot complete an operation after the recovery hub timed it out.
|
||||
{
|
||||
CSRWSharedLock lock(context->m_localRecoveryLock);
|
||||
if (context->m_latestRecoveryValid &&
|
||||
context->SameRecoveryAction(action,
|
||||
context->m_latestRecoveryAction))
|
||||
action = context->m_latestRecoveryAction;
|
||||
}
|
||||
|
||||
if (!context->QueueLocalRecovery(action))
|
||||
context->m_transport->RecoveryStatus(
|
||||
route, session, serial, active,
|
||||
ITransport::Recovery::FAILED, RPC_S_SERVER_UNAVAILABLE);
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> transitionLock(
|
||||
context->m_recoveryTransitionMutex);
|
||||
context->m_helperRecoveryAction = action;
|
||||
context->m_helperRecoveryComplete = true;
|
||||
context->RemoveLocalRecovery(action);
|
||||
|
||||
ITransport::Recovery state = ITransport::Recovery::FAILED;
|
||||
uint32_t error = ERROR_SUCCESS;
|
||||
switch (result)
|
||||
{
|
||||
case LGPipeMsg::RECOVERY_OFF:
|
||||
case CPipeServer::RecoveryResult::NORMAL:
|
||||
state = ITransport::Recovery::NORMAL;
|
||||
{
|
||||
CSRWExclusiveLock lock(context->m_localRecoveryLock);
|
||||
if (context->m_latestRecoveryValid &&
|
||||
context->SameRecoveryAction(action,
|
||||
context->m_latestRecoveryAction))
|
||||
{
|
||||
context->m_recoveryActive = false;
|
||||
context->m_latestRecoveryAction.deadline = 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case LGPipeMsg::RECOVERY_ON:
|
||||
case CPipeServer::RecoveryResult::ACTIVE:
|
||||
state = ITransport::Recovery::ACTIVE;
|
||||
{
|
||||
CSRWExclusiveLock lock(context->m_localRecoveryLock);
|
||||
if (context->m_latestRecoveryValid &&
|
||||
context->SameRecoveryAction(action,
|
||||
context->m_latestRecoveryAction))
|
||||
{
|
||||
context->m_recoveryActive = true;
|
||||
context->m_latestRecoveryAction.deadline = 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case LGPipeMsg::RECOVERY_FAILED:
|
||||
case CPipeServer::RecoveryResult::FAILED:
|
||||
error = ERROR_GEN_FAILURE;
|
||||
break;
|
||||
|
||||
case LGPipeMsg::RECOVERY_NO_DISPLAY:
|
||||
case CPipeServer::RecoveryResult::NO_DISPLAY:
|
||||
error = ERROR_NOT_FOUND;
|
||||
break;
|
||||
|
||||
@@ -588,6 +666,8 @@ bool CDeviceContext::SetupTransport(size_t alignSize)
|
||||
|
||||
void CDeviceContext::TransportTimer()
|
||||
{
|
||||
ProcessLocalRecovery();
|
||||
|
||||
// Monitor work is deferred off IddCx callback threads.
|
||||
switch (m_monitorManager.TakeDeferredAction())
|
||||
{
|
||||
@@ -623,6 +703,360 @@ InteractionResult CDeviceContext::OnSetResolution(const SourceKey& source,
|
||||
|
||||
bool CDeviceContext::OnRecoveryAction(const RecoveryAction& action)
|
||||
{
|
||||
return g_pipe.SetRecovery(this, action.route, action.session,
|
||||
action.serial, action.active);
|
||||
bool recoveryActive;
|
||||
bool monitorDisabled;
|
||||
{
|
||||
std::lock_guard<std::mutex> transitionLock(m_recoveryTransitionMutex);
|
||||
CSRWExclusiveLock lock(m_localRecoveryLock);
|
||||
m_latestRecoveryAction = action;
|
||||
m_latestRecoveryValid = true;
|
||||
recoveryActive = m_recoveryActive;
|
||||
monitorDisabled = m_recoveryMonitorDisabled;
|
||||
}
|
||||
|
||||
// The monitor must exist before Helper can restore the LG display path.
|
||||
// Re-arrive it first when recovery previously used the IDD-only fallback.
|
||||
if (!action.active && monitorDisabled)
|
||||
return QueueLocalRecovery(action);
|
||||
|
||||
const CPipeServer::RecoveryDispatch dispatch = g_pipe.SetRecovery(
|
||||
this, action.route, action.session, action.serial, action.active,
|
||||
action.active || !recoveryActive);
|
||||
|
||||
std::lock_guard<std::mutex> transitionLock(m_recoveryTransitionMutex);
|
||||
if (m_helperRecoveryComplete &&
|
||||
SameRecoveryAction(m_helperRecoveryAction, action))
|
||||
return true;
|
||||
|
||||
switch (dispatch)
|
||||
{
|
||||
case CPipeServer::RecoveryDispatch::SENT:
|
||||
return true;
|
||||
|
||||
case CPipeServer::RecoveryDispatch::UNAVAILABLE:
|
||||
case CPipeServer::RecoveryDispatch::QUEUED:
|
||||
return QueueLocalRecovery(action);
|
||||
|
||||
case CPipeServer::RecoveryDispatch::REJECTED:
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CDeviceContext::SameRecoveryAction(
|
||||
const RecoveryAction& left, const RecoveryAction& right)
|
||||
{
|
||||
return left.route == right.route &&
|
||||
left.session == right.session &&
|
||||
left.serial == right.serial &&
|
||||
left.active == right.active;
|
||||
}
|
||||
|
||||
bool CDeviceContext::QueueLocalRecovery(const RecoveryAction& action)
|
||||
{
|
||||
if (!action.route || !action.session || !action.serial)
|
||||
return false;
|
||||
|
||||
CSRWExclusiveLock lock(m_localRecoveryLock);
|
||||
if (m_latestRecoveryValid &&
|
||||
!SameRecoveryAction(action, m_latestRecoveryAction))
|
||||
return true;
|
||||
|
||||
if (m_localRecoveryArrival)
|
||||
{
|
||||
const RecoveryAction& pending = m_localRecoveryArrivalAction;
|
||||
if (SameRecoveryAction(pending, action))
|
||||
return true;
|
||||
}
|
||||
|
||||
for (const RecoveryAction& pending : m_localRecoveryQueue)
|
||||
if (SameRecoveryAction(pending, action))
|
||||
return true;
|
||||
|
||||
m_localRecoveryQueue.push_back(action);
|
||||
return true;
|
||||
}
|
||||
|
||||
void CDeviceContext::RemoveLocalRecovery(const RecoveryAction& action)
|
||||
{
|
||||
CSRWExclusiveLock lock(m_localRecoveryLock);
|
||||
for (auto it = m_localRecoveryQueue.begin();
|
||||
it != m_localRecoveryQueue.end();)
|
||||
{
|
||||
if (SameRecoveryAction(*it, action))
|
||||
it = m_localRecoveryQueue.erase(it);
|
||||
else
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
void CDeviceContext::ProcessLocalRecovery()
|
||||
{
|
||||
std::lock_guard<std::mutex> transitionLock(m_recoveryTransitionMutex);
|
||||
|
||||
RecoveryAction action;
|
||||
bool haveAction = false;
|
||||
bool reconcileActive = false;
|
||||
bool reconcileAdapterReady = false;
|
||||
{
|
||||
CSRWExclusiveLock lock(m_localRecoveryLock);
|
||||
for (;;)
|
||||
{
|
||||
if (m_localRecoveryQueue.empty())
|
||||
break;
|
||||
action = m_localRecoveryQueue.front();
|
||||
m_localRecoveryQueue.pop_front();
|
||||
const bool current = !m_latestRecoveryValid ||
|
||||
SameRecoveryAction(action, m_latestRecoveryAction);
|
||||
const bool expired = action.deadline &&
|
||||
GetTickCount64() >= action.deadline;
|
||||
if (current && !expired)
|
||||
{
|
||||
haveAction = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// The request can expire after Helper disappears but before this timer
|
||||
// consumes the handoff. Its result is stale, but an already-established
|
||||
// ACTIVE state still needs the IDD monitor physically disabled.
|
||||
if (current && expired && m_recoveryActive &&
|
||||
!m_recoveryMonitorDisabled)
|
||||
{
|
||||
m_recoveryMonitorDisabled = true;
|
||||
reconcileActive = true;
|
||||
reconcileAdapterReady = m_adapterReady;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (reconcileActive)
|
||||
{
|
||||
DEBUG_WARN(
|
||||
"IDD Helper is unavailable; reconciling the active recovery topology");
|
||||
if (!m_monitorManager.Disable())
|
||||
{
|
||||
CSRWExclusiveLock lock(m_localRecoveryLock);
|
||||
m_recoveryMonitorDisabled = false;
|
||||
}
|
||||
else if (reconcileAdapterReady)
|
||||
m_transport->SyncRecovery();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!haveAction)
|
||||
return;
|
||||
|
||||
if (action.active)
|
||||
{
|
||||
DEBUG_WARN(
|
||||
"IDD Helper is unavailable; disabling the virtual monitor for recovery");
|
||||
bool oldRecoveryActive;
|
||||
bool oldMonitorDisabled;
|
||||
bool adapterReady;
|
||||
{
|
||||
CSRWExclusiveLock lock(m_localRecoveryLock);
|
||||
oldRecoveryActive = m_recoveryActive;
|
||||
oldMonitorDisabled = m_recoveryMonitorDisabled;
|
||||
m_recoveryActive = true;
|
||||
m_recoveryMonitorDisabled = true;
|
||||
adapterReady = m_adapterReady;
|
||||
}
|
||||
|
||||
const bool disabled = m_monitorManager.Disable();
|
||||
if (!disabled)
|
||||
{
|
||||
CSRWExclusiveLock lock(m_localRecoveryLock);
|
||||
m_recoveryActive = oldRecoveryActive;
|
||||
m_recoveryMonitorDisabled = oldMonitorDisabled;
|
||||
}
|
||||
if (disabled && adapterReady)
|
||||
m_transport->SyncRecovery();
|
||||
m_transport->RecoveryStatus(action.route, action.session, action.serial,
|
||||
true, disabled ? ITransport::Recovery::ACTIVE :
|
||||
ITransport::Recovery::FAILED,
|
||||
disabled ? ERROR_SUCCESS : ERROR_GEN_FAILURE);
|
||||
return;
|
||||
}
|
||||
|
||||
bool disable = false;
|
||||
{
|
||||
CSRWExclusiveLock lock(m_localRecoveryLock);
|
||||
disable = m_recoveryActive && !m_recoveryMonitorDisabled;
|
||||
if (disable)
|
||||
m_recoveryMonitorDisabled = true;
|
||||
}
|
||||
|
||||
if (disable)
|
||||
{
|
||||
DEBUG_WARN(
|
||||
"IDD Helper disconnected during recovery; cycling the virtual monitor");
|
||||
if (!m_monitorManager.Disable())
|
||||
{
|
||||
{
|
||||
CSRWExclusiveLock lock(m_localRecoveryLock);
|
||||
m_recoveryMonitorDisabled = false;
|
||||
}
|
||||
m_transport->RecoveryStatus(action.route, action.session, action.serial,
|
||||
false, ITransport::Recovery::FAILED, ERROR_GEN_FAILURE);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
bool enable = false;
|
||||
bool waitArrival = false;
|
||||
{
|
||||
CSRWExclusiveLock lock(m_localRecoveryLock);
|
||||
if (m_recoveryMonitorDisabled)
|
||||
{
|
||||
m_localRecoveryArrivalAction = action;
|
||||
m_localRecoveryArrival = true;
|
||||
enable = m_adapterReady;
|
||||
waitArrival = true;
|
||||
}
|
||||
else
|
||||
m_recoveryActive = false;
|
||||
}
|
||||
|
||||
if (enable)
|
||||
m_monitorManager.Enable();
|
||||
|
||||
if (waitArrival)
|
||||
return;
|
||||
|
||||
// The normal monitor is already present. With no Helper there is no
|
||||
// user-session topology work to perform, so monitor presence is the local
|
||||
// completion boundary.
|
||||
m_transport->RecoveryStatus(action.route, action.session, action.serial,
|
||||
false, ITransport::Recovery::NORMAL, ERROR_SUCCESS);
|
||||
}
|
||||
|
||||
void CDeviceContext::CompleteRecoveryArrival(bool arrived)
|
||||
{
|
||||
RecoveryAction action;
|
||||
RecoveryAction latest;
|
||||
bool stale = false;
|
||||
bool latestValid = false;
|
||||
bool latestHandled = false;
|
||||
bool latestCurrent = false;
|
||||
std::unique_lock<std::mutex> transitionLock(m_recoveryTransitionMutex);
|
||||
{
|
||||
CSRWExclusiveLock lock(m_localRecoveryLock);
|
||||
if (!m_localRecoveryArrival)
|
||||
return;
|
||||
|
||||
action = m_localRecoveryArrivalAction;
|
||||
const uint64_t now = GetTickCount64();
|
||||
const bool expired = action.deadline && now >= action.deadline;
|
||||
const bool superseded = m_latestRecoveryValid &&
|
||||
!SameRecoveryAction(action, m_latestRecoveryAction);
|
||||
stale = expired || superseded;
|
||||
if (stale)
|
||||
{
|
||||
latestValid = m_latestRecoveryValid;
|
||||
latest = m_latestRecoveryAction;
|
||||
latestHandled = m_helperRecoveryComplete &&
|
||||
SameRecoveryAction(m_helperRecoveryAction, latest);
|
||||
latestCurrent = latestValid &&
|
||||
(!latest.deadline || now < latest.deadline);
|
||||
if (superseded && arrived && latestCurrent && !latest.active)
|
||||
{
|
||||
action = latest;
|
||||
m_localRecoveryArrivalAction = latest;
|
||||
stale = false;
|
||||
for (auto it = m_localRecoveryQueue.begin();
|
||||
it != m_localRecoveryQueue.end();)
|
||||
{
|
||||
if (SameRecoveryAction(*it, latest))
|
||||
it = m_localRecoveryQueue.erase(it);
|
||||
else
|
||||
++it;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_localRecoveryArrivalAction = {};
|
||||
m_localRecoveryArrival = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (arrived && !stale)
|
||||
{
|
||||
// Arrival is the IDD-only NORMAL completion boundary. Keep the monitor
|
||||
// present if Helper disappears while applying its user-session topology.
|
||||
m_localRecoveryArrivalAction = {};
|
||||
m_localRecoveryArrival = false;
|
||||
m_recoveryActive = false;
|
||||
m_recoveryMonitorDisabled = false;
|
||||
}
|
||||
else if (!stale)
|
||||
{
|
||||
m_localRecoveryArrivalAction = {};
|
||||
m_localRecoveryArrival = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (stale)
|
||||
{
|
||||
// The expired/superseded NORMAL operation must not leave the IDD monitor
|
||||
// arrived while CPipe still retains the preceding ACTIVE topology. Return
|
||||
// to that stable local state before allowing newer work to proceed.
|
||||
const bool disabled = m_monitorManager.Disable();
|
||||
{
|
||||
CSRWExclusiveLock lock(m_localRecoveryLock);
|
||||
m_recoveryActive = true;
|
||||
m_recoveryMonitorDisabled = disabled;
|
||||
}
|
||||
|
||||
const bool queueLatest = latestValid && latest.active &&
|
||||
latestCurrent && !latestHandled;
|
||||
transitionLock.unlock();
|
||||
if (queueLatest)
|
||||
QueueLocalRecovery(latest);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!arrived)
|
||||
{
|
||||
const bool disabled = m_monitorManager.Disable();
|
||||
{
|
||||
CSRWExclusiveLock lock(m_localRecoveryLock);
|
||||
if (disabled)
|
||||
{
|
||||
m_recoveryActive = true;
|
||||
m_recoveryMonitorDisabled = true;
|
||||
}
|
||||
}
|
||||
transitionLock.unlock();
|
||||
m_transport->RecoveryStatus(action.route, action.session, action.serial,
|
||||
false, ITransport::Recovery::FAILED, ERROR_GEN_FAILURE);
|
||||
return;
|
||||
}
|
||||
|
||||
transitionLock.unlock();
|
||||
|
||||
// If Helper appeared while the monitor was disabled, let it restore the
|
||||
// user-session topology now that the LG display path exists again.
|
||||
const CPipeServer::RecoveryDispatch dispatch = g_pipe.SetRecovery(
|
||||
this, action.route, action.session, action.serial, false, true);
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> completionLock(m_recoveryTransitionMutex);
|
||||
CSRWExclusiveLock lock(m_localRecoveryLock);
|
||||
if (m_helperRecoveryComplete &&
|
||||
SameRecoveryAction(m_helperRecoveryAction, action))
|
||||
return;
|
||||
}
|
||||
|
||||
if (dispatch == CPipeServer::RecoveryDispatch::SENT)
|
||||
return;
|
||||
|
||||
m_transport->RecoveryStatus(action.route, action.session, action.serial,
|
||||
false,
|
||||
(dispatch == CPipeServer::RecoveryDispatch::QUEUED ||
|
||||
dispatch == CPipeServer::RecoveryDispatch::UNAVAILABLE) ?
|
||||
ITransport::Recovery::NORMAL : ITransport::Recovery::FAILED,
|
||||
(dispatch == CPipeServer::RecoveryDispatch::QUEUED ||
|
||||
dispatch == CPipeServer::RecoveryDispatch::UNAVAILABLE) ?
|
||||
ERROR_SUCCESS : RPC_S_SERVER_UNAVAILABLE);
|
||||
}
|
||||
|
||||
@@ -26,7 +26,9 @@
|
||||
#include <wdf.h>
|
||||
#include <IddCx.h>
|
||||
|
||||
#include <deque>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
@@ -55,6 +57,19 @@ private:
|
||||
WDFTIMER m_transportTimer = nullptr;
|
||||
bool m_recoveryHandlerSet = false;
|
||||
|
||||
std::mutex m_recoveryTransitionMutex;
|
||||
CSRWLock m_localRecoveryLock;
|
||||
std::deque<RecoveryAction> m_localRecoveryQueue;
|
||||
RecoveryAction m_localRecoveryArrivalAction = {};
|
||||
RecoveryAction m_latestRecoveryAction = {};
|
||||
RecoveryAction m_helperRecoveryAction = {};
|
||||
bool m_localRecoveryArrival = false;
|
||||
bool m_latestRecoveryValid = false;
|
||||
bool m_helperRecoveryComplete = false;
|
||||
bool m_recoveryActive = false;
|
||||
bool m_recoveryMonitorDisabled = false;
|
||||
bool m_adapterReady = false;
|
||||
|
||||
UINT m_iddCxVersion = 0;
|
||||
bool m_hasIddCx110DDIs = false;
|
||||
bool m_canProcessFP16 = false;
|
||||
@@ -67,6 +82,12 @@ private:
|
||||
|
||||
bool InitializeTransport();
|
||||
void TransportTimer();
|
||||
static bool SameRecoveryAction(
|
||||
const RecoveryAction& left, const RecoveryAction& right);
|
||||
bool QueueLocalRecovery(const RecoveryAction& action);
|
||||
void RemoveLocalRecovery(const RecoveryAction& action);
|
||||
void ProcessLocalRecovery();
|
||||
void CompleteRecoveryArrival(bool arrived);
|
||||
InteractionResult OnSetCursorPos(
|
||||
const SourceKey& source, int32_t x, int32_t y) override;
|
||||
InteractionResult OnSetResolution(const SourceKey& source,
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
bool CMonitorManager::Create(UINT connectorIndex, IDDCX_ADAPTER adapter,
|
||||
std::vector<BYTE> edid, CDeviceContext * owner)
|
||||
{
|
||||
std::lock_guard<std::mutex> lifecycleLock(m_lifecycleMutex);
|
||||
DEBUG_INFO("Creating monitor on connector %u", connectorIndex);
|
||||
|
||||
// We support a single monitor; never create a second one if one already
|
||||
@@ -34,6 +35,8 @@ bool CMonitorManager::Create(UINT connectorIndex, IDDCX_ADAPTER adapter,
|
||||
bool haveMonitor;
|
||||
{
|
||||
CSRWExclusiveLock lock(m_lock);
|
||||
if (!m_enabled)
|
||||
return false;
|
||||
haveMonitor = m_monitor != WDF_NO_HANDLE;
|
||||
}
|
||||
if (haveMonitor)
|
||||
@@ -79,18 +82,27 @@ bool CMonitorManager::Create(UINT connectorIndex, IDDCX_ADAPTER adapter,
|
||||
|
||||
DEBUG_INFO("Monitor object created (%p)", createOut.MonitorObject);
|
||||
|
||||
const IDDCX_MONITOR monitor = createOut.MonitorObject;
|
||||
|
||||
auto * wrapper = WdfObjectGet_CMonitorContextWrapper(monitor);
|
||||
wrapper->context = new CMonitorContext(monitor, owner);
|
||||
|
||||
{
|
||||
CSRWExclusiveLock lock(m_lock);
|
||||
m_monitor = createOut.MonitorObject;
|
||||
m_monitor = monitor;
|
||||
m_monitorDeparted = false;
|
||||
}
|
||||
|
||||
auto * wrapper = WdfObjectGet_CMonitorContextWrapper(m_monitor);
|
||||
wrapper->context = new CMonitorContext(m_monitor, owner);
|
||||
|
||||
IDARG_OUT_MONITORARRIVAL out = {};
|
||||
status = IddCxMonitorArrival(m_monitor, &out);
|
||||
status = IddCxMonitorArrival(monitor, &out);
|
||||
if (FAILED(status))
|
||||
{
|
||||
{
|
||||
CSRWExclusiveLock lock(m_lock);
|
||||
if (m_monitor == monitor)
|
||||
m_monitor = nullptr;
|
||||
}
|
||||
WdfObjectDelete((WDFOBJECT)monitor);
|
||||
DEBUG_ERROR_HR(status, "IddCxMonitorArrival Failed");
|
||||
return false;
|
||||
}
|
||||
@@ -99,12 +111,100 @@ bool CMonitorManager::Create(UINT connectorIndex, IDDCX_ADAPTER adapter,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CMonitorManager::Disable()
|
||||
{
|
||||
std::lock_guard<std::mutex> lifecycleLock(m_lifecycleMutex);
|
||||
IDDCX_MONITOR monitor = nullptr;
|
||||
bool oldReplugMonitor = false;
|
||||
bool oldReplugPending = false;
|
||||
bool oldMonitorDeparted = false;
|
||||
bool oldWaitForSwapChainRelease = false;
|
||||
LONG oldCreateQueued = 0;
|
||||
LONG oldReplugQueued = 0;
|
||||
{
|
||||
CSRWExclusiveLock lock(m_lock);
|
||||
if (!m_enabled)
|
||||
return true;
|
||||
|
||||
oldReplugMonitor = m_replugMonitor;
|
||||
oldReplugPending = m_replugPending;
|
||||
oldMonitorDeparted = m_monitorDeparted;
|
||||
oldWaitForSwapChainRelease = m_waitForSwapChainRelease;
|
||||
oldCreateQueued = Atomic::Load(m_createQueued);
|
||||
oldReplugQueued = Atomic::Load(m_replugQueued);
|
||||
|
||||
m_enabled = false;
|
||||
m_replugMonitor = false;
|
||||
m_replugPending = false;
|
||||
Atomic::Store(m_createQueued, 0);
|
||||
Atomic::Store(m_replugQueued, 0);
|
||||
|
||||
monitor = m_monitor;
|
||||
if (monitor == WDF_NO_HANDLE)
|
||||
return true;
|
||||
|
||||
m_monitor = nullptr;
|
||||
m_monitorDeparted = false;
|
||||
m_waitForSwapChainRelease = m_swapChainAssigned;
|
||||
}
|
||||
|
||||
DEBUG_INFO("Disabling the virtual monitor for recovery");
|
||||
const NTSTATUS status = IddCxMonitorDeparture(monitor);
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
CSRWExclusiveLock lock(m_lock);
|
||||
m_enabled = true;
|
||||
m_monitor = monitor;
|
||||
m_replugMonitor = oldReplugMonitor;
|
||||
m_replugPending = oldReplugPending;
|
||||
m_monitorDeparted = oldMonitorDeparted;
|
||||
m_waitForSwapChainRelease = oldWaitForSwapChainRelease;
|
||||
Atomic::Store(m_createQueued, oldCreateQueued);
|
||||
Atomic::Store(m_replugQueued, oldReplugQueued);
|
||||
DEBUG_ERROR_HR(status,
|
||||
"Failed to disable the virtual monitor for recovery");
|
||||
return false;
|
||||
}
|
||||
|
||||
{
|
||||
CSRWExclusiveLock lock(m_lock);
|
||||
m_monitorDeparted = true;
|
||||
}
|
||||
DEBUG_INFO("Virtual monitor disabled for recovery");
|
||||
return true;
|
||||
}
|
||||
|
||||
void CMonitorManager::Enable()
|
||||
{
|
||||
std::lock_guard<std::mutex> lifecycleLock(m_lifecycleMutex);
|
||||
bool create = false;
|
||||
{
|
||||
CSRWExclusiveLock lock(m_lock);
|
||||
if (m_enabled)
|
||||
return;
|
||||
|
||||
m_enabled = true;
|
||||
create = m_monitor == WDF_NO_HANDLE &&
|
||||
!m_waitForSwapChainRelease;
|
||||
}
|
||||
|
||||
if (create)
|
||||
Atomic::Store(m_createQueued, 1);
|
||||
}
|
||||
|
||||
CMonitorManager::ReplugAction CMonitorManager::Replug()
|
||||
{
|
||||
std::lock_guard<std::mutex> lifecycleLock(m_lifecycleMutex);
|
||||
IDDCX_MONITOR monitor;
|
||||
{
|
||||
CSRWExclusiveLock lock(m_lock);
|
||||
|
||||
if (!m_enabled)
|
||||
return ReplugAction::NONE;
|
||||
|
||||
if (m_waitForSwapChainRelease)
|
||||
return ReplugAction::NONE;
|
||||
|
||||
if (m_replugMonitor || (m_swapChainAssigned && !m_swapChainReady))
|
||||
{
|
||||
// Coalesce changes received while a swap chain is being initialized,
|
||||
@@ -198,10 +298,11 @@ void CMonitorManager::OnSwapChainReleased()
|
||||
CSRWExclusiveLock lock(m_lock);
|
||||
m_swapChainAssigned = false;
|
||||
m_swapChainReady = false;
|
||||
if (m_replugMonitor && m_waitForSwapChainRelease)
|
||||
if (m_waitForSwapChainRelease)
|
||||
{
|
||||
m_waitForSwapChainRelease = false;
|
||||
rebuild = m_monitorDeparted;
|
||||
rebuild = m_enabled && m_monitorDeparted &&
|
||||
m_monitor == WDF_NO_HANDLE;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -217,6 +318,9 @@ CMonitorManager::ReadyAction CMonitorManager::OnSwapChainReady()
|
||||
{
|
||||
CSRWExclusiveLock lock(m_lock);
|
||||
m_swapChainReady = true;
|
||||
if (!m_enabled || m_waitForSwapChainRelease)
|
||||
return action;
|
||||
|
||||
if (m_replugMonitor)
|
||||
{
|
||||
m_replugMonitor = false;
|
||||
@@ -250,16 +354,27 @@ CMonitorManager::ReadyAction CMonitorManager::OnSwapChainReady()
|
||||
|
||||
void CMonitorManager::QueueReplug()
|
||||
{
|
||||
CSRWSharedLock lock(m_lock);
|
||||
if (!m_enabled)
|
||||
return;
|
||||
Atomic::Store(m_replugQueued, 1);
|
||||
}
|
||||
|
||||
CMonitorManager::DeferredAction CMonitorManager::TakeDeferredAction()
|
||||
{
|
||||
if (Atomic::Swap(m_createQueued, 0))
|
||||
return DeferredAction::CREATE;
|
||||
{
|
||||
CSRWSharedLock lock(m_lock);
|
||||
if (m_enabled)
|
||||
return DeferredAction::CREATE;
|
||||
}
|
||||
|
||||
if (Atomic::Swap(m_replugQueued, 0))
|
||||
return DeferredAction::REPLUG;
|
||||
{
|
||||
CSRWSharedLock lock(m_lock);
|
||||
if (m_enabled)
|
||||
return DeferredAction::REPLUG;
|
||||
}
|
||||
|
||||
return DeferredAction::NONE;
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include <Windows.h>
|
||||
#include <wdf.h>
|
||||
#include <IddCx.h>
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
|
||||
#include "config/CSettings.h"
|
||||
@@ -61,9 +62,14 @@ private:
|
||||
// IddCx callback threads, the swap-chain thread, and the transport timer.
|
||||
CSRWLock m_lock;
|
||||
|
||||
// Serializes monitor arrival/departure calls. IddCx can finish adapter
|
||||
// initialization on a different thread from transport recovery work.
|
||||
std::mutex m_lifecycleMutex;
|
||||
|
||||
bool m_replugMonitor = false;
|
||||
bool m_replugPending = false;
|
||||
bool m_monitorDeparted = false;
|
||||
bool m_enabled = true;
|
||||
bool m_swapChainAssigned = false;
|
||||
bool m_swapChainReady = false;
|
||||
bool m_waitForSwapChainRelease = false;
|
||||
@@ -77,6 +83,8 @@ private:
|
||||
public:
|
||||
bool Create(UINT connectorIndex, IDDCX_ADAPTER adapter,
|
||||
std::vector<BYTE> edid, CDeviceContext * owner);
|
||||
bool Disable();
|
||||
void Enable();
|
||||
ReplugAction Replug();
|
||||
void RequestMode(const CSettings::DisplayMode& mode);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user