[host] improve mouse sync with the client

This commit is contained in:
Geoffrey McRae 2018-10-09 17:48:44 +11:00
parent d8b4d0c1ce
commit db907b1b67
4 changed files with 101 additions and 83 deletions

View File

@ -350,20 +350,20 @@ size_t DXGI::GetMaxFrameSize()
return (m_width * m_height * 4); return (m_width * m_height * 4);
} }
GrabStatus Capture::DXGI::Capture() unsigned int Capture::DXGI::Capture()
{ {
if (!m_initialized) if (!m_initialized)
return GRAB_STATUS_ERROR; return GRAB_STATUS_ERROR;
CursorInfo cursor = { 0 }; CursorInfo cursor = { 0 };
bool cursorUpdated = false;
DXGI_OUTDUPL_FRAME_INFO frameInfo; DXGI_OUTDUPL_FRAME_INFO frameInfo;
IDXGIResourcePtr res; IDXGIResourcePtr res;
unsigned int ret;
HRESULT status; HRESULT status;
for (int retryCount = 0; retryCount < 2; ++retryCount) for (int retryCount = 0; retryCount < 2; ++retryCount)
{ {
GrabStatus ret = ReleaseFrame(); ret = ReleaseFrame();
if (ret != GRAB_STATUS_OK) if (ret != GRAB_STATUS_OK)
return ret; return ret;
@ -396,7 +396,7 @@ GrabStatus Capture::DXGI::Capture()
m_lastCursorX != frameInfo.PointerPosition.Position.x || m_lastCursorX != frameInfo.PointerPosition.Position.x ||
m_lastCursorY != frameInfo.PointerPosition.Position.y m_lastCursorY != frameInfo.PointerPosition.Position.y
) { ) {
cursorUpdated = true; ret |= GRAB_STATUS_CURSOR;
cursor.hasPos = true; cursor.hasPos = true;
cursor.x = m_lastCursorX = frameInfo.PointerPosition.Position.x; cursor.x = m_lastCursorX = frameInfo.PointerPosition.Position.x;
cursor.y = m_lastCursorY = frameInfo.PointerPosition.Position.y; cursor.y = m_lastCursorY = frameInfo.PointerPosition.Position.y;
@ -404,12 +404,24 @@ GrabStatus Capture::DXGI::Capture()
if (m_lastMouseVis != frameInfo.PointerPosition.Visible) if (m_lastMouseVis != frameInfo.PointerPosition.Visible)
{ {
cursorUpdated = true; ret |= GRAB_STATUS_CURSOR;
m_lastMouseVis = frameInfo.PointerPosition.Visible; m_lastMouseVis = frameInfo.PointerPosition.Visible;
} }
cursor.visible = m_lastMouseVis == TRUE; cursor.visible = m_lastMouseVis == TRUE;
} }
else
{
// always report the mouse position to prevent the guest losing sync (ie: dragging windows)
POINT curPos;
if (GetCursorPos(&curPos) && (curPos.x != m_lastCursorX || curPos.y != m_lastCursorY))
{
ret |= GRAB_STATUS_CURSOR;
cursor.hasPos = true;
cursor.x = m_lastCursorX = curPos.x;
cursor.y = m_lastCursorY = curPos.y;
}
}
// if the pointer shape has changed // if the pointer shape has changed
if (frameInfo.PointerShapeBufferSize > 0) if (frameInfo.PointerShapeBufferSize > 0)
@ -442,7 +454,7 @@ GrabStatus Capture::DXGI::Capture()
buf->pointerSize = 0; buf->pointerSize = 0;
cursor.shape = buf; cursor.shape = buf;
cursorUpdated = true; ret |= GRAB_STATUS_CURSOR;
DXGI_OUTDUPL_POINTER_SHAPE_INFO shapeInfo; DXGI_OUTDUPL_POINTER_SHAPE_INFO shapeInfo;
status = m_dup->GetFramePointerShape(buf->bufferSize, buf->buffer, &buf->pointerSize, &shapeInfo); status = m_dup->GetFramePointerShape(buf->bufferSize, buf->buffer, &buf->pointerSize, &shapeInfo);
@ -467,7 +479,7 @@ GrabStatus Capture::DXGI::Capture()
cursor.pitch = shapeInfo.Pitch; cursor.pitch = shapeInfo.Pitch;
} }
if (cursorUpdated) if (ret & GRAB_STATUS_CURSOR)
{ {
// push the cursor update into the queue // push the cursor update into the queue
EnterCriticalSection(&m_cursorCS); EnterCriticalSection(&m_cursorCS);
@ -479,20 +491,23 @@ GrabStatus Capture::DXGI::Capture()
if (frameInfo.LastPresentTime.QuadPart == 0) if (frameInfo.LastPresentTime.QuadPart == 0)
{ {
// if there is nothing to update, just start again // if there is nothing to update, just start again
if (!cursorUpdated) if (!ret)
{ {
--retryCount; --retryCount;
continue; continue;
} }
res = NULL; res = NULL;
return GRAB_STATUS_CURSOR; ret |= GRAB_STATUS_OK;
return ret;
} }
// success, break out of the retry loop // success, break out of the retry loop
break; break;
} }
ret |= GRAB_STATUS_FRAME;
// ensure we have a frame // ensure we have a frame
if (!m_releaseFrame) if (!m_releaseFrame)
{ {
@ -508,7 +523,8 @@ GrabStatus Capture::DXGI::Capture()
return GRAB_STATUS_ERROR; return GRAB_STATUS_ERROR;
} }
return GRAB_STATUS_OK; ret |= GRAB_STATUS_OK;
return ret;
} }
GrabStatus Capture::DXGI::ReleaseFrame() GrabStatus Capture::DXGI::ReleaseFrame()

View File

@ -58,7 +58,7 @@ namespace Capture
enum FrameType GetFrameType(); enum FrameType GetFrameType();
size_t GetMaxFrameSize(); size_t GetMaxFrameSize();
GrabStatus Capture(); unsigned int Capture();
GrabStatus GetFrame (struct FrameInfo & frame ); GrabStatus GetFrame (struct FrameInfo & frame );
bool GetCursor(CursorInfo & cursor); bool GetCursor(CursorInfo & cursor);
void FreeCursor(CursorInfo & cursor); void FreeCursor(CursorInfo & cursor);

View File

@ -54,11 +54,12 @@ struct FrameInfo
enum GrabStatus enum GrabStatus
{ {
GRAB_STATUS_OK, GRAB_STATUS_OK = 1,
GRAB_STATUS_TIMEOUT, GRAB_STATUS_TIMEOUT = 2,
GRAB_STATUS_REINIT, GRAB_STATUS_REINIT = 4,
GRAB_STATUS_CURSOR, GRAB_STATUS_CURSOR = 8,
GRAB_STATUS_ERROR GRAB_STATUS_FRAME = 16,
GRAB_STATUS_ERROR = 32
}; };
typedef std::vector<const char *> CaptureOptions; typedef std::vector<const char *> CaptureOptions;
@ -74,7 +75,7 @@ public:
virtual bool ReInitialize() = 0; virtual bool ReInitialize() = 0;
virtual enum FrameType GetFrameType() = 0; virtual enum FrameType GetFrameType() = 0;
virtual size_t GetMaxFrameSize() = 0; virtual size_t GetMaxFrameSize() = 0;
virtual enum GrabStatus Capture() = 0; virtual unsigned int Capture() = 0;
virtual enum GrabStatus GetFrame(struct FrameInfo & frame) = 0; virtual enum GrabStatus GetFrame(struct FrameInfo & frame) = 0;
virtual bool GetCursor(CursorInfo & cursor) = 0; virtual bool GetCursor(CursorInfo & cursor) = 0;
virtual void FreeCursor(CursorInfo & cursor) = 0; virtual void FreeCursor(CursorInfo & cursor) = 0;

View File

@ -193,87 +193,88 @@ bool Service::Process()
INTERLOCKED_AND8(flags, ~(KVMFR_HEADER_FLAG_RESTART)); INTERLOCKED_AND8(flags, ~(KVMFR_HEADER_FLAG_RESTART));
} }
GrabStatus result; unsigned int status;
bool ok = false; bool ok = false;
bool cursorOnly = false;
bool repeat = false; bool repeat = false;
for(int i = 0; i < 2; ++i) for(int i = 0; i < 2; ++i)
{ {
// capture a frame of data status = m_capture->Capture();
switch (m_capture->Capture()) if (status & GRAB_STATUS_OK)
{ {
case GRAB_STATUS_OK: ok = true;
ok = true; break;
break;
case GRAB_STATUS_TIMEOUT:
if (m_haveFrame)
{
ok = true;
repeat = true;
break;
}
// capture timeouts are not errors
--i;
continue;
case GRAB_STATUS_CURSOR:
ok = true;
cursorOnly = true;
break;
case GRAB_STATUS_ERROR:
DEBUG_ERROR("Capture failed");
return false;
case GRAB_STATUS_REINIT:
DEBUG_INFO("ReInitialize Requested");
INTERLOCKED_OR8(flags, KVMFR_HEADER_FLAG_PAUSED);
if(WTSGetActiveConsoleSessionId() != m_consoleSessionID)
{
DEBUG_INFO("User switch detected, waiting to regain control");
while (WTSGetActiveConsoleSessionId() != m_consoleSessionID)
Sleep(100);
}
while (!m_capture->CanInitialize())
Sleep(100);
if (!m_capture->ReInitialize())
{
DEBUG_ERROR("ReInitialize Failed");
return false;
}
if (m_capture->GetMaxFrameSize() > m_frameSize)
{
DEBUG_ERROR("Maximum frame size of %zd bytes excceds maximum space available", m_capture->GetMaxFrameSize());
return false;
}
INTERLOCKED_AND8(flags, ~KVMFR_HEADER_FLAG_PAUSED);
// re-init request should not count towards a failure to capture
--i;
continue;
} }
if (ok) if (status & GRAB_STATUS_TIMEOUT)
break; {
if (m_haveFrame)
{
ok = true;
repeat = true;
break;
}
// timeouts should not count towards a failure to capture
--i;
continue;
}
if (status & GRAB_STATUS_ERROR)
{
DEBUG_ERROR("Capture failed, retrying");
continue;
}
if (status & GRAB_STATUS_REINIT)
{
DEBUG_INFO("ReInitialize Requested");
INTERLOCKED_OR8(flags, KVMFR_HEADER_FLAG_PAUSED);
if (WTSGetActiveConsoleSessionId() != m_consoleSessionID)
{
DEBUG_INFO("User switch detected, waiting to regain control");
while (WTSGetActiveConsoleSessionId() != m_consoleSessionID)
Sleep(100);
}
while (!m_capture->CanInitialize())
Sleep(100);
if (!m_capture->ReInitialize())
{
DEBUG_ERROR("ReInitialize Failed");
return false;
}
if (m_capture->GetMaxFrameSize() > m_frameSize)
{
DEBUG_ERROR("Maximum frame size of %zd bytes excceds maximum space available", m_capture->GetMaxFrameSize());
return false;
}
INTERLOCKED_AND8(flags, ~KVMFR_HEADER_FLAG_PAUSED);
// re-init request should not count towards a failure to capture
--i;
continue;
}
DEBUG_ERROR("Capture interface returned an unexpected result");
return false;
} }
if (!ok) if (!ok)
{ {
DEBUG_ERROR("Capture retry count exceeded"); DEBUG_ERROR("Capture retry count exceeded");
return false; return false;
} }
SetEvent(m_cursorEvent); if (status & GRAB_STATUS_CURSOR)
SetEvent(m_cursorEvent);
if (!cursorOnly) if (status & GRAB_STATUS_FRAME)
{ {
volatile KVMFRFrame * fi = &(m_shmHeader->frame); volatile KVMFRFrame * fi = &(m_shmHeader->frame);
@ -284,7 +285,7 @@ bool Service::Process()
frame.buffer = m_frame[m_frameIndex]; frame.buffer = m_frame[m_frameIndex];
frame.bufferSize = m_frameSize; frame.bufferSize = m_frameSize;
result = m_capture->GetFrame(frame); GrabStatus result = m_capture->GetFrame(frame);
if (result != GRAB_STATUS_OK) if (result != GRAB_STATUS_OK)
{ {
DEBUG_INFO("GetFrame failed"); DEBUG_INFO("GetFrame failed");