[host] use a global hook to obtain cursor move pos

This commit is contained in:
Geoffrey McRae 2019-01-11 23:58:50 +11:00
parent 5518ccb795
commit 1ef61f6cd3
7 changed files with 124 additions and 80 deletions

View file

@ -539,8 +539,6 @@ unsigned int Capture::DXGI::Capture()
cursor.w = shapeInfo.Width; cursor.w = shapeInfo.Width;
cursor.h = shapeInfo.Height; cursor.h = shapeInfo.Height;
cursor.pitch = shapeInfo.Pitch; cursor.pitch = shapeInfo.Pitch;
m_hotSpot.x = shapeInfo.HotSpot.x;
m_hotSpot.y = shapeInfo.HotSpot.y;
} }
// if we have a mouse update // if we have a mouse update
@ -556,24 +554,6 @@ unsigned int Capture::DXGI::Capture()
cursor.y = m_lastCursorY = frameInfo.PointerPosition.Position.y; cursor.y = m_lastCursorY = frameInfo.PointerPosition.Position.y;
} }
} }
else
{
// always report the mouse position to prevent the guest losing sync (ie: dragging windows)
POINT curPos;
if (GetCursorPos(&curPos))
{
curPos.x -= m_hotSpot.x;
curPos.y -= m_hotSpot.y;
if (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 (m_lastMouseVis != frameInfo.PointerPosition.Visible) if (m_lastMouseVis != frameInfo.PointerPosition.Visible)
m_lastMouseVis = frameInfo.PointerPosition.Visible; m_lastMouseVis = frameInfo.PointerPosition.Visible;

View file

@ -102,6 +102,5 @@ namespace Capture
int m_lastCursorX, m_lastCursorY; int m_lastCursorX, m_lastCursorY;
BOOL m_lastMouseVis; BOOL m_lastMouseVis;
POINT m_hotSpot;
}; };
}; };

View file

@ -41,6 +41,7 @@ NvFBC::NvFBC() :
m_optNoCrop(false), m_optNoCrop(false),
m_optNoWait(false), m_optNoWait(false),
m_initialized(false), m_initialized(false),
m_first(true),
m_hDLL(NULL), m_hDLL(NULL),
m_nvFBC(NULL) m_nvFBC(NULL)
{ {
@ -60,6 +61,7 @@ bool NvFBC::Initialize(CaptureOptions * options)
if (m_initialized) if (m_initialized)
DeInitialize(); DeInitialize();
m_first = true;
m_options = options; m_options = options;
m_optNoCrop = false; m_optNoCrop = false;
@ -345,7 +347,11 @@ unsigned int Capture::NvFBC::Capture()
return GRAB_STATUS_REINIT; return GRAB_STATUS_REINIT;
} }
return GRAB_STATUS_OK | GRAB_STATUS_FRAME; // turn off the cursor on the first frame as NvFBC is drawing it
if (m_first)
return GRAB_STATUS_OK | GRAB_STATUS_FRAME | GRAB_STATUS_CURSOR;
else
return GRAB_STATUS_OK | GRAB_STATUS_FRAME;
} }
bool Capture::NvFBC::GetCursor(CursorInfo & cursor) bool Capture::NvFBC::GetCursor(CursorInfo & cursor)
@ -353,6 +359,13 @@ bool Capture::NvFBC::GetCursor(CursorInfo & cursor)
cursor.hasShape = false; cursor.hasShape = false;
cursor.hasPos = false; cursor.hasPos = false;
cursor.visible = false; cursor.visible = false;
if (m_first)
{
m_first = false;
return true;
}
return false; return false;
} }

View file

@ -59,6 +59,7 @@ namespace Capture
bool m_optNoWait; bool m_optNoWait;
bool m_initialized; bool m_initialized;
bool m_first;
HMODULE m_hDLL; HMODULE m_hDLL;
NvFBC_CreateFunctionExType m_fnCreateEx; NvFBC_CreateFunctionExType m_fnCreateEx;

View file

@ -27,8 +27,6 @@ Place, Suite 330, Boston, MA 02111-1307 USA
#include "Util.h" #include "Util.h"
#include "CaptureFactory.h" #include "CaptureFactory.h"
Service * Service::m_instance = NULL;
Service::Service() : Service::Service() :
m_initialized(false), m_initialized(false),
m_memory(NULL), m_memory(NULL),
@ -41,10 +39,39 @@ Service::Service() :
{ {
m_consoleSessionID = WTSGetActiveConsoleSessionId(); m_consoleSessionID = WTSGetActiveConsoleSessionId();
m_ivshmem = IVSHMEM::Get(); m_ivshmem = IVSHMEM::Get();
if (!m_ivshmem->Initialize())
throw "IVSHMEM failed to initalize";
if (m_ivshmem->GetSize() < sizeof(KVMFRHeader))
throw "Shared memory is not large enough for the KVMFRHeader";
m_memory = static_cast<uint8_t*>(m_ivshmem->GetMemory());
if (!m_memory)
throw "Failed to get IVSHMEM memory";
if (!InitPointers())
throw "Failed to initialize the shared memory pointers";
} }
Service::~Service() Service::~Service()
{ {
DeInitialize();
}
LRESULT Service::LowLevelMouseProc(int nCode, WPARAM wParam, LPARAM lParam)
{
if (nCode == HC_ACTION && wParam == WM_MOUSEMOVE)
{
MSLLHOOKSTRUCT *msg = (MSLLHOOKSTRUCT *)lParam;
volatile KVMFRCursor * cursor = &(m_shmHeader->cursor);
volatile char * flags = (volatile char *)&(cursor->flags);
cursor->x = msg->pt.x;
cursor->y = msg->pt.y;
INTERLOCKED_OR8(flags, KVMFR_CURSOR_FLAG_POS);
}
return CallNextHookEx(m_mouseHook, nCode, wParam, lParam);
} }
bool Service::Initialize(ICapture * captureDevice) bool Service::Initialize(ICapture * captureDevice)
@ -52,35 +79,8 @@ bool Service::Initialize(ICapture * captureDevice)
if (m_initialized) if (m_initialized)
DeInitialize(); DeInitialize();
m_tryTarget = 0; m_tryTarget = 0;
m_capture = captureDevice; m_capture = captureDevice;
if (!m_ivshmem->Initialize())
{
DEBUG_ERROR("IVSHMEM failed to initalize");
DeInitialize();
return false;
}
if (m_ivshmem->GetSize() < sizeof(KVMFRHeader))
{
DEBUG_ERROR("Shared memory is not large enough for the KVMFRHeader");
DeInitialize();
return false;
}
m_memory = static_cast<uint8_t*>(m_ivshmem->GetMemory());
if (!m_memory)
{
DEBUG_ERROR("Failed to get IVSHMEM memory");
DeInitialize();
return false;
}
if (!InitPointers())
{
DeInitialize();
return false;
}
if (m_capture->GetMaxFrameSize() > m_frameSize) if (m_capture->GetMaxFrameSize() > m_frameSize)
{ {
@ -90,7 +90,7 @@ bool Service::Initialize(ICapture * captureDevice)
} }
// Create the cursor thread // Create the cursor thread
m_cursorThread = CreateThread(NULL, 0, _CursorThread, this, 0, NULL); m_cursorThread = CreateThread(NULL, 0, _CursorThread, NULL, 0, NULL);
m_cursorEvent = CreateEvent (NULL, FALSE, FALSE, L"CursorEvent"); m_cursorEvent = CreateEvent (NULL, FALSE, FALSE, L"CursorEvent");
InitializeCriticalSection(&m_cursorCS); InitializeCriticalSection(&m_cursorCS);
@ -340,21 +340,20 @@ DWORD Service::CursorThread()
{ {
volatile KVMFRCursor * cursor = &(m_shmHeader->cursor); volatile KVMFRCursor * cursor = &(m_shmHeader->cursor);
// wait until the client is ready // wait until the client is ready
while (cursor->flags != 0) while ((cursor->flags & ~KVMFR_CURSOR_FLAG_POS) != 0)
{ {
Sleep(1); Sleep(1);
if (!m_capture) if (!m_capture)
return 0; return 0;
} }
uint8_t flags = 0; uint8_t flags = cursor->flags;
if (ci.hasPos) if (ci.hasPos)
{ {
// tell the client where the cursor is
flags |= KVMFR_CURSOR_FLAG_POS;
cursor->x = ci.x; cursor->x = ci.x;
cursor->y = ci.y; cursor->y = ci.y;
flags |= KVMFR_CURSOR_FLAG_POS;
} }
if (ci.hasShape) if (ci.hasShape)

View file

@ -25,6 +25,7 @@ Place, Suite 330, Boston, MA 02111-1307 USA
#include "IVSHMEM.h" #include "IVSHMEM.h"
#include "ICapture.h" #include "ICapture.h"
#include "common/debug.h"
#define MAX_FRAMES 2 #define MAX_FRAMES 2
@ -38,11 +39,26 @@ enum ProcessStatus
class Service class Service
{ {
public: public:
static Service * Get() static Service & Instance()
{ {
if (!m_instance) static Service service;
m_instance = new Service(); return service;
return m_instance; }
static void InstallHook()
{
if (Instance().m_mouseHook)
RemoveHook();
Instance().m_mouseHook = SetWindowsHookEx(WH_MOUSE_LL, _LowLevelMouseProc, NULL, 0);
}
static void RemoveHook()
{
if (Instance().m_mouseHook)
{
UnhookWindowsHookEx(Instance().m_mouseHook);
Instance().m_mouseHook = NULL;
}
} }
bool Initialize(ICapture * captureDevice); bool Initialize(ICapture * captureDevice);
@ -52,13 +68,16 @@ public:
private: private:
bool InitPointers(); bool InitPointers();
static Service * m_instance;
int m_tryTarget; int m_tryTarget;
int m_lastTryCount; int m_lastTryCount;
Service(); Service();
~Service(); ~Service();
HHOOK m_mouseHook;
LRESULT LowLevelMouseProc(int nCode, WPARAM wParam, LPARAM lParam);
static LRESULT WINAPI _LowLevelMouseProc(int nCode, WPARAM wParam, LPARAM lParam) { return Service::Instance().LowLevelMouseProc(nCode, wParam, lParam); }
bool ReInit(volatile char * flags); bool ReInit(volatile char * flags);
bool m_initialized; bool m_initialized;
@ -77,7 +96,7 @@ private:
uint64_t m_dataOffset[MAX_FRAMES]; uint64_t m_dataOffset[MAX_FRAMES];
int m_frameIndex; int m_frameIndex;
static DWORD WINAPI _CursorThread(LPVOID lpParameter) { return ((Service *)lpParameter)->CursorThread(); } static DWORD WINAPI _CursorThread(LPVOID lpParameter) { return Service::Instance().CursorThread(); }
DWORD CursorThread(); DWORD CursorThread();
HANDLE m_cursorThread; HANDLE m_cursorThread;

View file

@ -34,11 +34,13 @@ Place, Suite 330, Boston, MA 02111-1307 USA
#include <iostream> #include <iostream>
int parseArgs(struct StartupArgs & args); int parseArgs(struct StartupArgs & args);
int run(struct StartupArgs & args); static DWORD WINAPI CaptureThread(LPVOID lpParameter);
int run();
void doHelp(); void doHelp();
void doLicense(); void doLicense();
bool running = true;
bool consoleActive = false; bool consoleActive = false;
void setupConsole(); void setupConsole();
@ -50,6 +52,7 @@ struct StartupArgs
const char * captureDevice; const char * captureDevice;
CaptureOptions captureOptions; CaptureOptions captureOptions;
}; };
struct StartupArgs args;
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdParam, int iCmdShow) int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdParam, int iCmdShow)
{ {
@ -57,21 +60,41 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdParam
TraceUtil::Initialize(); TraceUtil::Initialize();
CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE); CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
struct StartupArgs args;
args.foreground = false; args.foreground = false;
args.captureDevice = NULL; args.captureDevice = NULL;
int ret = parseArgs(args); int ret = parseArgs(args);
if (ret == 0) if (ret != 0)
fprintf(stderr, "Failed to parse command line arguments\n");
else
{ {
ret = run(args); if (args.foreground)
if (ret != 0) setupConsole();
Service::InstallHook();
HANDLE captureThread = CreateThread(NULL, 0, CaptureThread, NULL, 0, NULL);
while (running)
{ {
if (!args.foreground) MSG msg;
BOOL bRet = GetMessage(&msg, NULL, 0, 0);
if (bRet == -1 || bRet == 0)
{ {
setupConsole(); ret = msg.wParam;
fprintf(stderr, "An error occurred, re-run in forground mode (-f) for more information\n"); break;
} }
DispatchMessage(&msg);
}
Service::RemoveHook();
running = false;
ret = WaitForSingleObject(captureThread, INFINITE);
CloseHandle(captureThread);
}
if (ret != 0)
{
if (!args.foreground)
{
setupConsole();
fprintf(stderr, "An error occurred, re-run in forground mode (-f) for more information\n");
} }
} }
@ -85,11 +108,21 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdParam
return ret; return ret;
} }
int run(struct StartupArgs & args) static DWORD WINAPI CaptureThread(LPVOID lpParameter)
{ {
if (args.foreground) int ret = 0;
setupConsole(); while (running)
{
ret = run();
if (ret != 0)
break;
}
running = false;
return ret;
}
int run()
{
/* increase the system timer resolution */ /* increase the system timer resolution */
ULONG currentRes; ULONG currentRes;
NtSetTimerResolution(0, TRUE, &currentRes); NtSetTimerResolution(0, TRUE, &currentRes);
@ -124,15 +157,15 @@ int run(struct StartupArgs & args)
return -1; return -1;
} }
Service *svc = svc->Get(); Service &svc = Service::Instance();
if (!svc->Initialize(captureDevice)) if (!svc.Initialize(captureDevice))
return -1; return -1;
int retry = 0; int retry = 0;
bool running = true; bool running = true;
while (running) while (running)
{ {
switch (svc->Process()) switch (svc.Process())
{ {
case PROCESS_STATUS_OK: case PROCESS_STATUS_OK:
retry = 0; retry = 0;
@ -152,7 +185,7 @@ int run(struct StartupArgs & args)
} }
} }
svc->DeInitialize(); svc.DeInitialize();
if (task) if (task)
AvRevertMmThreadCharacteristics(task); AvRevertMmThreadCharacteristics(task);