From b29f1c62bb84a56f2c3f5a98e869b546c16cce31 Mon Sep 17 00:00:00 2001 From: Geoffrey McRae Date: Thu, 24 May 2018 11:24:24 +1000 Subject: [PATCH] [host] update to KVMFR v6 and decouple mouse --- host/Capture/DXGI.cpp | 43 ++++++----- host/Capture/DXGI.h | 8 +- host/Capture/NvFBC.cpp | 2 +- host/Capture/NvFBC.h | 2 +- host/ICapture.h | 4 +- host/Service.cpp | 172 ++++++++++++++++++++++++----------------- host/Service.h | 8 +- host/main.cpp | 2 +- 8 files changed, 140 insertions(+), 101 deletions(-) diff --git a/host/Capture/DXGI.cpp b/host/Capture/DXGI.cpp index 180942c1..be8386e7 100644 --- a/host/Capture/DXGI.cpp +++ b/host/Capture/DXGI.cpp @@ -598,7 +598,7 @@ void DXGI::WaitForDesktop() CloseDesktop(desktop); } -GrabStatus Capture::DXGI::GrabFrameTexture(FrameInfo & frame, ID3D11Texture2DPtr & texture, bool & timeout) +GrabStatus Capture::DXGI::GrabFrameTexture(struct FrameInfo & frame, struct CursorInfo & cursor, ID3D11Texture2DPtr & texture, bool & timeout) { if (!m_initialized) return GRAB_STATUS_ERROR; @@ -653,9 +653,9 @@ GrabStatus Capture::DXGI::GrabFrameTexture(FrameInfo & frame, ID3D11Texture2DPtr m_lastMousePos.y != frameInfo.PointerPosition.Position.y ) { cursorUpdate = true; - frame.cursor.hasPos = true; - frame.cursor.x = frameInfo.PointerPosition.Position.x; - frame.cursor.y = frameInfo.PointerPosition.Position.y; + cursor.hasPos = true; + cursor.x = frameInfo.PointerPosition.Position.x; + cursor.y = frameInfo.PointerPosition.Position.y; m_lastMousePos.x = frameInfo.PointerPosition.Position.x; m_lastMousePos.y = frameInfo.PointerPosition.Position.y; } @@ -666,7 +666,7 @@ GrabStatus Capture::DXGI::GrabFrameTexture(FrameInfo & frame, ID3D11Texture2DPtr m_lastMouseVis = frameInfo.PointerPosition.Visible; } - frame.cursor.visible = m_lastMouseVis == TRUE; + cursor.visible = m_lastMouseVis == TRUE; } // if the pointer shape has changed @@ -691,22 +691,23 @@ GrabStatus Capture::DXGI::GrabFrameTexture(FrameInfo & frame, ID3D11Texture2DPtr switch (shapeInfo.Type) { - case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_COLOR : frame.cursor.type = CURSOR_TYPE_COLOR; break; - case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MASKED_COLOR: frame.cursor.type = CURSOR_TYPE_MASKED_COLOR; break; - case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MONOCHROME : frame.cursor.type = CURSOR_TYPE_MONOCHROME; break; + case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_COLOR : cursor.type = CURSOR_TYPE_COLOR; break; + case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MASKED_COLOR: cursor.type = CURSOR_TYPE_MASKED_COLOR; break; + case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MONOCHROME : cursor.type = CURSOR_TYPE_MONOCHROME; break; default: DEBUG_ERROR("Invalid cursor type"); return GRAB_STATUS_ERROR; } - frame.cursor.hasShape = true; - frame.cursor.shape = m_pointer; - frame.cursor.w = shapeInfo.Width; - frame.cursor.h = shapeInfo.Height; - frame.cursor.pitch = shapeInfo.Pitch; - frame.cursor.dataSize = m_pointerSize; + cursor.hasShape = true; + cursor.shape = m_pointer; + cursor.w = shapeInfo.Width; + cursor.h = shapeInfo.Height; + cursor.pitch = shapeInfo.Pitch; + cursor.dataSize = m_pointerSize; } + // if we also have frame data if (frameInfo.LastPresentTime.QuadPart != 0) break; @@ -753,7 +754,7 @@ GrabStatus Capture::DXGI::GrabFrameTexture(FrameInfo & frame, ID3D11Texture2DPtr return GRAB_STATUS_OK; } -GrabStatus Capture::DXGI::GrabFrameRaw(FrameInfo & frame) +GrabStatus Capture::DXGI::GrabFrameRaw(FrameInfo & frame, struct CursorInfo & cursor) { GrabStatus result; ID3D11Texture2DPtr src; @@ -762,7 +763,7 @@ GrabStatus Capture::DXGI::GrabFrameRaw(FrameInfo & frame) while(true) { TRACE_START("GrabFrame"); - result = GrabFrameTexture(frame, src, timeout); + result = GrabFrameTexture(frame, cursor, src, timeout); TRACE_END; if (result != GRAB_STATUS_OK) return result; @@ -818,7 +819,7 @@ GrabStatus Capture::DXGI::GrabFrameRaw(FrameInfo & frame) return GRAB_STATUS_OK; } -GrabStatus Capture::DXGI::GrabFrameH264(FrameInfo & frame) +GrabStatus Capture::DXGI::GrabFrameH264(struct FrameInfo & frame, struct CursorInfo & cursor) { while(true) { @@ -857,7 +858,7 @@ GrabStatus Capture::DXGI::GrabFrameH264(FrameInfo & frame) while(true) { - result = GrabFrameTexture(frame, src, timeout); + result = GrabFrameTexture(frame, cursor, src, timeout); if (result != GRAB_STATUS_OK) { return result; @@ -961,13 +962,13 @@ GrabStatus Capture::DXGI::GrabFrameH264(FrameInfo & frame) } } -GrabStatus DXGI::GrabFrame(FrameInfo & frame) +GrabStatus DXGI::GrabFrame(struct FrameInfo & frame, struct CursorInfo & cursor) { frame.width = m_width; frame.height = m_height; if (m_frameType == FRAME_TYPE_H264) - return GrabFrameH264(frame); + return GrabFrameH264(frame, cursor); else - return GrabFrameRaw(frame); + return GrabFrameRaw(frame, cursor); } \ No newline at end of file diff --git a/host/Capture/DXGI.h b/host/Capture/DXGI.h index dca992b4..aa450a74 100644 --- a/host/Capture/DXGI.h +++ b/host/Capture/DXGI.h @@ -79,7 +79,7 @@ namespace Capture enum FrameType GetFrameType(); size_t GetMaxFrameSize(); - enum GrabStatus GrabFrame(struct FrameInfo & frame); + enum GrabStatus GrabFrame(struct FrameInfo & frame, struct CursorInfo & cursor); /* Junk needed for the horrid IMFAsyncCallback interface @@ -116,9 +116,9 @@ namespace Capture bool InitRawCapture(); bool InitH264Capture(); - GrabStatus GrabFrameTexture(FrameInfo & frame, ID3D11Texture2DPtr & texture, bool & timeout); - GrabStatus GrabFrameRaw (FrameInfo & frame); - GrabStatus GrabFrameH264 (FrameInfo & frame); + GrabStatus GrabFrameTexture(struct FrameInfo & frame, struct CursorInfo & cursor, ID3D11Texture2DPtr & texture, bool & timeout); + GrabStatus GrabFrameRaw (struct FrameInfo & frame, struct CursorInfo & cursor); + GrabStatus GrabFrameH264 (struct FrameInfo & frame, struct CursorInfo & cursor); void WaitForDesktop(); diff --git a/host/Capture/NvFBC.cpp b/host/Capture/NvFBC.cpp index a883ecdd..c4dfa4d6 100644 --- a/host/Capture/NvFBC.cpp +++ b/host/Capture/NvFBC.cpp @@ -215,7 +215,7 @@ size_t NvFBC::GetMaxFrameSize() return m_maxCaptureWidth * m_maxCaptureHeight * 4; } -enum GrabStatus NvFBC::GrabFrame(struct FrameInfo & frame) +enum GrabStatus NvFBC::GrabFrame(struct FrameInfo & frame, struct CursorInfo & cursor) { if (!m_initialized) return GRAB_STATUS_ERROR; diff --git a/host/Capture/NvFBC.h b/host/Capture/NvFBC.h index 8304715f..7a581f7e 100644 --- a/host/Capture/NvFBC.h +++ b/host/Capture/NvFBC.h @@ -47,7 +47,7 @@ namespace Capture } enum FrameType GetFrameType(); size_t GetMaxFrameSize(); - enum GrabStatus GrabFrame(struct FrameInfo & frame); + enum GrabStatus GrabFrame(struct FrameInfo & frame, struct CursorInfo & cursor); private: CaptureOptions * m_options; diff --git a/host/ICapture.h b/host/ICapture.h index 35062e12..9ca22dd6 100644 --- a/host/ICapture.h +++ b/host/ICapture.h @@ -45,8 +45,6 @@ struct FrameInfo unsigned int pitch; void * buffer; size_t bufferSize; - - struct CursorInfo cursor; }; enum GrabStatus @@ -69,5 +67,5 @@ public: virtual bool ReInitialize() = 0; virtual enum FrameType GetFrameType() = 0; virtual size_t GetMaxFrameSize() = 0; - virtual enum GrabStatus GrabFrame(struct FrameInfo & frame) = 0; + virtual enum GrabStatus GrabFrame(struct FrameInfo & frame, struct CursorInfo & cursor) = 0; }; diff --git a/host/Service.cpp b/host/Service.cpp index e11badb5..bb28f73c 100644 --- a/host/Service.cpp +++ b/host/Service.cpp @@ -94,13 +94,18 @@ bool Service::Initialize(ICapture * captureDevice) return false; } + // Create the cursor thread + m_cursorThread = CreateThread(NULL, 0, _CursorThread, this, 0, NULL); + m_cursorEvent = CreateEvent (NULL, FALSE, FALSE, L"CursorEvent"); + // update everything except for the hostID memcpy(m_shmHeader->magic, KVMFR_HEADER_MAGIC, sizeof(KVMFR_HEADER_MAGIC)); m_shmHeader->version = KVMFR_HEADER_VERSION; - // clear but retain the restart flag if it was set by the client - INTERLOCKED_AND8((char *)&m_shmHeader->flags, KVMFR_HEADER_FLAG_RESTART); - ZeroMemory(&m_shmHeader->detail, sizeof(KVMFRDetail)); + // zero and tell the client we have restarted + ZeroMemory(&m_shmHeader->frame , sizeof(KVMFRFrame )); + ZeroMemory(&m_shmHeader->cursor, sizeof(KVMFRCursor)); + m_shmHeader->flags &= ~KVMFR_HEADER_FLAG_RESTART; m_initialized = true; return true; @@ -159,6 +164,10 @@ void Service::DeInitialize() m_capture = NULL; } + WaitForSingleObject(m_cursorThread, INFINITE); + CloseHandle(m_cursorThread); + CloseHandle(m_cursorEvent ); + m_memory = NULL; m_initialized = false; } @@ -168,11 +177,6 @@ bool Service::Process() if (!m_initialized) return false; - struct FrameInfo frame; - ZeroMemory(&frame, sizeof(FrameInfo)); - frame.buffer = m_frame[m_frameIndex]; - frame.bufferSize = m_frameSize; - volatile uint8_t *flags = &m_shmHeader->flags; // wait for the host to notify that is it is ready to proceed @@ -200,27 +204,25 @@ bool Service::Process() break; } - // check if the client has flagged it's ready - if (!(f & KVMFR_HEADER_FLAG_FRAME)) + // check if the client has flagged it's ready for a frame + if (!(m_shmHeader->frame.flags & KVMFR_FRAME_FLAG_UPDATE)) break; - // wait for 100ns before polling again - LARGE_INTEGER timeout; - timeout.QuadPart = -100; - if (!SetWaitableTimer(m_timer, &timeout, 0, NULL, NULL, FALSE)) - { - DEBUG_ERROR("Failed to set waitable timer"); - return false; - } - WaitForSingleObject(m_timer, INFINITE); + // wait for 2ms before polling again + Sleep(2); } + struct FrameInfo frame; + ZeroMemory(&frame, sizeof(FrameInfo)); + frame.buffer = m_frame[m_frameIndex]; + frame.bufferSize = m_frameSize; + bool ok = false; bool cursorOnly = false; for(int i = 0; i < 2; ++i) { // capture a frame of data - switch (m_capture->GrabFrame(frame)) + switch (m_capture->GrabFrame(frame, m_cursorInfo)) { case GRAB_STATUS_OK: ok = true; @@ -240,8 +242,10 @@ bool Service::Process() if(WTSGetActiveConsoleSessionId() != m_consoleSessionID) { DEBUG_INFO("User switch detected, waiting to regain control"); + *flags |= KVMFR_HEADER_FLAG_PAUSED; while (WTSGetActiveConsoleSessionId() != m_consoleSessionID) Sleep(100); + *flags &= ~KVMFR_HEADER_FLAG_PAUSED; } if (!m_capture->ReInitialize()) @@ -268,64 +272,94 @@ bool Service::Process() return false; } - uint8_t updateFlags = 0; + if (m_cursorInfo.hasPos || m_cursorInfo.hasShape) + SetEvent(m_cursorEvent); if (!cursorOnly) { - // signal a frame update - updateFlags |= KVMFR_HEADER_FLAG_FRAME; - m_detail.frame.type = m_capture->GetFrameType(); - m_detail.frame.width = frame.width; - m_detail.frame.height = frame.height; - m_detail.frame.stride = frame.stride; - m_detail.frame.pitch = frame.pitch; - m_detail.frame.dataPos = m_dataOffset[m_frameIndex]; + KVMFRFrame * fi = &m_shmHeader->frame; + + fi->type = m_capture->GetFrameType(); + fi->width = frame.width; + fi->height = frame.height; + fi->stride = frame.stride; + fi->pitch = frame.pitch; + fi->dataPos = m_dataOffset[m_frameIndex]; if (++m_frameIndex == 2) m_frameIndex = 0; + + // signal a frame update + fi->flags |= KVMFR_FRAME_FLAG_UPDATE; } - if (frame.cursor.hasPos) - { - // tell the host where the cursor is - updateFlags |= KVMFR_HEADER_FLAG_CURSOR; - m_detail.cursor.flags |= KVMFR_CURSOR_FLAG_POS; - m_detail.cursor.x = frame.cursor.x; - m_detail.cursor.y = frame.cursor.y; - - if (frame.cursor.visible) - m_detail.cursor.flags |= KVMFR_CURSOR_FLAG_VISIBLE; - else - m_detail.cursor.flags &= ~KVMFR_CURSOR_FLAG_VISIBLE; - } - - if (frame.cursor.hasShape) - { - if (frame.cursor.dataSize > m_cursorDataSize) - { - DEBUG_ERROR("Cursor size exceeds allocated space"); - return false; - } - - // give the host the new cursor shape - updateFlags |= KVMFR_HEADER_FLAG_CURSOR; - m_detail.cursor.flags |= KVMFR_CURSOR_FLAG_SHAPE; - ++m_detail.cursor.version; - - m_detail.cursor.type = frame.cursor.type; - m_detail.cursor.width = frame.cursor.w; - m_detail.cursor.height = frame.cursor.h; - m_detail.cursor.pitch = frame.cursor.pitch; - m_detail.cursor.dataPos = m_cursorOffset; - memcpy(m_cursorData, frame.cursor.shape, frame.cursor.dataSize); - } - - // update the shared details only - memcpy(&m_shmHeader->detail, &m_detail, sizeof(KVMFRDetail)); - // update the flags INTERLOCKED_AND8((volatile char *)flags, KVMFR_HEADER_FLAG_RESTART); - INTERLOCKED_OR8 ((volatile char *)flags, updateFlags); - _mm_sfence(); +// _mm_sfence(); return true; +} + +DWORD Service::CursorThread() +{ + while(m_capture) + { + if (WaitForSingleObject(m_cursorEvent, 1000) != WAIT_OBJECT_0) + continue; + + KVMFRCursor * cursor = &m_shmHeader->cursor; + // wait until the client is ready + while (cursor->flags != 0) + { + Sleep(2); + if (!m_capture) + return 0; + } + + struct CursorInfo ci; + memcpy(&ci, &m_cursorInfo, sizeof(ci)); + ZeroMemory(&m_cursorInfo, sizeof(m_cursorInfo)); + + if (ci.hasPos) + { + ci.hasPos = false; + + // tell the client where the cursor is + cursor->flags |= KVMFR_CURSOR_FLAG_POS; + cursor->x = ci.x; + cursor->y = ci.y; + + if (ci.visible) + cursor->flags |= KVMFR_CURSOR_FLAG_VISIBLE; + else + cursor->flags &= ~KVMFR_CURSOR_FLAG_VISIBLE; + } + + if (ci.hasShape) + { + ci.hasShape = false; + + if (ci.dataSize > m_cursorDataSize) + { + DEBUG_ERROR("Cursor size exceeds allocated space"); + cursor->flags = 0; + continue; + } + + // give the client the new cursor shape + cursor->flags |= KVMFR_CURSOR_FLAG_SHAPE; + ++cursor->version; + + cursor->type = ci.type; + cursor->width = ci.w; + cursor->height = ci.h; + cursor->pitch = ci.pitch; + cursor->dataPos = m_cursorOffset; + + memcpy(m_cursorData, ci.shape, ci.dataSize); + } + + cursor->flags |= KVMFR_CURSOR_FLAG_UPDATE; + } + + return 0; } \ No newline at end of file diff --git a/host/Service.h b/host/Service.h index 75641888..2ac86f8d 100644 --- a/host/Service.h +++ b/host/Service.h @@ -55,13 +55,19 @@ private: HANDLE m_timer; ICapture * m_capture; - KVMFRDetail m_detail; KVMFRHeader * m_shmHeader; + uint8_t * m_frame[2]; size_t m_frameSize; uint64_t m_dataOffset[2]; int m_frameIndex; + static DWORD WINAPI _CursorThread(LPVOID lpParameter) { return ((Service *)lpParameter)->CursorThread(); } + DWORD CursorThread(); + + HANDLE m_cursorThread; + HANDLE m_cursorEvent; + CursorInfo m_cursorInfo; size_t m_cursorDataSize; uint8_t * m_cursorData; uint64_t m_cursorOffset; diff --git a/host/main.cpp b/host/main.cpp index a1f3ef5d..1e7fe4e8 100644 --- a/host/main.cpp +++ b/host/main.cpp @@ -29,7 +29,7 @@ Place, Suite 330, Boston, MA 02111-1307 USA #include "Service.h" #include -#include +#include #include int parseArgs(struct StartupArgs & args);