mirror of
https://github.com/gnif/LookingGlass.git
synced 2026-08-09 08:41:31 +00:00
[idd] input: preserve reports under backpressure
Move named pipe writes off the LGMP input worker so a stalled LGInput endpoint cannot block queue draining or lease maintenance. Coalesce motion only under queue pressure while preserving mode, button, wheel, and keyboard transitions. Reset HID state after discontinuities and carry all 32 mouse button bits through the pipe and HID reports.
This commit is contained in:
@@ -130,15 +130,9 @@ CPipeEndpoint::PipeIoResult CPipeEndpoint::WriteMessage(
|
|||||||
const void * message,
|
const void * message,
|
||||||
DWORD messageSize)
|
DWORD messageSize)
|
||||||
{
|
{
|
||||||
HANDLE ioEvent = CreateEventW(nullptr, TRUE, FALSE, nullptr);
|
ResetEvent(m_writeEvent);
|
||||||
if (!ioEvent)
|
|
||||||
{
|
|
||||||
DEBUG_ERROR_HR(GetLastError(), "Failed to create named pipe write event");
|
|
||||||
return PipeIoResult::Error;
|
|
||||||
}
|
|
||||||
|
|
||||||
OVERLAPPED overlapped = {};
|
OVERLAPPED overlapped = {};
|
||||||
overlapped.hEvent = ioEvent;
|
overlapped.hEvent = m_writeEvent;
|
||||||
DWORD bytesWritten = 0;
|
DWORD bytesWritten = 0;
|
||||||
PipeIoResult result = PipeIoResult::Success;
|
PipeIoResult result = PipeIoResult::Success;
|
||||||
|
|
||||||
@@ -148,7 +142,7 @@ CPipeEndpoint::PipeIoResult CPipeEndpoint::WriteMessage(
|
|||||||
if (error == ERROR_IO_PENDING)
|
if (error == ERROR_IO_PENDING)
|
||||||
result = WaitForOverlapped(
|
result = WaitForOverlapped(
|
||||||
pipe,
|
pipe,
|
||||||
ioEvent,
|
m_writeEvent,
|
||||||
&overlapped,
|
&overlapped,
|
||||||
&bytesWritten,
|
&bytesWritten,
|
||||||
WRITE_TIMEOUT_MS);
|
WRITE_TIMEOUT_MS);
|
||||||
@@ -174,7 +168,6 @@ CPipeEndpoint::PipeIoResult CPipeEndpoint::WriteMessage(
|
|||||||
result = PipeIoResult::Error;
|
result = PipeIoResult::Error;
|
||||||
}
|
}
|
||||||
|
|
||||||
CloseHandle(ioEvent);
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -197,9 +190,16 @@ bool CPipeEndpoint::Start(
|
|||||||
m_mode = mode;
|
m_mode = mode;
|
||||||
m_messageSize = messageSize;
|
m_messageSize = messageSize;
|
||||||
m_stopEvent = CreateEventW(nullptr, TRUE, FALSE, nullptr);
|
m_stopEvent = CreateEventW(nullptr, TRUE, FALSE, nullptr);
|
||||||
if (!m_stopEvent)
|
m_writeEvent = CreateEventW(nullptr, TRUE, FALSE, nullptr);
|
||||||
|
if (!m_stopEvent || !m_writeEvent)
|
||||||
{
|
{
|
||||||
DEBUG_ERROR_HR(GetLastError(), "Failed to create named pipe stop event");
|
DEBUG_ERROR_HR(GetLastError(), "Failed to create named pipe events");
|
||||||
|
if (m_writeEvent)
|
||||||
|
CloseHandle(m_writeEvent);
|
||||||
|
if (m_stopEvent)
|
||||||
|
CloseHandle(m_stopEvent);
|
||||||
|
m_writeEvent = nullptr;
|
||||||
|
m_stopEvent = nullptr;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -210,6 +210,8 @@ bool CPipeEndpoint::Start(
|
|||||||
{
|
{
|
||||||
CloseHandle(m_stopEvent);
|
CloseHandle(m_stopEvent);
|
||||||
m_stopEvent = nullptr;
|
m_stopEvent = nullptr;
|
||||||
|
CloseHandle(m_writeEvent);
|
||||||
|
m_writeEvent = nullptr;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
PublishPipe(pipe);
|
PublishPipe(pipe);
|
||||||
@@ -232,6 +234,8 @@ bool CPipeEndpoint::Start(
|
|||||||
|
|
||||||
CloseHandle(m_stopEvent);
|
CloseHandle(m_stopEvent);
|
||||||
m_stopEvent = nullptr;
|
m_stopEvent = nullptr;
|
||||||
|
CloseHandle(m_writeEvent);
|
||||||
|
m_writeEvent = nullptr;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -241,6 +245,7 @@ bool CPipeEndpoint::Start(
|
|||||||
void CPipeEndpoint::Stop()
|
void CPipeEndpoint::Stop()
|
||||||
{
|
{
|
||||||
m_running.store(false);
|
m_running.store(false);
|
||||||
|
m_connected.store(false);
|
||||||
if (m_stopEvent)
|
if (m_stopEvent)
|
||||||
SetEvent(m_stopEvent);
|
SetEvent(m_stopEvent);
|
||||||
|
|
||||||
@@ -270,6 +275,12 @@ void CPipeEndpoint::Stop()
|
|||||||
m_stopEvent = nullptr;
|
m_stopEvent = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (m_writeEvent)
|
||||||
|
{
|
||||||
|
CloseHandle(m_writeEvent);
|
||||||
|
m_writeEvent = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
m_connected.store(false);
|
m_connected.store(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -128,4 +128,5 @@ private:
|
|||||||
HANDLE m_pipe = INVALID_HANDLE_VALUE;
|
HANDLE m_pipe = INVALID_HANDLE_VALUE;
|
||||||
HANDLE m_thread = nullptr;
|
HANDLE m_thread = nullptr;
|
||||||
HANDLE m_stopEvent = nullptr;
|
HANDLE m_stopEvent = nullptr;
|
||||||
|
HANDLE m_writeEvent = nullptr;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -20,72 +20,59 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "common/KVMFRInput.h"
|
||||||
|
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
static constexpr wchar_t LG_INPUT_PIPE_NAME[] =
|
static constexpr wchar_t LG_INPUT_PIPE_NAME[] =
|
||||||
L"\\\\.\\pipe\\LookingGlassIDDInput";
|
L"\\\\.\\pipe\\LookingGlassIDDInput";
|
||||||
|
|
||||||
static constexpr uint32_t LG_INPUT_PIPE_MAGIC = 0x5049474c;
|
static constexpr uint32_t LG_INPUT_PIPE_MAGIC = 0x5049474c;
|
||||||
static constexpr uint16_t LG_INPUT_PIPE_VERSION = 2;
|
static constexpr uint16_t LG_INPUT_PIPE_VERSION = 3;
|
||||||
static constexpr size_t LG_INPUT_PIPE_MAX_PAYLOAD_SIZE = 64;
|
static constexpr size_t LG_INPUT_PIPE_MAX_PAYLOAD_SIZE = 64;
|
||||||
static constexpr uint8_t LG_INPUT_MOUSE_BUTTON_MASK = 0x1f;
|
static constexpr uint16_t LG_INPUT_MOUSE_ABSOLUTE_MAX =
|
||||||
static constexpr uint16_t LG_INPUT_MOUSE_ABSOLUTE_MAX = 32767;
|
KVMFR_INPUT_MOUSE_ABSOLUTE_MAX;
|
||||||
static constexpr int8_t LG_INPUT_MOUSE_WHEEL_MIN = -127;
|
static constexpr int8_t LG_INPUT_MOUSE_WHEEL_MIN = -127;
|
||||||
static constexpr size_t LG_INPUT_KEYBOARD_KEY_COUNT = 6;
|
static constexpr int32_t LG_INPUT_MOUSE_DELTA_MAX = INT16_MAX * 4;
|
||||||
static constexpr uint8_t LG_INPUT_KEYBOARD_USAGE_MAX = 0xe7;
|
static constexpr int32_t LG_INPUT_MOUSE_DELTA_MIN = INT16_MIN * 4;
|
||||||
|
static constexpr int32_t LG_INPUT_MOUSE_WHEEL_MAX = INT8_MAX * 4;
|
||||||
|
static constexpr int32_t LG_INPUT_MOUSE_WHEEL_MIN_TOTAL =
|
||||||
|
LG_INPUT_MOUSE_WHEEL_MIN * 4;
|
||||||
|
static constexpr size_t LG_INPUT_KEYBOARD_KEY_COUNT =
|
||||||
|
KVMFR_INPUT_KEYBOARD_KEY_COUNT;
|
||||||
|
static constexpr uint8_t LG_INPUT_KEYBOARD_USAGE_MAX =
|
||||||
|
KVMFR_INPUT_KEYBOARD_USAGE_MAX;
|
||||||
|
|
||||||
enum LGInputMouseButton : uint8_t
|
enum LGInputMouseButton : uint32_t
|
||||||
{
|
{
|
||||||
LG_INPUT_MOUSE_BUTTON_LEFT = 1 << 0,
|
LG_INPUT_MOUSE_BUTTON_LEFT = 1 << 0,
|
||||||
LG_INPUT_MOUSE_BUTTON_RIGHT = 1 << 1,
|
LG_INPUT_MOUSE_BUTTON_RIGHT = 1 << 1,
|
||||||
LG_INPUT_MOUSE_BUTTON_MIDDLE = 1 << 2,
|
LG_INPUT_MOUSE_BUTTON_MIDDLE = 1 << 2,
|
||||||
LG_INPUT_MOUSE_BUTTON_BACK = 1 << 3,
|
LG_INPUT_MOUSE_BUTTON_BACK = 1 << 3,
|
||||||
LG_INPUT_MOUSE_BUTTON_FORWARD = 1 << 4,
|
LG_INPUT_MOUSE_BUTTON_FORWARD = 1 << 4,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum LGInputKeyboardModifier : uint8_t
|
enum LGInputKeyboardModifier : uint8_t
|
||||||
{
|
{
|
||||||
LG_INPUT_KEYBOARD_MODIFIER_LEFT_CONTROL = 1 << 0,
|
LG_INPUT_KEYBOARD_MODIFIER_LEFT_CONTROL = 1 << 0,
|
||||||
LG_INPUT_KEYBOARD_MODIFIER_LEFT_SHIFT = 1 << 1,
|
LG_INPUT_KEYBOARD_MODIFIER_LEFT_SHIFT = 1 << 1,
|
||||||
LG_INPUT_KEYBOARD_MODIFIER_LEFT_ALT = 1 << 2,
|
LG_INPUT_KEYBOARD_MODIFIER_LEFT_ALT = 1 << 2,
|
||||||
LG_INPUT_KEYBOARD_MODIFIER_LEFT_GUI = 1 << 3,
|
LG_INPUT_KEYBOARD_MODIFIER_LEFT_GUI = 1 << 3,
|
||||||
LG_INPUT_KEYBOARD_MODIFIER_RIGHT_CONTROL = 1 << 4,
|
LG_INPUT_KEYBOARD_MODIFIER_RIGHT_CONTROL = 1 << 4,
|
||||||
LG_INPUT_KEYBOARD_MODIFIER_RIGHT_SHIFT = 1 << 5,
|
LG_INPUT_KEYBOARD_MODIFIER_RIGHT_SHIFT = 1 << 5,
|
||||||
LG_INPUT_KEYBOARD_MODIFIER_RIGHT_ALT = 1 << 6,
|
LG_INPUT_KEYBOARD_MODIFIER_RIGHT_ALT = 1 << 6,
|
||||||
LG_INPUT_KEYBOARD_MODIFIER_RIGHT_GUI = 1 << 7,
|
LG_INPUT_KEYBOARD_MODIFIER_RIGHT_GUI = 1 << 7,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum LGInputPipeMessageType : uint16_t
|
enum LGInputPipeMessageType : uint16_t
|
||||||
{
|
{
|
||||||
LG_INPUT_PIPE_MESSAGE_MOUSE_ABSOLUTE = 1,
|
LG_INPUT_PIPE_MESSAGE_MOUSE_ABSOLUTE = 1,
|
||||||
LG_INPUT_PIPE_MESSAGE_MOUSE_RELATIVE = 2,
|
LG_INPUT_PIPE_MESSAGE_MOUSE_RELATIVE = 2,
|
||||||
LG_INPUT_PIPE_MESSAGE_KEYBOARD = 3,
|
LG_INPUT_PIPE_MESSAGE_KEYBOARD = 3,
|
||||||
};
|
};
|
||||||
|
|
||||||
#pragma pack(push, 1)
|
#pragma pack(push, 1)
|
||||||
struct LGInputPipeMouseRelative
|
|
||||||
{
|
|
||||||
uint8_t buttons;
|
|
||||||
int16_t deltaX;
|
|
||||||
int16_t deltaY;
|
|
||||||
int8_t wheel;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct LGInputPipeMouseAbsolute
|
|
||||||
{
|
|
||||||
uint8_t buttons;
|
|
||||||
uint16_t x;
|
|
||||||
uint16_t y;
|
|
||||||
int8_t wheel;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct LGInputPipeKeyboard
|
|
||||||
{
|
|
||||||
uint8_t modifiers;
|
|
||||||
uint8_t keys[LG_INPUT_KEYBOARD_KEY_COUNT];
|
|
||||||
};
|
|
||||||
|
|
||||||
struct LGInputPipeMessage
|
struct LGInputPipeMessage
|
||||||
{
|
{
|
||||||
uint32_t magic;
|
uint32_t magic;
|
||||||
@@ -97,11 +84,15 @@ struct LGInputPipeMessage
|
|||||||
};
|
};
|
||||||
#pragma pack(pop)
|
#pragma pack(pop)
|
||||||
|
|
||||||
static_assert(sizeof(LGInputPipeMouseRelative) == 6,
|
using LGInputPipeMouseRelative = KVMFRInputMouseRelative;
|
||||||
|
using LGInputPipeMouseAbsolute = KVMFRInputMouseAbsolute;
|
||||||
|
using LGInputPipeKeyboard = KVMFRInputKeyboard;
|
||||||
|
|
||||||
|
static_assert(sizeof(LGInputPipeMouseRelative) == 16,
|
||||||
"LGInputPipeMouseRelative wire layout changed");
|
"LGInputPipeMouseRelative wire layout changed");
|
||||||
static_assert(sizeof(LGInputPipeMouseAbsolute) == 6,
|
static_assert(sizeof(LGInputPipeMouseAbsolute) == 16,
|
||||||
"LGInputPipeMouseAbsolute wire layout changed");
|
"LGInputPipeMouseAbsolute wire layout changed");
|
||||||
static_assert(sizeof(LGInputPipeKeyboard) == 7,
|
static_assert(sizeof(LGInputPipeKeyboard) == 16,
|
||||||
"LGInputPipeKeyboard wire layout changed");
|
"LGInputPipeKeyboard wire layout changed");
|
||||||
static_assert(sizeof(LGInputPipeMessage) == 84,
|
static_assert(sizeof(LGInputPipeMessage) == 84,
|
||||||
"LGInputPipeMessage wire layout changed");
|
"LGInputPipeMessage wire layout changed");
|
||||||
|
|||||||
@@ -61,7 +61,7 @@
|
|||||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||||
<PreprocessorDefinitions>_ITERATOR_DEBUG_LEVEL=0;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
<PreprocessorDefinitions>_ITERATOR_DEBUG_LEVEL=0;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
<AdditionalOptions>/EHsc /D_ATL_NO_WIN_SUPPORT %(AdditionalOptions)</AdditionalOptions>
|
<AdditionalOptions>/EHsc /D_ATL_NO_WIN_SUPPORT %(AdditionalOptions)</AdditionalOptions>
|
||||||
<AdditionalIncludeDirectories>$(ProjectDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
<AdditionalIncludeDirectories>$(ProjectDir);$(SolutionDir)..\common\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
</ItemDefinitionGroup>
|
</ItemDefinitionGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -27,8 +27,8 @@ class IInputSink
|
|||||||
public:
|
public:
|
||||||
virtual ~IInputSink() = default;
|
virtual ~IInputSink() = default;
|
||||||
|
|
||||||
virtual bool IsAvailable() const = 0;
|
// Odd states are available; a state change invalidates in-flight input.
|
||||||
virtual uint64_t GetGeneration() const = 0;
|
virtual uint64_t GetState() const = 0;
|
||||||
virtual bool SendMouseRelative(int32_t deltaX, int32_t deltaY,
|
virtual bool SendMouseRelative(int32_t deltaX, int32_t deltaY,
|
||||||
int32_t wheel, uint32_t buttons) = 0;
|
int32_t wheel, uint32_t buttons) = 0;
|
||||||
virtual bool SendMouseAbsolute(uint16_t x, uint16_t y,
|
virtual bool SendMouseAbsolute(uint16_t x, uint16_t y,
|
||||||
|
|||||||
@@ -28,39 +28,191 @@
|
|||||||
|
|
||||||
CInputPipeServer g_inputPipeServer;
|
CInputPipeServer g_inputPipeServer;
|
||||||
|
|
||||||
static constexpr int32_t MAX_SPLIT_REPORTS = 4;
|
static constexpr DWORD WAIT_FIRST_OBJECT_VALUE = 0;
|
||||||
static constexpr int32_t MAX_MOUSE_DELTA =
|
|
||||||
INT16_MAX * MAX_SPLIT_REPORTS;
|
|
||||||
static constexpr int32_t MIN_MOUSE_DELTA =
|
|
||||||
INT16_MIN * MAX_SPLIT_REPORTS;
|
|
||||||
static constexpr int32_t MAX_MOUSE_WHEEL =
|
|
||||||
INT8_MAX * MAX_SPLIT_REPORTS;
|
|
||||||
static constexpr int32_t MIN_MOUSE_WHEEL =
|
|
||||||
LG_INPUT_MOUSE_WHEEL_MIN * MAX_SPLIT_REPORTS;
|
|
||||||
|
|
||||||
bool CInputPipeServer::Init()
|
bool CInputPipeServer::Init()
|
||||||
{
|
{
|
||||||
|
DeInit();
|
||||||
|
|
||||||
m_state.store(0, std::memory_order_release);
|
m_state.store(0, std::memory_order_release);
|
||||||
|
m_stopEvent = CreateEventW(nullptr, TRUE, FALSE, nullptr);
|
||||||
|
m_queueEvent = CreateEventW(nullptr, FALSE, FALSE, nullptr);
|
||||||
|
if (!m_stopEvent || !m_queueEvent)
|
||||||
{
|
{
|
||||||
CSRWExclusiveLock lock(&m_sendLock);
|
DEBUG_ERROR_HR(GetLastError(),
|
||||||
m_sequence = 0;
|
"Failed to create LGInput sender resources");
|
||||||
m_mouseMode = MouseMode::NONE;
|
DeInit();
|
||||||
m_absoluteValid = false;
|
return false;
|
||||||
m_absoluteX = 0;
|
}
|
||||||
m_absoluteY = 0;
|
|
||||||
|
m_thread = CreateThread(nullptr, 0, ThreadProc, this, 0, nullptr);
|
||||||
|
if (!m_thread)
|
||||||
|
{
|
||||||
|
DEBUG_ERROR_HR(GetLastError(), "Failed to create LGInput sender");
|
||||||
|
DeInit();
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_endpoint.SetHandler(this);
|
m_endpoint.SetHandler(this);
|
||||||
return m_endpoint.Start(
|
if (!m_endpoint.Start(
|
||||||
LG_INPUT_PIPE_NAME,
|
LG_INPUT_PIPE_NAME,
|
||||||
CPipeEndpoint::Mode::Server,
|
CPipeEndpoint::Mode::Server,
|
||||||
sizeof(LGInputPipeMessage));
|
sizeof(LGInputPipeMessage)))
|
||||||
|
{
|
||||||
|
DeInit();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CInputPipeServer::DeInit()
|
void CInputPipeServer::DeInit()
|
||||||
{
|
{
|
||||||
Invalidate();
|
Invalidate(0, false);
|
||||||
|
if (m_stopEvent)
|
||||||
|
SetEvent(m_stopEvent);
|
||||||
|
|
||||||
m_endpoint.Stop();
|
m_endpoint.Stop();
|
||||||
|
|
||||||
|
if (m_thread)
|
||||||
|
{
|
||||||
|
WaitForSingleObject(m_thread, INFINITE);
|
||||||
|
CloseHandle(m_thread);
|
||||||
|
m_thread = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_queueEvent)
|
||||||
|
{
|
||||||
|
CloseHandle(m_queueEvent);
|
||||||
|
m_queueEvent = nullptr;
|
||||||
|
}
|
||||||
|
if (m_stopEvent)
|
||||||
|
{
|
||||||
|
CloseHandle(m_stopEvent);
|
||||||
|
m_stopEvent = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
CSRWExclusiveLock lock(&m_queueLock);
|
||||||
|
m_queueHead = 0;
|
||||||
|
m_queueCount = 0;
|
||||||
|
m_mouseMode = MouseMode::NONE;
|
||||||
|
m_absoluteValid = false;
|
||||||
|
m_relativeButtons = 0;
|
||||||
|
m_absoluteButtons = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CInputPipeServer::QueueLocked(
|
||||||
|
LGInputPipeMessageType type,
|
||||||
|
const KVMFRInputPayload& payload,
|
||||||
|
bool pureMotion)
|
||||||
|
{
|
||||||
|
if (m_queueCount)
|
||||||
|
{
|
||||||
|
const size_t tailIndex =
|
||||||
|
(m_queueHead + m_queueCount - 1) % QUEUE_LENGTH;
|
||||||
|
QueueItem& tail = m_queue[tailIndex];
|
||||||
|
if (tail.type == type)
|
||||||
|
{
|
||||||
|
if (type == LG_INPUT_PIPE_MESSAGE_MOUSE_RELATIVE &&
|
||||||
|
m_queueCount >= MOTION_COALESCE_THRESHOLD &&
|
||||||
|
tail.pureMotion && pureMotion &&
|
||||||
|
tail.payload.mouseRelative.wheel == 0 &&
|
||||||
|
payload.mouseRelative.wheel == 0 &&
|
||||||
|
tail.payload.mouseRelative.buttons ==
|
||||||
|
payload.mouseRelative.buttons)
|
||||||
|
{
|
||||||
|
const int64_t x = static_cast<int64_t>(
|
||||||
|
tail.payload.mouseRelative.deltaX) +
|
||||||
|
payload.mouseRelative.deltaX;
|
||||||
|
const int64_t y = static_cast<int64_t>(
|
||||||
|
tail.payload.mouseRelative.deltaY) +
|
||||||
|
payload.mouseRelative.deltaY;
|
||||||
|
if (x >= LG_INPUT_MOUSE_DELTA_MIN &&
|
||||||
|
x <= LG_INPUT_MOUSE_DELTA_MAX &&
|
||||||
|
y >= LG_INPUT_MOUSE_DELTA_MIN &&
|
||||||
|
y <= LG_INPUT_MOUSE_DELTA_MAX)
|
||||||
|
{
|
||||||
|
tail.payload.mouseRelative.deltaX = static_cast<int32_t>(x);
|
||||||
|
tail.payload.mouseRelative.deltaY = static_cast<int32_t>(y);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (type == LG_INPUT_PIPE_MESSAGE_MOUSE_ABSOLUTE &&
|
||||||
|
tail.pureMotion && pureMotion &&
|
||||||
|
tail.payload.mouseAbsolute.wheel == 0 &&
|
||||||
|
payload.mouseAbsolute.wheel == 0 &&
|
||||||
|
tail.payload.mouseAbsolute.buttons ==
|
||||||
|
payload.mouseAbsolute.buttons)
|
||||||
|
{
|
||||||
|
tail.payload.mouseAbsolute.x = payload.mouseAbsolute.x;
|
||||||
|
tail.payload.mouseAbsolute.y = payload.mouseAbsolute.y;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return QueueRawLocked(type, payload, pureMotion);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CInputPipeServer::QueueRawLocked(
|
||||||
|
LGInputPipeMessageType type,
|
||||||
|
const KVMFRInputPayload& payload,
|
||||||
|
bool pureMotion)
|
||||||
|
{
|
||||||
|
if (m_queueCount == QUEUE_LENGTH)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const size_t index =
|
||||||
|
(m_queueHead + m_queueCount) % QUEUE_LENGTH;
|
||||||
|
m_queue[index].type = type;
|
||||||
|
m_queue[index].state =
|
||||||
|
m_state.load(std::memory_order_relaxed);
|
||||||
|
m_queue[index].payload = payload;
|
||||||
|
m_queue[index].pureMotion = pureMotion;
|
||||||
|
++m_queueCount;
|
||||||
|
SetEvent(m_queueEvent);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CInputPipeServer::QueueResetLocked()
|
||||||
|
{
|
||||||
|
KVMFRInputPayload payload = {};
|
||||||
|
if (!QueueRawLocked(
|
||||||
|
LG_INPUT_PIPE_MESSAGE_MOUSE_RELATIVE, payload, false))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (m_absoluteValid)
|
||||||
|
{
|
||||||
|
payload.mouseAbsolute.x = m_absoluteX;
|
||||||
|
payload.mouseAbsolute.y = m_absoluteY;
|
||||||
|
if (!QueueRawLocked(
|
||||||
|
LG_INPUT_PIPE_MESSAGE_MOUSE_ABSOLUTE, payload, false))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
payload = {};
|
||||||
|
if (!QueueRawLocked(LG_INPUT_PIPE_MESSAGE_KEYBOARD, payload, false))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
m_mouseMode = MouseMode::NONE;
|
||||||
|
m_relativeButtons = 0;
|
||||||
|
m_absoluteButtons = 0;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CInputPipeServer::ResyncLocked()
|
||||||
|
{
|
||||||
|
m_queueHead = 0;
|
||||||
|
m_queueCount = 0;
|
||||||
|
uint64_t state = m_state.load(std::memory_order_relaxed);
|
||||||
|
while (state & 1)
|
||||||
|
{
|
||||||
|
if (m_state.compare_exchange_weak(
|
||||||
|
state, state + 2, std::memory_order_acq_rel))
|
||||||
|
{
|
||||||
|
QueueResetLocked();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CInputPipeServer::SendMouseRelative(
|
bool CInputPipeServer::SendMouseRelative(
|
||||||
@@ -69,66 +221,47 @@ bool CInputPipeServer::SendMouseRelative(
|
|||||||
int32_t wheel,
|
int32_t wheel,
|
||||||
uint32_t buttons)
|
uint32_t buttons)
|
||||||
{
|
{
|
||||||
if (deltaX < MIN_MOUSE_DELTA || deltaX > MAX_MOUSE_DELTA ||
|
if (deltaX < LG_INPUT_MOUSE_DELTA_MIN ||
|
||||||
deltaY < MIN_MOUSE_DELTA || deltaY > MAX_MOUSE_DELTA ||
|
deltaX > LG_INPUT_MOUSE_DELTA_MAX ||
|
||||||
wheel < MIN_MOUSE_WHEEL || wheel > MAX_MOUSE_WHEEL ||
|
deltaY < LG_INPUT_MOUSE_DELTA_MIN ||
|
||||||
(buttons & ~static_cast<uint32_t>(LG_INPUT_MOUSE_BUTTON_MASK)))
|
deltaY > LG_INPUT_MOUSE_DELTA_MAX ||
|
||||||
|
wheel < LG_INPUT_MOUSE_WHEEL_MIN_TOTAL ||
|
||||||
|
wheel > LG_INPUT_MOUSE_WHEEL_MAX ||
|
||||||
|
!(m_state.load(std::memory_order_acquire) & 1))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
CSRWExclusiveLock lock(&m_sendLock);
|
KVMFRInputPayload payload = {};
|
||||||
if (!IsAvailable())
|
payload.mouseRelative.buttons = buttons;
|
||||||
return false;
|
payload.mouseRelative.deltaX = deltaX;
|
||||||
|
payload.mouseRelative.deltaY = deltaY;
|
||||||
|
payload.mouseRelative.wheel = wheel;
|
||||||
|
|
||||||
if (m_mouseMode == MouseMode::ABSOLUTE)
|
CSRWExclusiveLock lock(&m_queueLock);
|
||||||
|
const bool pureMotion = wheel == 0 && buttons == m_relativeButtons;
|
||||||
|
const bool switching = m_mouseMode == MouseMode::ABSOLUTE_INPUT;
|
||||||
|
bool queued = (m_state.load(std::memory_order_relaxed) & 1) != 0;
|
||||||
|
if (queued && switching)
|
||||||
{
|
{
|
||||||
const LGInputPipeMouseAbsolute neutral = {
|
KVMFRInputPayload neutral = {};
|
||||||
0,
|
neutral.mouseAbsolute.x = m_absoluteX;
|
||||||
m_absoluteX,
|
neutral.mouseAbsolute.y = m_absoluteY;
|
||||||
m_absoluteY,
|
queued = QueueRawLocked(
|
||||||
0,
|
LG_INPUT_PIPE_MESSAGE_MOUSE_ABSOLUTE, neutral, false);
|
||||||
};
|
|
||||||
if (!SendMessageLocked(
|
|
||||||
LG_INPUT_PIPE_MESSAGE_MOUSE_ABSOLUTE,
|
|
||||||
&neutral,
|
|
||||||
sizeof(neutral)))
|
|
||||||
{
|
|
||||||
Invalidate();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
m_mouseMode = MouseMode::NONE;
|
|
||||||
}
|
}
|
||||||
|
if (queued)
|
||||||
|
queued = QueueLocked(
|
||||||
|
LG_INPUT_PIPE_MESSAGE_MOUSE_RELATIVE, payload, pureMotion);
|
||||||
|
|
||||||
do
|
if (!queued)
|
||||||
|
ResyncLocked();
|
||||||
|
else
|
||||||
{
|
{
|
||||||
const int16_t x = deltaX > INT16_MAX ? INT16_MAX :
|
if (switching)
|
||||||
deltaX < INT16_MIN ? INT16_MIN : static_cast<int16_t>(deltaX);
|
m_absoluteButtons = 0;
|
||||||
const int16_t y = deltaY > INT16_MAX ? INT16_MAX :
|
m_mouseMode = MouseMode::RELATIVE_INPUT;
|
||||||
deltaY < INT16_MIN ? INT16_MIN : static_cast<int16_t>(deltaY);
|
m_relativeButtons = buttons;
|
||||||
const int8_t wheelDelta = wheel > INT8_MAX ? INT8_MAX :
|
|
||||||
wheel < LG_INPUT_MOUSE_WHEEL_MIN ? LG_INPUT_MOUSE_WHEEL_MIN :
|
|
||||||
static_cast<int8_t>(wheel);
|
|
||||||
|
|
||||||
const LGInputPipeMouseRelative payload = {
|
|
||||||
static_cast<uint8_t>(buttons),
|
|
||||||
x,
|
|
||||||
y,
|
|
||||||
wheelDelta,
|
|
||||||
};
|
|
||||||
if (!SendMessageLocked(
|
|
||||||
LG_INPUT_PIPE_MESSAGE_MOUSE_RELATIVE, &payload, sizeof(payload)))
|
|
||||||
{
|
|
||||||
Invalidate();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_mouseMode = MouseMode::RELATIVE;
|
|
||||||
deltaX -= x;
|
|
||||||
deltaY -= y;
|
|
||||||
wheel -= wheelDelta;
|
|
||||||
}
|
}
|
||||||
while (deltaX || deltaY || wheel);
|
return queued;
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CInputPipeServer::SendMouseAbsolute(
|
bool CInputPipeServer::SendMouseAbsolute(
|
||||||
@@ -139,170 +272,206 @@ bool CInputPipeServer::SendMouseAbsolute(
|
|||||||
{
|
{
|
||||||
if (x > LG_INPUT_MOUSE_ABSOLUTE_MAX ||
|
if (x > LG_INPUT_MOUSE_ABSOLUTE_MAX ||
|
||||||
y > LG_INPUT_MOUSE_ABSOLUTE_MAX ||
|
y > LG_INPUT_MOUSE_ABSOLUTE_MAX ||
|
||||||
wheel < MIN_MOUSE_WHEEL || wheel > MAX_MOUSE_WHEEL ||
|
wheel < LG_INPUT_MOUSE_WHEEL_MIN_TOTAL ||
|
||||||
(buttons & ~static_cast<uint32_t>(LG_INPUT_MOUSE_BUTTON_MASK)))
|
wheel > LG_INPUT_MOUSE_WHEEL_MAX ||
|
||||||
|
!(m_state.load(std::memory_order_acquire) & 1))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
CSRWExclusiveLock lock(&m_sendLock);
|
KVMFRInputPayload payload = {};
|
||||||
if (!IsAvailable())
|
payload.mouseAbsolute.buttons = buttons;
|
||||||
return false;
|
payload.mouseAbsolute.x = x;
|
||||||
|
payload.mouseAbsolute.y = y;
|
||||||
|
payload.mouseAbsolute.wheel = wheel;
|
||||||
|
|
||||||
if (m_mouseMode == MouseMode::RELATIVE)
|
CSRWExclusiveLock lock(&m_queueLock);
|
||||||
|
const bool pureMotion = wheel == 0 && buttons == m_absoluteButtons;
|
||||||
|
const bool switching = m_mouseMode == MouseMode::RELATIVE_INPUT;
|
||||||
|
bool queued = (m_state.load(std::memory_order_relaxed) & 1) != 0;
|
||||||
|
if (queued && switching)
|
||||||
{
|
{
|
||||||
const LGInputPipeMouseRelative neutral = {};
|
const KVMFRInputPayload neutral = {};
|
||||||
if (!SendMessageLocked(
|
queued = QueueRawLocked(
|
||||||
LG_INPUT_PIPE_MESSAGE_MOUSE_RELATIVE,
|
LG_INPUT_PIPE_MESSAGE_MOUSE_RELATIVE, neutral, false);
|
||||||
&neutral,
|
|
||||||
sizeof(neutral)))
|
|
||||||
{
|
|
||||||
Invalidate();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
m_mouseMode = MouseMode::NONE;
|
|
||||||
}
|
}
|
||||||
|
if (queued)
|
||||||
|
queued = QueueLocked(
|
||||||
|
LG_INPUT_PIPE_MESSAGE_MOUSE_ABSOLUTE, payload, pureMotion);
|
||||||
|
|
||||||
do
|
if (!queued)
|
||||||
|
ResyncLocked();
|
||||||
|
else
|
||||||
{
|
{
|
||||||
const int8_t wheelDelta = wheel > INT8_MAX ? INT8_MAX :
|
if (switching)
|
||||||
wheel < LG_INPUT_MOUSE_WHEEL_MIN ? LG_INPUT_MOUSE_WHEEL_MIN :
|
m_relativeButtons = 0;
|
||||||
static_cast<int8_t>(wheel);
|
m_mouseMode = MouseMode::ABSOLUTE_INPUT;
|
||||||
const LGInputPipeMouseAbsolute payload = {
|
m_absoluteValid = true;
|
||||||
static_cast<uint8_t>(buttons),
|
m_absoluteX = x;
|
||||||
x,
|
m_absoluteY = y;
|
||||||
y,
|
m_absoluteButtons = buttons;
|
||||||
wheelDelta,
|
|
||||||
};
|
|
||||||
if (!SendMessageLocked(
|
|
||||||
LG_INPUT_PIPE_MESSAGE_MOUSE_ABSOLUTE, &payload, sizeof(payload)))
|
|
||||||
{
|
|
||||||
Invalidate();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
m_mouseMode = MouseMode::ABSOLUTE;
|
|
||||||
m_absoluteValid = true;
|
|
||||||
m_absoluteX = x;
|
|
||||||
m_absoluteY = y;
|
|
||||||
wheel -= wheelDelta;
|
|
||||||
}
|
}
|
||||||
while (wheel);
|
return queued;
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CInputPipeServer::SendKeyboard(
|
bool CInputPipeServer::SendKeyboard(
|
||||||
uint8_t modifiers,
|
uint8_t modifiers,
|
||||||
const uint8_t * keys)
|
const uint8_t * keys)
|
||||||
{
|
{
|
||||||
if (!keys)
|
if (!keys || !(m_state.load(std::memory_order_acquire) & 1))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
LGInputPipeKeyboard payload = {};
|
KVMFRInputPayload payload = {};
|
||||||
payload.modifiers = modifiers;
|
payload.keyboard.modifiers = modifiers;
|
||||||
for (size_t i = 0; i < LG_INPUT_KEYBOARD_KEY_COUNT; ++i)
|
for (size_t i = 0; i < LG_INPUT_KEYBOARD_KEY_COUNT; ++i)
|
||||||
{
|
{
|
||||||
if (keys[i] > LG_INPUT_KEYBOARD_USAGE_MAX)
|
if (keys[i] > LG_INPUT_KEYBOARD_USAGE_MAX)
|
||||||
return false;
|
return false;
|
||||||
payload.keys[i] = keys[i];
|
payload.keyboard.keys[i] = keys[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
CSRWExclusiveLock lock(&m_sendLock);
|
CSRWExclusiveLock lock(&m_queueLock);
|
||||||
const bool sent = IsAvailable() &&
|
const bool queued = (m_state.load(std::memory_order_relaxed) & 1) &&
|
||||||
SendMessageLocked(
|
QueueLocked(LG_INPUT_PIPE_MESSAGE_KEYBOARD, payload, false);
|
||||||
LG_INPUT_PIPE_MESSAGE_KEYBOARD,
|
if (!queued)
|
||||||
&payload,
|
ResyncLocked();
|
||||||
sizeof(payload));
|
return queued;
|
||||||
if (!sent)
|
|
||||||
Invalidate();
|
|
||||||
return sent;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CInputPipeServer::Reset()
|
bool CInputPipeServer::Reset()
|
||||||
{
|
{
|
||||||
CSRWExclusiveLock lock(&m_sendLock);
|
if (!(m_state.load(std::memory_order_acquire) & 1))
|
||||||
const bool reset = IsAvailable() &&
|
|
||||||
ResetLocked();
|
|
||||||
if (!reset)
|
|
||||||
Invalidate();
|
|
||||||
return reset;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CInputPipeServer::ResetLocked()
|
|
||||||
{
|
|
||||||
const LGInputPipeMouseRelative relative = {};
|
|
||||||
const LGInputPipeMouseAbsolute absolute = {
|
|
||||||
0,
|
|
||||||
m_absoluteX,
|
|
||||||
m_absoluteY,
|
|
||||||
0,
|
|
||||||
};
|
|
||||||
const LGInputPipeKeyboard keyboard = {};
|
|
||||||
|
|
||||||
if (!SendMessageLocked(
|
|
||||||
LG_INPUT_PIPE_MESSAGE_MOUSE_RELATIVE,
|
|
||||||
&relative,
|
|
||||||
sizeof(relative)) ||
|
|
||||||
(m_absoluteValid &&
|
|
||||||
!SendMessageLocked(
|
|
||||||
LG_INPUT_PIPE_MESSAGE_MOUSE_ABSOLUTE,
|
|
||||||
&absolute,
|
|
||||||
sizeof(absolute))) ||
|
|
||||||
!SendMessageLocked(
|
|
||||||
LG_INPUT_PIPE_MESSAGE_KEYBOARD,
|
|
||||||
&keyboard,
|
|
||||||
sizeof(keyboard)))
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
m_mouseMode = MouseMode::NONE;
|
CSRWExclusiveLock lock(&m_queueLock);
|
||||||
|
bool queued = (m_state.load(std::memory_order_relaxed) & 1) != 0;
|
||||||
|
if (queued)
|
||||||
|
queued = QueueResetLocked();
|
||||||
|
if (!queued)
|
||||||
|
ResyncLocked();
|
||||||
|
return queued;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CInputPipeServer::Pop(QueueItem& item)
|
||||||
|
{
|
||||||
|
CSRWExclusiveLock lock(&m_queueLock);
|
||||||
|
if (!m_queueCount)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
item = m_queue[m_queueHead];
|
||||||
|
m_queueHead = (m_queueHead + 1) % QUEUE_LENGTH;
|
||||||
|
--m_queueCount;
|
||||||
|
if (m_queueCount)
|
||||||
|
SetEvent(m_queueEvent);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CInputPipeServer::SendMessageLocked(
|
bool CInputPipeServer::Send(const QueueItem& item)
|
||||||
LGInputPipeMessageType type,
|
|
||||||
const void * payload,
|
|
||||||
size_t size)
|
|
||||||
{
|
{
|
||||||
if (!payload || !size || size > LG_INPUT_PIPE_MAX_PAYLOAD_SIZE)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
LGInputPipeMessage message = {};
|
LGInputPipeMessage message = {};
|
||||||
message.magic = LG_INPUT_PIPE_MAGIC;
|
message.magic = LG_INPUT_PIPE_MAGIC;
|
||||||
message.version = LG_INPUT_PIPE_VERSION;
|
message.version = LG_INPUT_PIPE_VERSION;
|
||||||
message.type = type;
|
message.type = item.type;
|
||||||
message.payloadSize = static_cast<uint32_t>(size);
|
message.payloadSize = sizeof(KVMFRInputPayload);
|
||||||
memcpy(message.payload, payload, size);
|
memcpy(message.payload, &item.payload, sizeof(item.payload));
|
||||||
|
|
||||||
message.sequence = ++m_sequence;
|
bool current;
|
||||||
const bool sent = m_endpoint.Send(&message, sizeof(message));
|
bool sent = true;
|
||||||
return sent;
|
{
|
||||||
|
CSRWSharedLock lock(&m_connectionLock);
|
||||||
|
const uint64_t state = m_state.load(std::memory_order_acquire);
|
||||||
|
current = (state & 1) && item.state == state;
|
||||||
|
if (current)
|
||||||
|
{
|
||||||
|
message.sequence = ++m_sequence;
|
||||||
|
sent = m_endpoint.Send(&message, sizeof(message));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!sent)
|
||||||
|
{
|
||||||
|
Invalidate(item.state, true);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return current;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CInputPipeServer::Invalidate()
|
void CInputPipeServer::Invalidate(uint64_t state, bool requireMatch)
|
||||||
{
|
{
|
||||||
uint64_t state = m_state.load(std::memory_order_acquire);
|
CSRWExclusiveLock connectionLock(&m_connectionLock);
|
||||||
while ((state & 1) && !m_state.compare_exchange_weak(
|
uint64_t current = m_state.load(std::memory_order_relaxed);
|
||||||
state, state + 1, std::memory_order_acq_rel))
|
for (;;)
|
||||||
{
|
{
|
||||||
|
if (!(current & 1) || (requireMatch && state != current))
|
||||||
|
return;
|
||||||
|
if (m_state.compare_exchange_weak(
|
||||||
|
current, current + 1, std::memory_order_acq_rel))
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CSRWExclusiveLock queueLock(&m_queueLock);
|
||||||
|
m_queueHead = 0;
|
||||||
|
m_queueCount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
DWORD WINAPI CInputPipeServer::ThreadProc(void * context)
|
||||||
|
{
|
||||||
|
static_cast<CInputPipeServer *>(context)->Thread();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CInputPipeServer::Thread()
|
||||||
|
{
|
||||||
|
const HANDLE handles[] = { m_stopEvent, m_queueEvent };
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
const DWORD wait = WaitForMultipleObjects(
|
||||||
|
_countof(handles), handles, FALSE, INFINITE);
|
||||||
|
if (wait == WAIT_FIRST_OBJECT_VALUE)
|
||||||
|
break;
|
||||||
|
if (wait != WAIT_FIRST_OBJECT_VALUE + 1)
|
||||||
|
{
|
||||||
|
DEBUG_ERROR_HR(GetLastError(), "LGInput sender wait failed");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
QueueItem item = {};
|
||||||
|
while (Pop(item))
|
||||||
|
if (!Send(item))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
Invalidate(0, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CInputPipeServer::OnPipeConnected()
|
void CInputPipeServer::OnPipeConnected()
|
||||||
{
|
{
|
||||||
Invalidate();
|
CSRWExclusiveLock connectionLock(&m_connectionLock);
|
||||||
|
CSRWExclusiveLock queueLock(&m_queueLock);
|
||||||
|
|
||||||
CSRWExclusiveLock lock(&m_sendLock);
|
uint64_t state = m_state.load(std::memory_order_relaxed);
|
||||||
const bool ready = ResetLocked();
|
if (state & 1)
|
||||||
if (!ready)
|
++state;
|
||||||
|
++state;
|
||||||
|
m_sequence = 0;
|
||||||
|
|
||||||
|
m_queueHead = 0;
|
||||||
|
m_queueCount = 0;
|
||||||
|
m_mouseMode = MouseMode::NONE;
|
||||||
|
const bool reset = QueueResetLocked();
|
||||||
|
for (size_t i = 0; i < m_queueCount; ++i)
|
||||||
{
|
{
|
||||||
DEBUG_WARN("Failed to neutralize the LGInput endpoint");
|
const size_t index = (m_queueHead + i) % QUEUE_LENGTH;
|
||||||
return;
|
m_queue[index].state = state;
|
||||||
}
|
}
|
||||||
|
if (reset)
|
||||||
|
m_state.store(state, std::memory_order_release);
|
||||||
|
|
||||||
m_state.fetch_add(1, std::memory_order_acq_rel);
|
if (!reset)
|
||||||
|
DEBUG_WARN("Failed to queue LGInput endpoint neutralization");
|
||||||
}
|
}
|
||||||
|
|
||||||
void CInputPipeServer::OnPipeDisconnected()
|
void CInputPipeServer::OnPipeDisconnected()
|
||||||
{
|
{
|
||||||
Invalidate();
|
Invalidate(0, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CInputPipeServer::OnPipeMessage(
|
bool CInputPipeServer::OnPipeMessage(
|
||||||
|
|||||||
@@ -30,67 +30,90 @@
|
|||||||
|
|
||||||
class CInputPipeServer : public IInputSink, private IPipeEndpointHandler
|
class CInputPipeServer : public IInputSink, private IPipeEndpointHandler
|
||||||
{
|
{
|
||||||
|
private:
|
||||||
|
static constexpr size_t QUEUE_LENGTH = 128;
|
||||||
|
static constexpr size_t MOTION_COALESCE_THRESHOLD = QUEUE_LENGTH / 2;
|
||||||
|
|
||||||
|
struct QueueItem
|
||||||
|
{
|
||||||
|
KVMFRInputPayload payload;
|
||||||
|
uint64_t state;
|
||||||
|
LGInputPipeMessageType type;
|
||||||
|
bool pureMotion;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class MouseMode
|
||||||
|
{
|
||||||
|
NONE,
|
||||||
|
RELATIVE_INPUT,
|
||||||
|
ABSOLUTE_INPUT,
|
||||||
|
};
|
||||||
|
|
||||||
|
CPipeEndpoint m_endpoint;
|
||||||
|
|
||||||
|
// Odd states are available. Endpoint changes and resyncs advance the state.
|
||||||
|
std::atomic<uint64_t> m_state { 0 };
|
||||||
|
|
||||||
|
SRWLOCK m_queueLock = SRWLOCK_INIT;
|
||||||
|
SRWLOCK m_connectionLock = SRWLOCK_INIT;
|
||||||
|
HANDLE m_stopEvent = nullptr;
|
||||||
|
HANDLE m_queueEvent = nullptr;
|
||||||
|
HANDLE m_thread = nullptr;
|
||||||
|
|
||||||
|
QueueItem m_queue[QUEUE_LENGTH] = {};
|
||||||
|
size_t m_queueHead = 0;
|
||||||
|
size_t m_queueCount = 0;
|
||||||
|
uint64_t m_sequence = 0;
|
||||||
|
|
||||||
|
MouseMode m_mouseMode = MouseMode::NONE;
|
||||||
|
bool m_absoluteValid = false;
|
||||||
|
uint16_t m_absoluteX = 0;
|
||||||
|
uint16_t m_absoluteY = 0;
|
||||||
|
uint32_t m_relativeButtons = 0;
|
||||||
|
uint32_t m_absoluteButtons = 0;
|
||||||
|
|
||||||
|
bool QueueLocked(LGInputPipeMessageType type,
|
||||||
|
const KVMFRInputPayload& payload, bool pureMotion);
|
||||||
|
bool QueueRawLocked(LGInputPipeMessageType type,
|
||||||
|
const KVMFRInputPayload& payload, bool pureMotion);
|
||||||
|
bool QueueResetLocked();
|
||||||
|
void ResyncLocked();
|
||||||
|
bool Pop(QueueItem& item);
|
||||||
|
bool Send(const QueueItem& item);
|
||||||
|
void Invalidate(uint64_t state, bool requireMatch);
|
||||||
|
void Thread();
|
||||||
|
|
||||||
|
static DWORD WINAPI ThreadProc(void * context);
|
||||||
|
|
||||||
|
void OnPipeConnected() override;
|
||||||
|
void OnPipeDisconnected() override;
|
||||||
|
bool OnPipeMessage(const void * message, size_t size) override;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
~CInputPipeServer() { DeInit(); }
|
~CInputPipeServer() { DeInit(); }
|
||||||
|
|
||||||
bool Init();
|
bool Init();
|
||||||
void DeInit();
|
void DeInit();
|
||||||
|
|
||||||
bool IsAvailable() const override
|
uint64_t GetState() const override
|
||||||
{
|
|
||||||
return (m_state.load(std::memory_order_acquire) & 1) != 0;
|
|
||||||
}
|
|
||||||
uint64_t GetGeneration() const override
|
|
||||||
{
|
{
|
||||||
return m_state.load(std::memory_order_acquire);
|
return m_state.load(std::memory_order_acquire);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mouse buttons use LGInputMouseButton bits. Values outside the pipe report
|
|
||||||
// ranges are split into multiple reports without losing motion.
|
|
||||||
bool SendMouseRelative(
|
bool SendMouseRelative(
|
||||||
_In_ int32_t deltaX,
|
_In_ int32_t deltaX,
|
||||||
_In_ int32_t deltaY,
|
_In_ int32_t deltaY,
|
||||||
_In_ int32_t wheel,
|
_In_ int32_t wheel,
|
||||||
_In_ uint32_t buttons) override;
|
_In_ uint32_t buttons) override;
|
||||||
// Absolute coordinates are normalized to 0..32767 on each axis.
|
|
||||||
bool SendMouseAbsolute(
|
bool SendMouseAbsolute(
|
||||||
_In_range_(0, LG_INPUT_MOUSE_ABSOLUTE_MAX) uint16_t x,
|
_In_range_(0, LG_INPUT_MOUSE_ABSOLUTE_MAX) uint16_t x,
|
||||||
_In_range_(0, LG_INPUT_MOUSE_ABSOLUTE_MAX) uint16_t y,
|
_In_range_(0, LG_INPUT_MOUSE_ABSOLUTE_MAX) uint16_t y,
|
||||||
_In_ int32_t wheel,
|
_In_ int32_t wheel,
|
||||||
_In_ uint32_t buttons) override;
|
_In_ uint32_t buttons) override;
|
||||||
// Keys are USB HID Keyboard/Keypad usage IDs; zero marks an empty slot.
|
|
||||||
bool SendKeyboard(
|
bool SendKeyboard(
|
||||||
_In_ uint8_t modifiers,
|
_In_ uint8_t modifiers,
|
||||||
_In_reads_(LG_INPUT_KEYBOARD_KEY_COUNT) const uint8_t * keys) override;
|
_In_reads_(LG_INPUT_KEYBOARD_KEY_COUNT) const uint8_t * keys) override;
|
||||||
bool Reset() override;
|
bool Reset() override;
|
||||||
bool IsConnected() const { return m_endpoint.IsConnected(); }
|
|
||||||
|
|
||||||
private:
|
|
||||||
bool SendMessageLocked(
|
|
||||||
_In_ LGInputPipeMessageType type,
|
|
||||||
_In_reads_bytes_(size) const void * payload,
|
|
||||||
_In_ size_t size);
|
|
||||||
bool ResetLocked();
|
|
||||||
void Invalidate();
|
|
||||||
void OnPipeConnected() override;
|
|
||||||
void OnPipeDisconnected() override;
|
|
||||||
bool OnPipeMessage(const void * message, size_t size) override;
|
|
||||||
|
|
||||||
CPipeEndpoint m_endpoint;
|
|
||||||
// Odd states are available. Each endpoint transition advances the state.
|
|
||||||
std::atomic<uint64_t> m_state { 0 };
|
|
||||||
SRWLOCK m_sendLock = SRWLOCK_INIT;
|
|
||||||
uint64_t m_sequence = 0;
|
|
||||||
enum class MouseMode
|
|
||||||
{
|
|
||||||
NONE,
|
|
||||||
RELATIVE,
|
|
||||||
ABSOLUTE,
|
|
||||||
};
|
|
||||||
MouseMode m_mouseMode = MouseMode::NONE;
|
|
||||||
bool m_absoluteValid = false;
|
|
||||||
uint16_t m_absoluteX = 0;
|
|
||||||
uint16_t m_absoluteY = 0;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
extern CInputPipeServer g_inputPipeServer;
|
extern CInputPipeServer g_inputPipeServer;
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ static constexpr int32_t MAX_MOUSE_WHEEL =
|
|||||||
INT8_MAX * MAX_SPLIT_REPORTS;
|
INT8_MAX * MAX_SPLIT_REPORTS;
|
||||||
static constexpr int32_t MIN_MOUSE_WHEEL =
|
static constexpr int32_t MIN_MOUSE_WHEEL =
|
||||||
-INT8_MAX * MAX_SPLIT_REPORTS;
|
-INT8_MAX * MAX_SPLIT_REPORTS;
|
||||||
|
static constexpr DWORD WAIT_FIRST_OBJECT_VALUE = 0;
|
||||||
|
|
||||||
static bool IsZero(const void * data, size_t size)
|
static bool IsZero(const void * data, size_t size)
|
||||||
{
|
{
|
||||||
@@ -104,7 +105,7 @@ bool CLGMPInputTransport::Start(IInputSink& sink)
|
|||||||
const DWORD state = WaitForSingleObject(m_thread, 0);
|
const DWORD state = WaitForSingleObject(m_thread, 0);
|
||||||
if (state == WAIT_TIMEOUT)
|
if (state == WAIT_TIMEOUT)
|
||||||
return true;
|
return true;
|
||||||
if (state != WAIT_OBJECT_0)
|
if (state != WAIT_FIRST_OBJECT_VALUE)
|
||||||
{
|
{
|
||||||
DEBUG_ERROR_HR(GetLastError(),
|
DEBUG_ERROR_HR(GetLastError(),
|
||||||
"Failed to inspect LGMP input worker");
|
"Failed to inspect LGMP input worker");
|
||||||
@@ -144,13 +145,13 @@ bool CLGMPInputTransport::Start(IInputSink& sink)
|
|||||||
}
|
}
|
||||||
|
|
||||||
m_sink = &sink;
|
m_sink = &sink;
|
||||||
m_sinkGeneration = sink.GetGeneration();
|
m_sinkState = sink.GetState();
|
||||||
m_thread = CreateThread(nullptr, 0, ThreadProc, this, 0, nullptr);
|
m_thread = CreateThread(nullptr, 0, ThreadProc, this, 0, nullptr);
|
||||||
if (!m_thread)
|
if (!m_thread)
|
||||||
{
|
{
|
||||||
DEBUG_ERROR_HR(GetLastError(), "Failed to create LGMP input worker");
|
DEBUG_ERROR_HR(GetLastError(), "Failed to create LGMP input worker");
|
||||||
m_sink = nullptr;
|
m_sink = nullptr;
|
||||||
m_sinkGeneration = 0;
|
m_sinkState = 0;
|
||||||
CloseHandle(m_pollTimer);
|
CloseHandle(m_pollTimer);
|
||||||
CloseHandle(m_stopEvent);
|
CloseHandle(m_stopEvent);
|
||||||
m_pollTimer = nullptr;
|
m_pollTimer = nullptr;
|
||||||
@@ -163,18 +164,13 @@ bool CLGMPInputTransport::Start(IInputSink& sink)
|
|||||||
|
|
||||||
void CLGMPInputTransport::Stop()
|
void CLGMPInputTransport::Stop()
|
||||||
{
|
{
|
||||||
HANDLE thread;
|
|
||||||
{
|
|
||||||
CSRWExclusiveLock lock(&m_lifecycleLock);
|
|
||||||
thread = m_thread;
|
|
||||||
if (m_stopEvent)
|
|
||||||
SetEvent(m_stopEvent);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (thread)
|
|
||||||
WaitForSingleObject(thread, INFINITE);
|
|
||||||
|
|
||||||
CSRWExclusiveLock lock(&m_lifecycleLock);
|
CSRWExclusiveLock lock(&m_lifecycleLock);
|
||||||
|
|
||||||
|
if (m_stopEvent)
|
||||||
|
SetEvent(m_stopEvent);
|
||||||
|
if (m_thread)
|
||||||
|
WaitForSingleObject(m_thread, INFINITE);
|
||||||
|
|
||||||
if (m_thread)
|
if (m_thread)
|
||||||
{
|
{
|
||||||
CloseHandle(m_thread);
|
CloseHandle(m_thread);
|
||||||
@@ -195,7 +191,7 @@ void CLGMPInputTransport::Stop()
|
|||||||
m_ownerGeneration = 0;
|
m_ownerGeneration = 0;
|
||||||
m_ownerSequence = 0;
|
m_ownerSequence = 0;
|
||||||
m_ownerDeadline = 0;
|
m_ownerDeadline = 0;
|
||||||
m_sinkGeneration = 0;
|
m_sinkState = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CLGMPInputTransport::IsOwner(
|
bool CLGMPInputTransport::IsOwner(
|
||||||
@@ -208,13 +204,12 @@ bool CLGMPInputTransport::IsOwner(
|
|||||||
bool CLGMPInputTransport::Claim(
|
bool CLGMPInputTransport::Claim(
|
||||||
uint32_t sourceClientID, const KVMFRInputMessage& message)
|
uint32_t sourceClientID, const KVMFRInputMessage& message)
|
||||||
{
|
{
|
||||||
if (message.sequence != 1 || !m_sink || !m_sink->IsAvailable())
|
if (message.sequence != 1 || !m_sink)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
const uint64_t sinkGeneration = m_sink->GetGeneration();
|
const uint64_t sinkState = m_sink->GetState();
|
||||||
if (sinkGeneration != m_sinkGeneration || !m_sink->Reset() ||
|
if (!(sinkState & 1) || sinkState != m_sinkState ||
|
||||||
!m_sink->IsAvailable() ||
|
!m_sink->Reset() || m_sink->GetState() != sinkState)
|
||||||
m_sink->GetGeneration() != sinkGeneration)
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
m_ownerClientID = sourceClientID;
|
m_ownerClientID = sourceClientID;
|
||||||
@@ -255,10 +250,10 @@ void CLGMPInputTransport::CheckOwner()
|
|||||||
if (!m_sink)
|
if (!m_sink)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const uint64_t generation = m_sink->GetGeneration();
|
const uint64_t state = m_sink->GetState();
|
||||||
if (generation != m_sinkGeneration)
|
if (state != m_sinkState)
|
||||||
{
|
{
|
||||||
m_sinkGeneration = generation;
|
m_sinkState = state;
|
||||||
ReleaseOwner(true, "input endpoint changed");
|
ReleaseOwner(true, "input endpoint changed");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -266,7 +261,7 @@ void CLGMPInputTransport::CheckOwner()
|
|||||||
if (!m_ownerClientID)
|
if (!m_ownerClientID)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!m_sink->IsAvailable())
|
if (!(state & 1))
|
||||||
{
|
{
|
||||||
ReleaseOwner(true, "input unavailable");
|
ReleaseOwner(true, "input unavailable");
|
||||||
return;
|
return;
|
||||||
@@ -323,11 +318,11 @@ bool CLGMPInputTransport::ProcessMessage(
|
|||||||
uint32_t sourceClientID, const KVMFRInputMessage& message)
|
uint32_t sourceClientID, const KVMFRInputMessage& message)
|
||||||
{
|
{
|
||||||
const bool owner = IsOwner(sourceClientID, message.generation);
|
const bool owner = IsOwner(sourceClientID, message.generation);
|
||||||
const uint64_t sinkGeneration = m_sink ?
|
const uint64_t sinkState = m_sink ?
|
||||||
m_sink->GetGeneration() : m_sinkGeneration;
|
m_sink->GetState() : m_sinkState;
|
||||||
if (sinkGeneration != m_sinkGeneration)
|
if (sinkState != m_sinkState)
|
||||||
{
|
{
|
||||||
m_sinkGeneration = sinkGeneration;
|
m_sinkState = sinkState;
|
||||||
if (m_ownerClientID)
|
if (m_ownerClientID)
|
||||||
{
|
{
|
||||||
ReleaseOwner(true, "input endpoint changed");
|
ReleaseOwner(true, "input endpoint changed");
|
||||||
@@ -417,10 +412,10 @@ bool CLGMPInputTransport::ProcessMessage(
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const uint64_t deliveredGeneration = m_sink->GetGeneration();
|
const uint64_t deliveredState = m_sink->GetState();
|
||||||
if (deliveredGeneration != m_sinkGeneration)
|
if (deliveredState != m_sinkState)
|
||||||
{
|
{
|
||||||
m_sinkGeneration = deliveredGeneration;
|
m_sinkState = deliveredState;
|
||||||
ReleaseOwner(true, "input endpoint changed");
|
ReleaseOwner(true, "input endpoint changed");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -503,9 +498,9 @@ void CLGMPInputTransport::Thread()
|
|||||||
|
|
||||||
const DWORD wait = WaitForMultipleObjects(
|
const DWORD wait = WaitForMultipleObjects(
|
||||||
_countof(waitHandles), waitHandles, FALSE, INFINITE);
|
_countof(waitHandles), waitHandles, FALSE, INFINITE);
|
||||||
if (wait == WAIT_OBJECT_0)
|
if (wait == WAIT_FIRST_OBJECT_VALUE)
|
||||||
break;
|
break;
|
||||||
if (wait != WAIT_OBJECT_0 + 1)
|
if (wait != WAIT_FIRST_OBJECT_VALUE + 1)
|
||||||
{
|
{
|
||||||
DEBUG_ERROR_HR(GetLastError(), "LGMP input worker wait failed");
|
DEBUG_ERROR_HR(GetLastError(), "LGMP input worker wait failed");
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ private:
|
|||||||
uint32_t m_ownerGeneration = 0;
|
uint32_t m_ownerGeneration = 0;
|
||||||
uint32_t m_ownerSequence = 0;
|
uint32_t m_ownerSequence = 0;
|
||||||
ULONGLONG m_ownerDeadline = 0;
|
ULONGLONG m_ownerDeadline = 0;
|
||||||
uint64_t m_sinkGeneration = 0;
|
uint64_t m_sinkState = 0;
|
||||||
|
|
||||||
bool Initialize();
|
bool Initialize();
|
||||||
void DeInit();
|
void DeInit();
|
||||||
|
|||||||
@@ -26,16 +26,21 @@
|
|||||||
#include "ipc/CInputPipeClient.h"
|
#include "ipc/CInputPipeClient.h"
|
||||||
|
|
||||||
#include <hidport.h>
|
#include <hidport.h>
|
||||||
|
#include <limits.h>
|
||||||
#include <new>
|
#include <new>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
static constexpr USHORT LG_INPUT_VENDOR_ID = 0x0000;
|
static constexpr USHORT LG_INPUT_VENDOR_ID = 0x0000;
|
||||||
static constexpr USHORT LG_INPUT_PRODUCT_ID = 0x0000;
|
static constexpr USHORT LG_INPUT_PRODUCT_ID = 0x0000;
|
||||||
static constexpr USHORT LG_INPUT_VERSION = 0x0001;
|
static constexpr USHORT LG_INPUT_VERSION = 0x0001;
|
||||||
static constexpr size_t REPORT_QUEUE_LENGTH = 64;
|
static constexpr size_t REPORT_QUEUE_LENGTH = 64;
|
||||||
|
static constexpr size_t MOTION_COALESCE_THRESHOLD =
|
||||||
|
REPORT_QUEUE_LENGTH / 2;
|
||||||
|
|
||||||
struct HIDQueuedReport
|
struct HIDQueuedReport
|
||||||
{
|
{
|
||||||
UCHAR data[sizeof(HIDKeyboardReport)];
|
UCHAR data[HID_MAX_INPUT_REPORT_SIZE];
|
||||||
|
bool pureMotion;
|
||||||
size_t size;
|
size_t size;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -49,8 +54,14 @@ struct HIDDeviceContext
|
|||||||
SRWLOCK reportLock;
|
SRWLOCK reportLock;
|
||||||
bool active;
|
bool active;
|
||||||
bool stopping;
|
bool stopping;
|
||||||
|
UCHAR mouseMode;
|
||||||
size_t reportHead;
|
size_t reportHead;
|
||||||
size_t reportCount;
|
size_t reportCount;
|
||||||
|
uint32_t relativeButtons;
|
||||||
|
uint32_t absoluteButtons;
|
||||||
|
bool absoluteValid;
|
||||||
|
uint16_t absoluteX;
|
||||||
|
uint16_t absoluteY;
|
||||||
HIDQueuedReport reports[REPORT_QUEUE_LENGTH];
|
HIDQueuedReport reports[REPORT_QUEUE_LENGTH];
|
||||||
CInputPipeClient * inputPipe;
|
CInputPipeClient * inputPipe;
|
||||||
};
|
};
|
||||||
@@ -151,19 +162,114 @@ static void PopReport(
|
|||||||
--context->reportCount;
|
--context->reportCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static HIDQueuedReport& GetQueuedReport(
|
||||||
|
_Inout_ HIDDeviceContext * context,
|
||||||
|
_In_ size_t position)
|
||||||
|
{
|
||||||
|
return context->reports[
|
||||||
|
(context->reportHead + position) % REPORT_QUEUE_LENGTH];
|
||||||
|
}
|
||||||
|
|
||||||
|
static void RemoveQueuedReport(
|
||||||
|
_Inout_ HIDDeviceContext * context,
|
||||||
|
_In_ size_t position)
|
||||||
|
{
|
||||||
|
for (size_t i = position; i + 1 < context->reportCount; ++i)
|
||||||
|
GetQueuedReport(context, i) = GetQueuedReport(context, i + 1);
|
||||||
|
--context->reportCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool CompactStaleMotion(
|
||||||
|
_Inout_ HIDDeviceContext * context,
|
||||||
|
_In_ UCHAR incomingReportId)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < context->reportCount; ++i)
|
||||||
|
{
|
||||||
|
HIDQueuedReport& report = GetQueuedReport(context, i);
|
||||||
|
if (!report.pureMotion ||
|
||||||
|
report.data[0] != HID_REPORT_ID_MOUSE_ABSOLUTE)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
bool superseded = incomingReportId == HID_REPORT_ID_MOUSE_ABSOLUTE;
|
||||||
|
for (size_t j = i + 1; !superseded && j < context->reportCount; ++j)
|
||||||
|
superseded = GetQueuedReport(context, j).data[0] ==
|
||||||
|
HID_REPORT_ID_MOUSE_ABSOLUTE;
|
||||||
|
|
||||||
|
if (superseded)
|
||||||
|
{
|
||||||
|
RemoveQueuedReport(context, i);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
static NTSTATUS QueueReport(
|
static NTSTATUS QueueReport(
|
||||||
_Inout_ HIDDeviceContext * context,
|
_Inout_ HIDDeviceContext * context,
|
||||||
_In_reads_bytes_(size) const void * data,
|
_In_reads_bytes_(size) const void * data,
|
||||||
_In_ size_t size)
|
_In_ size_t size,
|
||||||
|
_In_ bool pureMotion)
|
||||||
{
|
{
|
||||||
|
const UCHAR reportId = *static_cast<const UCHAR *>(data);
|
||||||
|
if (context->reportCount)
|
||||||
|
{
|
||||||
|
HIDQueuedReport& tail =
|
||||||
|
GetQueuedReport(context, context->reportCount - 1);
|
||||||
|
if (tail.data[0] == reportId)
|
||||||
|
{
|
||||||
|
if (reportId == HID_REPORT_ID_MOUSE_RELATIVE &&
|
||||||
|
context->reportCount >= MOTION_COALESCE_THRESHOLD &&
|
||||||
|
tail.pureMotion && pureMotion)
|
||||||
|
{
|
||||||
|
HIDMouseRelativeReport * previous =
|
||||||
|
reinterpret_cast<HIDMouseRelativeReport *>(tail.data);
|
||||||
|
const HIDMouseRelativeReport * current =
|
||||||
|
static_cast<const HIDMouseRelativeReport *>(data);
|
||||||
|
const int32_t x = static_cast<int32_t>(previous->x) +
|
||||||
|
current->x;
|
||||||
|
const int32_t y = static_cast<int32_t>(previous->y) +
|
||||||
|
current->y;
|
||||||
|
if (x >= INT16_MIN && x <= INT16_MAX &&
|
||||||
|
y >= INT16_MIN && y <= INT16_MAX &&
|
||||||
|
previous->buttons == current->buttons)
|
||||||
|
{
|
||||||
|
previous->x = static_cast<int16_t>(x);
|
||||||
|
previous->y = static_cast<int16_t>(y);
|
||||||
|
return STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (reportId == HID_REPORT_ID_MOUSE_ABSOLUTE &&
|
||||||
|
tail.pureMotion && pureMotion)
|
||||||
|
{
|
||||||
|
const HIDMouseAbsoluteReport * previous =
|
||||||
|
reinterpret_cast<const HIDMouseAbsoluteReport *>(tail.data);
|
||||||
|
const HIDMouseAbsoluteReport * current =
|
||||||
|
static_cast<const HIDMouseAbsoluteReport *>(data);
|
||||||
|
if (previous->buttons == current->buttons)
|
||||||
|
{
|
||||||
|
CopyMemory(tail.data, data, size);
|
||||||
|
tail.size = size;
|
||||||
|
return STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (reportId == HID_REPORT_ID_KEYBOARD &&
|
||||||
|
tail.size == size && memcmp(tail.data, data, size) == 0)
|
||||||
|
return STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (context->reportCount == REPORT_QUEUE_LENGTH)
|
if (context->reportCount == REPORT_QUEUE_LENGTH)
|
||||||
return STATUS_BUFFER_OVERFLOW;
|
{
|
||||||
|
if (!CompactStaleMotion(context, reportId))
|
||||||
|
return STATUS_BUFFER_OVERFLOW;
|
||||||
|
}
|
||||||
|
|
||||||
const size_t index =
|
const size_t index =
|
||||||
(context->reportHead + context->reportCount) % REPORT_QUEUE_LENGTH;
|
(context->reportHead + context->reportCount) % REPORT_QUEUE_LENGTH;
|
||||||
HIDQueuedReport * report = &context->reports[index];
|
HIDQueuedReport * report = &context->reports[index];
|
||||||
CopyMemory(report->data, data, size);
|
CopyMemory(report->data, data, size);
|
||||||
report->size = size;
|
report->size = size;
|
||||||
|
report->pureMotion = pureMotion;
|
||||||
++context->reportCount;
|
++context->reportCount;
|
||||||
return STATUS_SUCCESS;
|
return STATUS_SUCCESS;
|
||||||
}
|
}
|
||||||
@@ -343,38 +449,136 @@ NTSTATUS CHIDDevice::SubmitReport(
|
|||||||
status = STATUS_DEVICE_NOT_READY;
|
status = STATUS_DEVICE_NOT_READY;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
bool pureMotion = false;
|
||||||
|
switch (reportId)
|
||||||
|
{
|
||||||
|
case HID_REPORT_ID_MOUSE_RELATIVE:
|
||||||
|
{
|
||||||
|
const HIDMouseRelativeReport * mouse =
|
||||||
|
static_cast<const HIDMouseRelativeReport *>(report);
|
||||||
|
pureMotion = context->mouseMode == reportId &&
|
||||||
|
(mouse->x != 0 || mouse->y != 0 || mouse->buttons != 0) &&
|
||||||
|
mouse->wheel == 0 &&
|
||||||
|
mouse->buttons == context->relativeButtons;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case HID_REPORT_ID_MOUSE_ABSOLUTE:
|
||||||
|
{
|
||||||
|
const HIDMouseAbsoluteReport * mouse =
|
||||||
|
static_cast<const HIDMouseAbsoluteReport *>(report);
|
||||||
|
pureMotion = context->mouseMode == reportId &&
|
||||||
|
(mouse->x != context->absoluteX ||
|
||||||
|
mouse->y != context->absoluteY || mouse->buttons != 0) &&
|
||||||
|
mouse->wheel == 0 &&
|
||||||
|
mouse->buttons == context->absoluteButtons;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
status = WdfIoQueueRetrieveNextRequest(context->reportQueue, &request);
|
status = WdfIoQueueRetrieveNextRequest(context->reportQueue, &request);
|
||||||
if (status == STATUS_NO_MORE_ENTRIES)
|
if (status == STATUS_NO_MORE_ENTRIES)
|
||||||
{
|
{
|
||||||
request = nullptr;
|
request = nullptr;
|
||||||
status = QueueReport(context, report, size);
|
status = QueueReport(context, report, size, pureMotion);
|
||||||
|
}
|
||||||
|
else if (NT_SUCCESS(status))
|
||||||
|
status = CopyToRequest(request, report, size);
|
||||||
|
|
||||||
|
if (NT_SUCCESS(status))
|
||||||
|
{
|
||||||
|
switch (reportId)
|
||||||
|
{
|
||||||
|
case HID_REPORT_ID_MOUSE_RELATIVE:
|
||||||
|
context->mouseMode = reportId;
|
||||||
|
context->relativeButtons =
|
||||||
|
static_cast<const HIDMouseRelativeReport *>(report)->buttons;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case HID_REPORT_ID_MOUSE_ABSOLUTE:
|
||||||
|
{
|
||||||
|
const HIDMouseAbsoluteReport * mouse =
|
||||||
|
static_cast<const HIDMouseAbsoluteReport *>(report);
|
||||||
|
context->mouseMode = reportId;
|
||||||
|
context->absoluteButtons = mouse->buttons;
|
||||||
|
context->absoluteValid = true;
|
||||||
|
context->absoluteX = mouse->x;
|
||||||
|
context->absoluteY = mouse->y;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (request)
|
if (request)
|
||||||
{
|
|
||||||
status = CopyToRequest(request, report, size);
|
|
||||||
WdfRequestComplete(request, status);
|
WdfRequestComplete(request, status);
|
||||||
}
|
|
||||||
|
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
NTSTATUS CHIDDevice::ClearReports()
|
NTSTATUS CHIDDevice::ResetReports()
|
||||||
{
|
{
|
||||||
CSRWSharedLock deviceLock(&s_deviceLock);
|
uint16_t absoluteX = 0;
|
||||||
HIDDeviceContext * context = s_device;
|
uint16_t absoluteY = 0;
|
||||||
if (!context)
|
bool absoluteValid = false;
|
||||||
return STATUS_DEVICE_NOT_READY;
|
{
|
||||||
|
CSRWSharedLock deviceLock(&s_deviceLock);
|
||||||
|
HIDDeviceContext * context = s_device;
|
||||||
|
if (!context)
|
||||||
|
return STATUS_DEVICE_NOT_READY;
|
||||||
|
|
||||||
CSRWExclusiveLock reportLock(&context->reportLock);
|
CSRWExclusiveLock reportLock(&context->reportLock);
|
||||||
if (context->stopping)
|
if (context->stopping)
|
||||||
return STATUS_DEVICE_NOT_READY;
|
return STATUS_DEVICE_NOT_READY;
|
||||||
|
|
||||||
context->reportHead = 0;
|
absoluteValid = context->absoluteValid;
|
||||||
context->reportCount = 0;
|
absoluteX = context->absoluteX;
|
||||||
return STATUS_SUCCESS;
|
absoluteY = context->absoluteY;
|
||||||
|
context->reportHead = 0;
|
||||||
|
context->reportCount = 0;
|
||||||
|
context->mouseMode = 0;
|
||||||
|
context->relativeButtons = 0;
|
||||||
|
context->absoluteButtons = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const HIDMouseRelativeReport relative = {
|
||||||
|
HID_REPORT_ID_MOUSE_RELATIVE,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
};
|
||||||
|
const HIDKeyboardReport keyboard = {
|
||||||
|
HID_REPORT_ID_KEYBOARD,
|
||||||
|
};
|
||||||
|
|
||||||
|
NTSTATUS status = SubmitReport(&relative, sizeof(relative));
|
||||||
|
if (absoluteValid)
|
||||||
|
{
|
||||||
|
const HIDMouseAbsoluteReport absolute = {
|
||||||
|
HID_REPORT_ID_MOUSE_ABSOLUTE,
|
||||||
|
0,
|
||||||
|
absoluteX,
|
||||||
|
absoluteY,
|
||||||
|
0,
|
||||||
|
};
|
||||||
|
const NTSTATUS absoluteStatus =
|
||||||
|
SubmitReport(&absolute, sizeof(absolute));
|
||||||
|
if (NT_SUCCESS(status))
|
||||||
|
status = absoluteStatus;
|
||||||
|
}
|
||||||
|
const NTSTATUS keyboardStatus =
|
||||||
|
SubmitReport(&keyboard, sizeof(keyboard));
|
||||||
|
if (NT_SUCCESS(status))
|
||||||
|
status = keyboardStatus;
|
||||||
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
VOID HIDEvtIoDeviceControl(
|
VOID HIDEvtIoDeviceControl(
|
||||||
|
|||||||
@@ -30,5 +30,5 @@ public:
|
|||||||
static NTSTATUS SubmitReport(
|
static NTSTATUS SubmitReport(
|
||||||
_In_reads_bytes_(size) const void * report,
|
_In_reads_bytes_(size) const void * report,
|
||||||
_In_ size_t size);
|
_In_ size_t size);
|
||||||
static NTSTATUS ClearReports();
|
static NTSTATUS ResetReports();
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -31,15 +31,12 @@ static const uint8_t REPORT_DESCRIPTOR[] =
|
|||||||
0xA1, 0x00, // Collection (Physical)
|
0xA1, 0x00, // Collection (Physical)
|
||||||
0x05, 0x09, // Usage Page (Button)
|
0x05, 0x09, // Usage Page (Button)
|
||||||
0x19, 0x01, // Usage Minimum (Button 1)
|
0x19, 0x01, // Usage Minimum (Button 1)
|
||||||
0x29, 0x05, // Usage Maximum (Button 5)
|
0x29, 0x20, // Usage Maximum (Button 32)
|
||||||
0x15, 0x00, // Logical Minimum (0)
|
0x15, 0x00, // Logical Minimum (0)
|
||||||
0x25, 0x01, // Logical Maximum (1)
|
0x25, 0x01, // Logical Maximum (1)
|
||||||
0x75, 0x01, // Report Size (1)
|
0x75, 0x01, // Report Size (1)
|
||||||
0x95, 0x05, // Report Count (5)
|
0x95, 0x20, // Report Count (32)
|
||||||
0x81, 0x02, // Input (Data, Variable, Absolute)
|
0x81, 0x02, // Input (Data, Variable, Absolute)
|
||||||
0x75, 0x03, // Report Size (3)
|
|
||||||
0x95, 0x01, // Report Count (1)
|
|
||||||
0x81, 0x03, // Input (Constant)
|
|
||||||
0x05, 0x01, // Usage Page (Generic Desktop)
|
0x05, 0x01, // Usage Page (Generic Desktop)
|
||||||
0x09, 0x30, // Usage (X)
|
0x09, 0x30, // Usage (X)
|
||||||
0x09, 0x31, // Usage (Y)
|
0x09, 0x31, // Usage (Y)
|
||||||
@@ -66,15 +63,12 @@ static const uint8_t REPORT_DESCRIPTOR[] =
|
|||||||
0xA1, 0x00, // Collection (Physical)
|
0xA1, 0x00, // Collection (Physical)
|
||||||
0x05, 0x09, // Usage Page (Button)
|
0x05, 0x09, // Usage Page (Button)
|
||||||
0x19, 0x01, // Usage Minimum (Button 1)
|
0x19, 0x01, // Usage Minimum (Button 1)
|
||||||
0x29, 0x05, // Usage Maximum (Button 5)
|
0x29, 0x20, // Usage Maximum (Button 32)
|
||||||
0x15, 0x00, // Logical Minimum (0)
|
0x15, 0x00, // Logical Minimum (0)
|
||||||
0x25, 0x01, // Logical Maximum (1)
|
0x25, 0x01, // Logical Maximum (1)
|
||||||
0x75, 0x01, // Report Size (1)
|
0x75, 0x01, // Report Size (1)
|
||||||
0x95, 0x05, // Report Count (5)
|
0x95, 0x20, // Report Count (32)
|
||||||
0x81, 0x02, // Input (Data, Variable, Absolute)
|
0x81, 0x02, // Input (Data, Variable, Absolute)
|
||||||
0x75, 0x03, // Report Size (3)
|
|
||||||
0x95, 0x01, // Report Count (1)
|
|
||||||
0x81, 0x03, // Input (Constant)
|
|
||||||
0x05, 0x01, // Usage Page (Generic Desktop)
|
0x05, 0x01, // Usage Page (Generic Desktop)
|
||||||
0x09, 0x30, // Usage (X)
|
0x09, 0x30, // Usage (X)
|
||||||
0x09, 0x31, // Usage (Y)
|
0x09, 0x31, // Usage (Y)
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ enum HIDReportId : uint8_t
|
|||||||
struct HIDMouseAbsoluteReport
|
struct HIDMouseAbsoluteReport
|
||||||
{
|
{
|
||||||
uint8_t reportId;
|
uint8_t reportId;
|
||||||
uint8_t buttons;
|
uint32_t buttons;
|
||||||
uint16_t x;
|
uint16_t x;
|
||||||
uint16_t y;
|
uint16_t y;
|
||||||
int8_t wheel;
|
int8_t wheel;
|
||||||
@@ -44,7 +44,7 @@ struct HIDMouseAbsoluteReport
|
|||||||
struct HIDMouseRelativeReport
|
struct HIDMouseRelativeReport
|
||||||
{
|
{
|
||||||
uint8_t reportId;
|
uint8_t reportId;
|
||||||
uint8_t buttons;
|
uint32_t buttons;
|
||||||
int16_t x;
|
int16_t x;
|
||||||
int16_t y;
|
int16_t y;
|
||||||
int8_t wheel;
|
int8_t wheel;
|
||||||
@@ -66,11 +66,14 @@ struct HIDKeyboardLedsReport
|
|||||||
|
|
||||||
#pragma pack(pop)
|
#pragma pack(pop)
|
||||||
|
|
||||||
static_assert(sizeof(HIDMouseAbsoluteReport) == 7);
|
static_assert(sizeof(HIDMouseAbsoluteReport) == 10);
|
||||||
static_assert(sizeof(HIDMouseRelativeReport) == 7);
|
static_assert(sizeof(HIDMouseRelativeReport) == 10);
|
||||||
static_assert(sizeof(HIDKeyboardReport) == 9);
|
static_assert(sizeof(HIDKeyboardReport) == 9);
|
||||||
static_assert(sizeof(HIDKeyboardLedsReport) == 2);
|
static_assert(sizeof(HIDKeyboardLedsReport) == 2);
|
||||||
|
|
||||||
|
static constexpr size_t HID_MAX_INPUT_REPORT_SIZE =
|
||||||
|
sizeof(HIDMouseAbsoluteReport);
|
||||||
|
|
||||||
const uint8_t * HIDGetReportDescriptor();
|
const uint8_t * HIDGetReportDescriptor();
|
||||||
size_t HIDGetReportDescriptorSize();
|
size_t HIDGetReportDescriptorSize();
|
||||||
size_t HIDGetInputReportSize(uint8_t reportId);
|
size_t HIDGetInputReportSize(uint8_t reportId);
|
||||||
|
|||||||
@@ -80,7 +80,7 @@
|
|||||||
<WppRecorderEnabled>true</WppRecorderEnabled>
|
<WppRecorderEnabled>true</WppRecorderEnabled>
|
||||||
<WppScanConfigurationData Condition="'%(ClCompile.ScanConfigurationData)' == ''">Trace.h</WppScanConfigurationData>
|
<WppScanConfigurationData Condition="'%(ClCompile.ScanConfigurationData)' == ''">Trace.h</WppScanConfigurationData>
|
||||||
<AdditionalOptions>/EHsc /D_ATL_NO_WIN_SUPPORT %(AdditionalOptions)</AdditionalOptions>
|
<AdditionalOptions>/EHsc /D_ATL_NO_WIN_SUPPORT %(AdditionalOptions)</AdditionalOptions>
|
||||||
<AdditionalIncludeDirectories>$(ProjectDir)..\LGCommon;$(DDK_INC_PATH);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
<AdditionalIncludeDirectories>$(ProjectDir)..\LGCommon;$(ProjectDir)..\..\common\include;$(DDK_INC_PATH);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<Link>
|
<Link>
|
||||||
<AdditionalDependencies>%(AdditionalDependencies);OneCoreUAP.lib</AdditionalDependencies>
|
<AdditionalDependencies>%(AdditionalDependencies);OneCoreUAP.lib</AdditionalDependencies>
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ void CInputPipeClient::Stop()
|
|||||||
m_endpoint.Stop();
|
m_endpoint.Stop();
|
||||||
m_lastSequence = 0;
|
m_lastSequence = 0;
|
||||||
if (wasRunning)
|
if (wasRunning)
|
||||||
CHIDDevice::ClearReports();
|
CHIDDevice::ResetReports();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CInputPipeClient::OnPipeConnected()
|
void CInputPipeClient::OnPipeConnected()
|
||||||
@@ -55,7 +55,7 @@ void CInputPipeClient::OnPipeConnected()
|
|||||||
void CInputPipeClient::OnPipeDisconnected()
|
void CInputPipeClient::OnPipeDisconnected()
|
||||||
{
|
{
|
||||||
m_lastSequence = 0;
|
m_lastSequence = 0;
|
||||||
CHIDDevice::ClearReports();
|
CHIDDevice::ResetReports();
|
||||||
DEBUG_INFO("Disconnected from the LGIdd input transport; reconnecting");
|
DEBUG_INFO("Disconnected from the LGIdd input transport; reconnecting");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,6 +78,7 @@ bool CInputPipeClient::OnPipeMessage(
|
|||||||
(m_lastSequence && message.sequence != m_lastSequence + 1))
|
(m_lastSequence && message.sequence != m_lastSequence + 1))
|
||||||
{
|
{
|
||||||
DEBUG_WARN("LGInput pipe report sequence changed unexpectedly");
|
DEBUG_WARN("LGInput pipe report sequence changed unexpectedly");
|
||||||
|
CHIDDevice::ResetReports();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
bool handled = false;
|
bool handled = false;
|
||||||
@@ -117,18 +118,41 @@ bool CInputPipeClient::HandleMouseRelative(
|
|||||||
|
|
||||||
LGInputPipeMouseRelative input = {};
|
LGInputPipeMouseRelative input = {};
|
||||||
memcpy(&input, payload, sizeof(input));
|
memcpy(&input, payload, sizeof(input));
|
||||||
if (input.wheel < LG_INPUT_MOUSE_WHEEL_MIN ||
|
if (input.deltaX < LG_INPUT_MOUSE_DELTA_MIN ||
|
||||||
(input.buttons & ~LG_INPUT_MOUSE_BUTTON_MASK))
|
input.deltaX > LG_INPUT_MOUSE_DELTA_MAX ||
|
||||||
|
input.deltaY < LG_INPUT_MOUSE_DELTA_MIN ||
|
||||||
|
input.deltaY > LG_INPUT_MOUSE_DELTA_MAX ||
|
||||||
|
input.wheel < LG_INPUT_MOUSE_WHEEL_MIN_TOTAL ||
|
||||||
|
input.wheel > LG_INPUT_MOUSE_WHEEL_MAX)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
const HIDMouseRelativeReport report = {
|
int32_t x = input.deltaX;
|
||||||
HID_REPORT_ID_MOUSE_RELATIVE,
|
int32_t y = input.deltaY;
|
||||||
input.buttons,
|
int32_t wheel = input.wheel;
|
||||||
input.deltaX,
|
do
|
||||||
input.deltaY,
|
{
|
||||||
input.wheel,
|
const int16_t reportX = x > INT16_MAX ? INT16_MAX :
|
||||||
};
|
x < INT16_MIN ? INT16_MIN : static_cast<int16_t>(x);
|
||||||
return SubmitReport(&report, sizeof(report));
|
const int16_t reportY = y > INT16_MAX ? INT16_MAX :
|
||||||
|
y < INT16_MIN ? INT16_MIN : static_cast<int16_t>(y);
|
||||||
|
const int8_t reportWheel = wheel > INT8_MAX ? INT8_MAX :
|
||||||
|
wheel < LG_INPUT_MOUSE_WHEEL_MIN ? LG_INPUT_MOUSE_WHEEL_MIN :
|
||||||
|
static_cast<int8_t>(wheel);
|
||||||
|
const HIDMouseRelativeReport report = {
|
||||||
|
HID_REPORT_ID_MOUSE_RELATIVE,
|
||||||
|
input.buttons,
|
||||||
|
reportX,
|
||||||
|
reportY,
|
||||||
|
reportWheel,
|
||||||
|
};
|
||||||
|
if (!SubmitReport(&report, sizeof(report)))
|
||||||
|
return false;
|
||||||
|
x -= reportX;
|
||||||
|
y -= reportY;
|
||||||
|
wheel -= reportWheel;
|
||||||
|
}
|
||||||
|
while (x || y || wheel);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CInputPipeClient::HandleMouseAbsolute(
|
bool CInputPipeClient::HandleMouseAbsolute(
|
||||||
@@ -142,18 +166,30 @@ bool CInputPipeClient::HandleMouseAbsolute(
|
|||||||
memcpy(&input, payload, sizeof(input));
|
memcpy(&input, payload, sizeof(input));
|
||||||
if (input.x > LG_INPUT_MOUSE_ABSOLUTE_MAX ||
|
if (input.x > LG_INPUT_MOUSE_ABSOLUTE_MAX ||
|
||||||
input.y > LG_INPUT_MOUSE_ABSOLUTE_MAX ||
|
input.y > LG_INPUT_MOUSE_ABSOLUTE_MAX ||
|
||||||
input.wheel < LG_INPUT_MOUSE_WHEEL_MIN ||
|
input.wheel < LG_INPUT_MOUSE_WHEEL_MIN_TOTAL ||
|
||||||
(input.buttons & ~LG_INPUT_MOUSE_BUTTON_MASK))
|
input.wheel > LG_INPUT_MOUSE_WHEEL_MAX ||
|
||||||
|
input.reserved)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
const HIDMouseAbsoluteReport report = {
|
int32_t wheel = input.wheel;
|
||||||
HID_REPORT_ID_MOUSE_ABSOLUTE,
|
do
|
||||||
input.buttons,
|
{
|
||||||
input.x,
|
const int8_t reportWheel = wheel > INT8_MAX ? INT8_MAX :
|
||||||
input.y,
|
wheel < LG_INPUT_MOUSE_WHEEL_MIN ? LG_INPUT_MOUSE_WHEEL_MIN :
|
||||||
input.wheel,
|
static_cast<int8_t>(wheel);
|
||||||
};
|
const HIDMouseAbsoluteReport report = {
|
||||||
return SubmitReport(&report, sizeof(report));
|
HID_REPORT_ID_MOUSE_ABSOLUTE,
|
||||||
|
input.buttons,
|
||||||
|
input.x,
|
||||||
|
input.y,
|
||||||
|
reportWheel,
|
||||||
|
};
|
||||||
|
if (!SubmitReport(&report, sizeof(report)))
|
||||||
|
return false;
|
||||||
|
wheel -= reportWheel;
|
||||||
|
}
|
||||||
|
while (wheel);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CInputPipeClient::HandleKeyboard(
|
bool CInputPipeClient::HandleKeyboard(
|
||||||
@@ -166,6 +202,10 @@ bool CInputPipeClient::HandleKeyboard(
|
|||||||
LGInputPipeKeyboard input = {};
|
LGInputPipeKeyboard input = {};
|
||||||
memcpy(&input, payload, sizeof(input));
|
memcpy(&input, payload, sizeof(input));
|
||||||
|
|
||||||
|
for (size_t i = 0; i < sizeof(input.reserved); ++i)
|
||||||
|
if (input.reserved[i])
|
||||||
|
return false;
|
||||||
|
|
||||||
HIDKeyboardReport report = {};
|
HIDKeyboardReport report = {};
|
||||||
report.reportId = HID_REPORT_ID_KEYBOARD;
|
report.reportId = HID_REPORT_ID_KEYBOARD;
|
||||||
report.modifiers = input.modifiers;
|
report.modifiers = input.modifiers;
|
||||||
@@ -189,8 +229,16 @@ bool CInputPipeClient::SubmitReport(
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (status == STATUS_BUFFER_OVERFLOW)
|
if (status == STATUS_BUFFER_OVERFLOW)
|
||||||
DEBUG_WARN("LGInput HID report queue is full; dropping a report");
|
{
|
||||||
else if (!NT_SUCCESS(status) && status != STATUS_DEVICE_NOT_READY)
|
DEBUG_WARN("LGInput HID report queue overflowed; resetting input state");
|
||||||
DEBUG_WARN_HR(status, "Failed to submit an LGInput HID report");
|
CHIDDevice::ResetReports();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!NT_SUCCESS(status))
|
||||||
|
{
|
||||||
|
if (status != STATUS_DEVICE_NOT_READY)
|
||||||
|
DEBUG_WARN_HR(status, "Failed to submit an LGInput HID report");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,7 +32,6 @@ public:
|
|||||||
|
|
||||||
bool Start();
|
bool Start();
|
||||||
void Stop();
|
void Stop();
|
||||||
bool IsConnected() const { return m_endpoint.IsConnected(); }
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void OnPipeConnected() override;
|
void OnPipeConnected() override;
|
||||||
|
|||||||
Reference in New Issue
Block a user