From bbd0c7a99b4d910d5bfd26c4d35d6759204d3c0a Mon Sep 17 00:00:00 2001 From: Geoffrey McRae Date: Fri, 14 Apr 2023 20:40:00 +1000 Subject: [PATCH] [idd] implement cursor shape & position transmission --- idd/LGIdd/CIndirectDeviceContext.cpp | 99 +++++++++++++++++++++++++++ idd/LGIdd/CIndirectDeviceContext.h | 17 +++-- idd/LGIdd/CIndirectMonitorContext.cpp | 72 +++++++++++++++++-- idd/LGIdd/CIndirectMonitorContext.h | 20 ++++-- 4 files changed, 194 insertions(+), 14 deletions(-) diff --git a/idd/LGIdd/CIndirectDeviceContext.cpp b/idd/LGIdd/CIndirectDeviceContext.cpp index b10f7a2a..c3019156 100644 --- a/idd/LGIdd/CIndirectDeviceContext.cpp +++ b/idd/LGIdd/CIndirectDeviceContext.cpp @@ -374,6 +374,9 @@ void CIndirectDeviceContext::LGMPTimer() if (wrapper) wrapper->context->ResendLastFrame(); } + + if (lgmpHostQueueNewSubs(m_pointerQueue)) + ResendCursor(); } void CIndirectDeviceContext::SendFrame(int width, int height, int pitch, DXGI_FORMAT format, void* data) @@ -444,4 +447,100 @@ void CIndirectDeviceContext::SendFrame(int width, int height, int pitch, DXGI_FO lgmpHostQueuePost(m_frameQueue, 0, m_frameMemory[m_frameIndex]); memcpy(fb->data, data, (size_t)height * (size_t)pitch); fb->wp = height * pitch; +} + +void CIndirectDeviceContext::SendCursor(const IDARG_OUT_QUERY_HWCURSOR& info, const BYTE * data) +{ + PLGMPMemory mem; + if (info.CursorShapeInfo.CursorType == IDDCX_CURSOR_SHAPE_TYPE_UNINITIALIZED) + { + mem = m_pointerMemory[m_pointerMemoryIndex]; + if (++m_pointerMemoryIndex == LGMP_Q_POINTER_LEN) + m_pointerMemoryIndex = 0; + } + else + { + mem = m_pointerShapeMemory[m_pointerShapeIndex]; + if (++m_pointerShapeIndex == POINTER_SHAPE_BUFFERS) + m_pointerShapeIndex = 0; + } + + KVMFRCursor * cursor = (KVMFRCursor *)lgmpHostMemPtr(mem); + + m_cursorVisible = info.IsCursorVisible; + m_cursorX = info.X; + m_cursorY = info.Y; + + cursor->x = (int16_t)info.X; + cursor->y = (int16_t)info.Y; + + uint32_t flags = CURSOR_FLAG_POSITION | + (info.IsCursorVisible ? CURSOR_FLAG_VISIBLE : 0); + + if (info.CursorShapeInfo.CursorType != IDDCX_CURSOR_SHAPE_TYPE_UNINITIALIZED) + { + memcpy(cursor + 1, data, + (size_t)(info.CursorShapeInfo.Height * info.CursorShapeInfo.Pitch)); + + cursor->hx = (int8_t )info.CursorShapeInfo.XHot; + cursor->hy = (int8_t )info.CursorShapeInfo.YHot; + cursor->width = (uint32_t)info.CursorShapeInfo.Width; + cursor->height = (uint32_t)info.CursorShapeInfo.Height; + cursor->pitch = (uint32_t)info.CursorShapeInfo.Pitch; + + switch (info.CursorShapeInfo.CursorType) + { + case IDDCX_CURSOR_SHAPE_TYPE_ALPHA: + cursor->type = CURSOR_TYPE_COLOR; + break; + + case IDDCX_CURSOR_SHAPE_TYPE_MASKED_COLOR: + cursor->type = CURSOR_TYPE_MASKED_COLOR; + break; + } + + flags |= CURSOR_FLAG_SHAPE; + m_pointerShape = mem; + } + + LGMP_STATUS status; + while ((status = lgmpHostQueuePost(m_pointerQueue, flags, mem)) != LGMP_OK) + { + if (status == LGMP_ERR_QUEUE_FULL) + { + Sleep(1); + continue; + } + + DBGPRINT("lgmpHostQueuePost Failed (Pointer): %s", lgmpStatusString(status)); + break; + } +} + +void CIndirectDeviceContext::ResendCursor() +{ + PLGMPMemory mem = m_pointerShape; + if (!mem) + return; + + KVMFRCursor* cursor = (KVMFRCursor*)lgmpHostMemPtr(mem); + cursor->x = (int16_t)m_cursorX; + cursor->y = (int16_t)m_cursorY; + + const uint32_t flags = + CURSOR_FLAG_POSITION | CURSOR_FLAG_SHAPE | + (m_cursorVisible ? CURSOR_FLAG_VISIBLE : 0); + + LGMP_STATUS status; + while ((status = lgmpHostQueuePost(m_pointerQueue, flags, mem)) != LGMP_OK) + { + if (status == LGMP_ERR_QUEUE_FULL) + { + Sleep(1); + continue; + } + + DBGPRINT("lgmpHostQueuePost Failed (Pointer): %s", lgmpStatusString(status)); + break; + } } \ No newline at end of file diff --git a/idd/LGIdd/CIndirectDeviceContext.h b/idd/LGIdd/CIndirectDeviceContext.h index 16f96f5d..b6974fa2 100644 --- a/idd/LGIdd/CIndirectDeviceContext.h +++ b/idd/LGIdd/CIndirectDeviceContext.h @@ -21,14 +21,20 @@ private: IDDCX_ADAPTER m_adapter = nullptr; IDDCX_MONITOR m_monitor = nullptr; - CIVSHMEM m_ivshmem; + CIVSHMEM m_ivshmem; - PLGMPHost m_lgmp = nullptr; - PLGMPHostQueue m_frameQueue = nullptr; + PLGMPHost m_lgmp = nullptr; + WDFTIMER m_lgmpTimer = nullptr; + PLGMPHostQueue m_frameQueue = nullptr; PLGMPHostQueue m_pointerQueue = nullptr; PLGMPMemory m_pointerMemory [LGMP_Q_POINTER_LEN ] = {}; PLGMPMemory m_pointerShapeMemory[POINTER_SHAPE_BUFFERS] = {}; + PLGMPMemory m_pointerShape = nullptr; + int m_pointerMemoryIndex = 0; + int m_pointerShapeIndex = 0; + bool m_cursorVisible = false; + int m_cursorX = 0, m_cursorY = 0; size_t m_maxFrameSize = 0; int m_frameIndex = 0; @@ -42,10 +48,8 @@ private: DXGI_FORMAT m_format = DXGI_FORMAT_UNKNOWN; bool SetupLGMP(); - void LGMPTimer(); - - WDFTIMER m_lgmpTimer = nullptr; + void ResendCursor(); public: CIndirectDeviceContext(_In_ WDFDEVICE wdfDevice) : @@ -58,6 +62,7 @@ public: void FinishInit(UINT connectorIndex); void SendFrame(int width, int height, int pitch, DXGI_FORMAT format, void* data); + void SendCursor(const IDARG_OUT_QUERY_HWCURSOR & info, const BYTE * data); }; struct CIndirectDeviceContextWrapper diff --git a/idd/LGIdd/CIndirectMonitorContext.cpp b/idd/LGIdd/CIndirectMonitorContext.cpp index 6d8fe0cd..163248d2 100644 --- a/idd/LGIdd/CIndirectMonitorContext.cpp +++ b/idd/LGIdd/CIndirectMonitorContext.cpp @@ -1,20 +1,27 @@ #include "CIndirectMonitorContext.h" #include "Direct3DDevice.h" +#include "Debug.h" CIndirectMonitorContext::CIndirectMonitorContext(_In_ IDDCX_MONITOR monitor, CIndirectDeviceContext * device) : m_monitor(monitor), m_devContext(device) { + m_terminateEvent .Attach(CreateEvent(nullptr, FALSE, FALSE, nullptr)); + m_cursorDataEvent.Attach(CreateEvent(nullptr, FALSE, FALSE, nullptr)); + m_thread.Attach(CreateThread(nullptr, 0, _CursorThread, this, 0, nullptr)); + m_shapeBuffer = new BYTE[512 * 512 * 4]; } CIndirectMonitorContext::~CIndirectMonitorContext() { - m_thread.reset(); + m_swapChain.reset(); + SetEvent(m_terminateEvent.Get()); + delete[] m_shapeBuffer; } void CIndirectMonitorContext::AssignSwapChain(IDDCX_SWAPCHAIN swapChain, LUID renderAdapter, HANDLE newFrameEvent) { - m_thread.reset(); + m_swapChain.reset(); auto device = std::make_shared(renderAdapter); if (FAILED(device->Init())) { @@ -22,10 +29,67 @@ void CIndirectMonitorContext::AssignSwapChain(IDDCX_SWAPCHAIN swapChain, LUID re return; } - m_thread.reset(new CSwapChainProcessor(m_devContext, swapChain, device, newFrameEvent)); + m_swapChain.reset(new CSwapChainProcessor(m_devContext, swapChain, device, newFrameEvent)); + + IDARG_IN_SETUP_HWCURSOR c = {}; + c.CursorInfo.Size = sizeof(c.CursorInfo); + c.CursorInfo.AlphaCursorSupport = TRUE; + c.CursorInfo.ColorXorCursorSupport = IDDCX_XOR_CURSOR_SUPPORT_FULL; + c.CursorInfo.MaxX = 512; + c.CursorInfo.MaxY = 512; + c.hNewCursorDataAvailable = m_cursorDataEvent.Get(); + NTSTATUS status = IddCxMonitorSetupHardwareCursor(m_monitor, &c); + if (!NT_SUCCESS(status)) + DBGPRINT("IddCxMonitorSetupHardwareCursor Failed: %08x", status); } void CIndirectMonitorContext::UnassignSwapChain() { - m_thread.reset(); + m_swapChain.reset(); +} + +DWORD CALLBACK CIndirectMonitorContext::_CursorThread(LPVOID arg) +{ + reinterpret_cast(arg)->CursorThread(); + return 0; +} + +void CIndirectMonitorContext::CursorThread() +{ + HRESULT hr = 0; + + for (;;) + { + HANDLE waitHandles[] = + { + m_cursorDataEvent.Get(), + m_terminateEvent.Get() + }; + DWORD waitResult = WaitForMultipleObjects(ARRAYSIZE(waitHandles), waitHandles, FALSE, 100); + if (waitResult == WAIT_TIMEOUT) + continue; + else if (waitResult == WAIT_OBJECT_0 + 1) + break; + else if (waitResult != WAIT_OBJECT_0) + { + hr = HRESULT_FROM_WIN32(waitResult); + DBGPRINT("WaitForMultipleObjects: %08", hr); + return; + } + + IDARG_IN_QUERY_HWCURSOR in = {}; + in.LastShapeId = m_lastShapeId; + in.pShapeBuffer = m_shapeBuffer; + in.ShapeBufferSizeInBytes = 512 * 512 * 4; + + IDARG_OUT_QUERY_HWCURSOR out = {}; + NTSTATUS status = IddCxMonitorQueryHardwareCursor(m_monitor, &in, &out); + if (FAILED(status)) + { + DBGPRINT("IddCxMonitorQueryHardwareCursor failed: %08x", status); + return; + } + + m_devContext->SendCursor(out, m_shapeBuffer); + } } \ No newline at end of file diff --git a/idd/LGIdd/CIndirectMonitorContext.h b/idd/LGIdd/CIndirectMonitorContext.h index e233c388..ed34e46c 100644 --- a/idd/LGIdd/CIndirectMonitorContext.h +++ b/idd/LGIdd/CIndirectMonitorContext.h @@ -8,12 +8,24 @@ #include "CIndirectDeviceContext.h" #include "CSwapChainProcessor.h" +using namespace Microsoft::WRL; + class CIndirectMonitorContext { -protected: +private: IDDCX_MONITOR m_monitor; CIndirectDeviceContext * m_devContext; - std::unique_ptr m_thread; + std::unique_ptr m_swapChain; + + Wrappers::Event m_terminateEvent; + Wrappers::Event m_cursorDataEvent; + Wrappers::HandleT m_thread; + BYTE * m_shapeBuffer; + + DWORD m_lastShapeId = 0; + + static DWORD CALLBACK _CursorThread(LPVOID arg); + void CursorThread(); public: CIndirectMonitorContext(_In_ IDDCX_MONITOR monitor, CIndirectDeviceContext * device); @@ -25,8 +37,8 @@ public: inline void ResendLastFrame() { - if (m_thread) - m_thread->ResendLastFrame(); + if (m_swapChain) + m_swapChain->ResendLastFrame(); } };