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,
|
||||
DWORD messageSize)
|
||||
{
|
||||
HANDLE ioEvent = CreateEventW(nullptr, TRUE, FALSE, nullptr);
|
||||
if (!ioEvent)
|
||||
{
|
||||
DEBUG_ERROR_HR(GetLastError(), "Failed to create named pipe write event");
|
||||
return PipeIoResult::Error;
|
||||
}
|
||||
|
||||
ResetEvent(m_writeEvent);
|
||||
OVERLAPPED overlapped = {};
|
||||
overlapped.hEvent = ioEvent;
|
||||
overlapped.hEvent = m_writeEvent;
|
||||
DWORD bytesWritten = 0;
|
||||
PipeIoResult result = PipeIoResult::Success;
|
||||
|
||||
@@ -148,7 +142,7 @@ CPipeEndpoint::PipeIoResult CPipeEndpoint::WriteMessage(
|
||||
if (error == ERROR_IO_PENDING)
|
||||
result = WaitForOverlapped(
|
||||
pipe,
|
||||
ioEvent,
|
||||
m_writeEvent,
|
||||
&overlapped,
|
||||
&bytesWritten,
|
||||
WRITE_TIMEOUT_MS);
|
||||
@@ -174,7 +168,6 @@ CPipeEndpoint::PipeIoResult CPipeEndpoint::WriteMessage(
|
||||
result = PipeIoResult::Error;
|
||||
}
|
||||
|
||||
CloseHandle(ioEvent);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -197,9 +190,16 @@ bool CPipeEndpoint::Start(
|
||||
m_mode = mode;
|
||||
m_messageSize = messageSize;
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -210,6 +210,8 @@ bool CPipeEndpoint::Start(
|
||||
{
|
||||
CloseHandle(m_stopEvent);
|
||||
m_stopEvent = nullptr;
|
||||
CloseHandle(m_writeEvent);
|
||||
m_writeEvent = nullptr;
|
||||
return false;
|
||||
}
|
||||
PublishPipe(pipe);
|
||||
@@ -232,6 +234,8 @@ bool CPipeEndpoint::Start(
|
||||
|
||||
CloseHandle(m_stopEvent);
|
||||
m_stopEvent = nullptr;
|
||||
CloseHandle(m_writeEvent);
|
||||
m_writeEvent = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -241,6 +245,7 @@ bool CPipeEndpoint::Start(
|
||||
void CPipeEndpoint::Stop()
|
||||
{
|
||||
m_running.store(false);
|
||||
m_connected.store(false);
|
||||
if (m_stopEvent)
|
||||
SetEvent(m_stopEvent);
|
||||
|
||||
@@ -270,6 +275,12 @@ void CPipeEndpoint::Stop()
|
||||
m_stopEvent = nullptr;
|
||||
}
|
||||
|
||||
if (m_writeEvent)
|
||||
{
|
||||
CloseHandle(m_writeEvent);
|
||||
m_writeEvent = nullptr;
|
||||
}
|
||||
|
||||
m_connected.store(false);
|
||||
}
|
||||
|
||||
|
||||
@@ -128,4 +128,5 @@ private:
|
||||
HANDLE m_pipe = INVALID_HANDLE_VALUE;
|
||||
HANDLE m_thread = nullptr;
|
||||
HANDLE m_stopEvent = nullptr;
|
||||
HANDLE m_writeEvent = nullptr;
|
||||
};
|
||||
|
||||
@@ -20,72 +20,59 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/KVMFRInput.h"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
static constexpr wchar_t LG_INPUT_PIPE_NAME[] =
|
||||
L"\\\\.\\pipe\\LookingGlassIDDInput";
|
||||
|
||||
static constexpr uint32_t LG_INPUT_PIPE_MAGIC = 0x5049474c;
|
||||
static constexpr uint16_t LG_INPUT_PIPE_VERSION = 2;
|
||||
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 = 32767;
|
||||
static constexpr int8_t LG_INPUT_MOUSE_WHEEL_MIN = -127;
|
||||
static constexpr size_t LG_INPUT_KEYBOARD_KEY_COUNT = 6;
|
||||
static constexpr uint8_t LG_INPUT_KEYBOARD_USAGE_MAX = 0xe7;
|
||||
static constexpr uint32_t LG_INPUT_PIPE_MAGIC = 0x5049474c;
|
||||
static constexpr uint16_t LG_INPUT_PIPE_VERSION = 3;
|
||||
static constexpr size_t LG_INPUT_PIPE_MAX_PAYLOAD_SIZE = 64;
|
||||
static constexpr uint16_t LG_INPUT_MOUSE_ABSOLUTE_MAX =
|
||||
KVMFR_INPUT_MOUSE_ABSOLUTE_MAX;
|
||||
static constexpr int8_t LG_INPUT_MOUSE_WHEEL_MIN = -127;
|
||||
static constexpr int32_t LG_INPUT_MOUSE_DELTA_MAX = INT16_MAX * 4;
|
||||
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_RIGHT = 1 << 1,
|
||||
LG_INPUT_MOUSE_BUTTON_MIDDLE = 1 << 2,
|
||||
LG_INPUT_MOUSE_BUTTON_BACK = 1 << 3,
|
||||
LG_INPUT_MOUSE_BUTTON_LEFT = 1 << 0,
|
||||
LG_INPUT_MOUSE_BUTTON_RIGHT = 1 << 1,
|
||||
LG_INPUT_MOUSE_BUTTON_MIDDLE = 1 << 2,
|
||||
LG_INPUT_MOUSE_BUTTON_BACK = 1 << 3,
|
||||
LG_INPUT_MOUSE_BUTTON_FORWARD = 1 << 4,
|
||||
};
|
||||
|
||||
enum LGInputKeyboardModifier : uint8_t
|
||||
{
|
||||
LG_INPUT_KEYBOARD_MODIFIER_LEFT_CONTROL = 1 << 0,
|
||||
LG_INPUT_KEYBOARD_MODIFIER_LEFT_SHIFT = 1 << 1,
|
||||
LG_INPUT_KEYBOARD_MODIFIER_LEFT_ALT = 1 << 2,
|
||||
LG_INPUT_KEYBOARD_MODIFIER_LEFT_GUI = 1 << 3,
|
||||
LG_INPUT_KEYBOARD_MODIFIER_LEFT_CONTROL = 1 << 0,
|
||||
LG_INPUT_KEYBOARD_MODIFIER_LEFT_SHIFT = 1 << 1,
|
||||
LG_INPUT_KEYBOARD_MODIFIER_LEFT_ALT = 1 << 2,
|
||||
LG_INPUT_KEYBOARD_MODIFIER_LEFT_GUI = 1 << 3,
|
||||
LG_INPUT_KEYBOARD_MODIFIER_RIGHT_CONTROL = 1 << 4,
|
||||
LG_INPUT_KEYBOARD_MODIFIER_RIGHT_SHIFT = 1 << 5,
|
||||
LG_INPUT_KEYBOARD_MODIFIER_RIGHT_ALT = 1 << 6,
|
||||
LG_INPUT_KEYBOARD_MODIFIER_RIGHT_GUI = 1 << 7,
|
||||
LG_INPUT_KEYBOARD_MODIFIER_RIGHT_SHIFT = 1 << 5,
|
||||
LG_INPUT_KEYBOARD_MODIFIER_RIGHT_ALT = 1 << 6,
|
||||
LG_INPUT_KEYBOARD_MODIFIER_RIGHT_GUI = 1 << 7,
|
||||
};
|
||||
|
||||
enum LGInputPipeMessageType : uint16_t
|
||||
{
|
||||
LG_INPUT_PIPE_MESSAGE_MOUSE_ABSOLUTE = 1,
|
||||
LG_INPUT_PIPE_MESSAGE_MOUSE_RELATIVE = 2,
|
||||
LG_INPUT_PIPE_MESSAGE_KEYBOARD = 3,
|
||||
LG_INPUT_PIPE_MESSAGE_KEYBOARD = 3,
|
||||
};
|
||||
|
||||
#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
|
||||
{
|
||||
uint32_t magic;
|
||||
@@ -97,11 +84,15 @@ struct LGInputPipeMessage
|
||||
};
|
||||
#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");
|
||||
static_assert(sizeof(LGInputPipeMouseAbsolute) == 6,
|
||||
static_assert(sizeof(LGInputPipeMouseAbsolute) == 16,
|
||||
"LGInputPipeMouseAbsolute wire layout changed");
|
||||
static_assert(sizeof(LGInputPipeKeyboard) == 7,
|
||||
static_assert(sizeof(LGInputPipeKeyboard) == 16,
|
||||
"LGInputPipeKeyboard wire layout changed");
|
||||
static_assert(sizeof(LGInputPipeMessage) == 84,
|
||||
"LGInputPipeMessage wire layout changed");
|
||||
|
||||
@@ -61,7 +61,7 @@
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
<PreprocessorDefinitions>_ITERATOR_DEBUG_LEVEL=0;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<AdditionalOptions>/EHsc /D_ATL_NO_WIN_SUPPORT %(AdditionalOptions)</AdditionalOptions>
|
||||
<AdditionalIncludeDirectories>$(ProjectDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>$(ProjectDir);$(SolutionDir)..\common\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
|
||||
@@ -27,8 +27,8 @@ class IInputSink
|
||||
public:
|
||||
virtual ~IInputSink() = default;
|
||||
|
||||
virtual bool IsAvailable() const = 0;
|
||||
virtual uint64_t GetGeneration() const = 0;
|
||||
// Odd states are available; a state change invalidates in-flight input.
|
||||
virtual uint64_t GetState() const = 0;
|
||||
virtual bool SendMouseRelative(int32_t deltaX, int32_t deltaY,
|
||||
int32_t wheel, uint32_t buttons) = 0;
|
||||
virtual bool SendMouseAbsolute(uint16_t x, uint16_t y,
|
||||
|
||||
@@ -28,39 +28,191 @@
|
||||
|
||||
CInputPipeServer g_inputPipeServer;
|
||||
|
||||
static constexpr int32_t MAX_SPLIT_REPORTS = 4;
|
||||
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;
|
||||
static constexpr DWORD WAIT_FIRST_OBJECT_VALUE = 0;
|
||||
|
||||
bool CInputPipeServer::Init()
|
||||
{
|
||||
DeInit();
|
||||
|
||||
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);
|
||||
m_sequence = 0;
|
||||
m_mouseMode = MouseMode::NONE;
|
||||
m_absoluteValid = false;
|
||||
m_absoluteX = 0;
|
||||
m_absoluteY = 0;
|
||||
DEBUG_ERROR_HR(GetLastError(),
|
||||
"Failed to create LGInput sender resources");
|
||||
DeInit();
|
||||
return false;
|
||||
}
|
||||
|
||||
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);
|
||||
return m_endpoint.Start(
|
||||
LG_INPUT_PIPE_NAME,
|
||||
CPipeEndpoint::Mode::Server,
|
||||
sizeof(LGInputPipeMessage));
|
||||
if (!m_endpoint.Start(
|
||||
LG_INPUT_PIPE_NAME,
|
||||
CPipeEndpoint::Mode::Server,
|
||||
sizeof(LGInputPipeMessage)))
|
||||
{
|
||||
DeInit();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void CInputPipeServer::DeInit()
|
||||
{
|
||||
Invalidate();
|
||||
Invalidate(0, false);
|
||||
if (m_stopEvent)
|
||||
SetEvent(m_stopEvent);
|
||||
|
||||
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(
|
||||
@@ -69,66 +221,47 @@ bool CInputPipeServer::SendMouseRelative(
|
||||
int32_t wheel,
|
||||
uint32_t buttons)
|
||||
{
|
||||
if (deltaX < MIN_MOUSE_DELTA || deltaX > MAX_MOUSE_DELTA ||
|
||||
deltaY < MIN_MOUSE_DELTA || deltaY > MAX_MOUSE_DELTA ||
|
||||
wheel < MIN_MOUSE_WHEEL || wheel > MAX_MOUSE_WHEEL ||
|
||||
(buttons & ~static_cast<uint32_t>(LG_INPUT_MOUSE_BUTTON_MASK)))
|
||||
if (deltaX < LG_INPUT_MOUSE_DELTA_MIN ||
|
||||
deltaX > LG_INPUT_MOUSE_DELTA_MAX ||
|
||||
deltaY < LG_INPUT_MOUSE_DELTA_MIN ||
|
||||
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;
|
||||
|
||||
CSRWExclusiveLock lock(&m_sendLock);
|
||||
if (!IsAvailable())
|
||||
return false;
|
||||
KVMFRInputPayload payload = {};
|
||||
payload.mouseRelative.buttons = buttons;
|
||||
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 = {
|
||||
0,
|
||||
m_absoluteX,
|
||||
m_absoluteY,
|
||||
0,
|
||||
};
|
||||
if (!SendMessageLocked(
|
||||
LG_INPUT_PIPE_MESSAGE_MOUSE_ABSOLUTE,
|
||||
&neutral,
|
||||
sizeof(neutral)))
|
||||
{
|
||||
Invalidate();
|
||||
return false;
|
||||
}
|
||||
m_mouseMode = MouseMode::NONE;
|
||||
KVMFRInputPayload neutral = {};
|
||||
neutral.mouseAbsolute.x = m_absoluteX;
|
||||
neutral.mouseAbsolute.y = m_absoluteY;
|
||||
queued = QueueRawLocked(
|
||||
LG_INPUT_PIPE_MESSAGE_MOUSE_ABSOLUTE, neutral, false);
|
||||
}
|
||||
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 :
|
||||
deltaX < INT16_MIN ? INT16_MIN : static_cast<int16_t>(deltaX);
|
||||
const int16_t y = deltaY > INT16_MAX ? INT16_MAX :
|
||||
deltaY < INT16_MIN ? INT16_MIN : static_cast<int16_t>(deltaY);
|
||||
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;
|
||||
if (switching)
|
||||
m_absoluteButtons = 0;
|
||||
m_mouseMode = MouseMode::RELATIVE_INPUT;
|
||||
m_relativeButtons = buttons;
|
||||
}
|
||||
while (deltaX || deltaY || wheel);
|
||||
|
||||
return true;
|
||||
return queued;
|
||||
}
|
||||
|
||||
bool CInputPipeServer::SendMouseAbsolute(
|
||||
@@ -139,170 +272,206 @@ bool CInputPipeServer::SendMouseAbsolute(
|
||||
{
|
||||
if (x > LG_INPUT_MOUSE_ABSOLUTE_MAX ||
|
||||
y > LG_INPUT_MOUSE_ABSOLUTE_MAX ||
|
||||
wheel < MIN_MOUSE_WHEEL || wheel > MAX_MOUSE_WHEEL ||
|
||||
(buttons & ~static_cast<uint32_t>(LG_INPUT_MOUSE_BUTTON_MASK)))
|
||||
wheel < LG_INPUT_MOUSE_WHEEL_MIN_TOTAL ||
|
||||
wheel > LG_INPUT_MOUSE_WHEEL_MAX ||
|
||||
!(m_state.load(std::memory_order_acquire) & 1))
|
||||
return false;
|
||||
|
||||
CSRWExclusiveLock lock(&m_sendLock);
|
||||
if (!IsAvailable())
|
||||
return false;
|
||||
KVMFRInputPayload payload = {};
|
||||
payload.mouseAbsolute.buttons = buttons;
|
||||
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 = {};
|
||||
if (!SendMessageLocked(
|
||||
LG_INPUT_PIPE_MESSAGE_MOUSE_RELATIVE,
|
||||
&neutral,
|
||||
sizeof(neutral)))
|
||||
{
|
||||
Invalidate();
|
||||
return false;
|
||||
}
|
||||
m_mouseMode = MouseMode::NONE;
|
||||
const KVMFRInputPayload neutral = {};
|
||||
queued = QueueRawLocked(
|
||||
LG_INPUT_PIPE_MESSAGE_MOUSE_RELATIVE, neutral, false);
|
||||
}
|
||||
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 :
|
||||
wheel < LG_INPUT_MOUSE_WHEEL_MIN ? LG_INPUT_MOUSE_WHEEL_MIN :
|
||||
static_cast<int8_t>(wheel);
|
||||
const LGInputPipeMouseAbsolute payload = {
|
||||
static_cast<uint8_t>(buttons),
|
||||
x,
|
||||
y,
|
||||
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;
|
||||
if (switching)
|
||||
m_relativeButtons = 0;
|
||||
m_mouseMode = MouseMode::ABSOLUTE_INPUT;
|
||||
m_absoluteValid = true;
|
||||
m_absoluteX = x;
|
||||
m_absoluteY = y;
|
||||
m_absoluteButtons = buttons;
|
||||
}
|
||||
while (wheel);
|
||||
|
||||
return true;
|
||||
return queued;
|
||||
}
|
||||
|
||||
bool CInputPipeServer::SendKeyboard(
|
||||
uint8_t modifiers,
|
||||
const uint8_t * keys)
|
||||
{
|
||||
if (!keys)
|
||||
if (!keys || !(m_state.load(std::memory_order_acquire) & 1))
|
||||
return false;
|
||||
|
||||
LGInputPipeKeyboard payload = {};
|
||||
payload.modifiers = modifiers;
|
||||
KVMFRInputPayload payload = {};
|
||||
payload.keyboard.modifiers = modifiers;
|
||||
for (size_t i = 0; i < LG_INPUT_KEYBOARD_KEY_COUNT; ++i)
|
||||
{
|
||||
if (keys[i] > LG_INPUT_KEYBOARD_USAGE_MAX)
|
||||
return false;
|
||||
payload.keys[i] = keys[i];
|
||||
payload.keyboard.keys[i] = keys[i];
|
||||
}
|
||||
|
||||
CSRWExclusiveLock lock(&m_sendLock);
|
||||
const bool sent = IsAvailable() &&
|
||||
SendMessageLocked(
|
||||
LG_INPUT_PIPE_MESSAGE_KEYBOARD,
|
||||
&payload,
|
||||
sizeof(payload));
|
||||
if (!sent)
|
||||
Invalidate();
|
||||
return sent;
|
||||
CSRWExclusiveLock lock(&m_queueLock);
|
||||
const bool queued = (m_state.load(std::memory_order_relaxed) & 1) &&
|
||||
QueueLocked(LG_INPUT_PIPE_MESSAGE_KEYBOARD, payload, false);
|
||||
if (!queued)
|
||||
ResyncLocked();
|
||||
return queued;
|
||||
}
|
||||
|
||||
bool CInputPipeServer::Reset()
|
||||
{
|
||||
CSRWExclusiveLock lock(&m_sendLock);
|
||||
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)))
|
||||
if (!(m_state.load(std::memory_order_acquire) & 1))
|
||||
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;
|
||||
}
|
||||
|
||||
bool CInputPipeServer::SendMessageLocked(
|
||||
LGInputPipeMessageType type,
|
||||
const void * payload,
|
||||
size_t size)
|
||||
bool CInputPipeServer::Send(const QueueItem& item)
|
||||
{
|
||||
if (!payload || !size || size > LG_INPUT_PIPE_MAX_PAYLOAD_SIZE)
|
||||
return false;
|
||||
|
||||
LGInputPipeMessage message = {};
|
||||
message.magic = LG_INPUT_PIPE_MAGIC;
|
||||
message.version = LG_INPUT_PIPE_VERSION;
|
||||
message.type = type;
|
||||
message.payloadSize = static_cast<uint32_t>(size);
|
||||
memcpy(message.payload, payload, size);
|
||||
message.magic = LG_INPUT_PIPE_MAGIC;
|
||||
message.version = LG_INPUT_PIPE_VERSION;
|
||||
message.type = item.type;
|
||||
message.payloadSize = sizeof(KVMFRInputPayload);
|
||||
memcpy(message.payload, &item.payload, sizeof(item.payload));
|
||||
|
||||
message.sequence = ++m_sequence;
|
||||
const bool sent = m_endpoint.Send(&message, sizeof(message));
|
||||
return sent;
|
||||
bool current;
|
||||
bool sent = true;
|
||||
{
|
||||
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);
|
||||
while ((state & 1) && !m_state.compare_exchange_weak(
|
||||
state, state + 1, std::memory_order_acq_rel))
|
||||
CSRWExclusiveLock connectionLock(&m_connectionLock);
|
||||
uint64_t current = m_state.load(std::memory_order_relaxed);
|
||||
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()
|
||||
{
|
||||
Invalidate();
|
||||
CSRWExclusiveLock connectionLock(&m_connectionLock);
|
||||
CSRWExclusiveLock queueLock(&m_queueLock);
|
||||
|
||||
CSRWExclusiveLock lock(&m_sendLock);
|
||||
const bool ready = ResetLocked();
|
||||
if (!ready)
|
||||
uint64_t state = m_state.load(std::memory_order_relaxed);
|
||||
if (state & 1)
|
||||
++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");
|
||||
return;
|
||||
const size_t index = (m_queueHead + i) % QUEUE_LENGTH;
|
||||
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()
|
||||
{
|
||||
Invalidate();
|
||||
Invalidate(0, false);
|
||||
}
|
||||
|
||||
bool CInputPipeServer::OnPipeMessage(
|
||||
|
||||
@@ -30,67 +30,90 @@
|
||||
|
||||
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:
|
||||
~CInputPipeServer() { DeInit(); }
|
||||
|
||||
bool Init();
|
||||
void DeInit();
|
||||
|
||||
bool IsAvailable() const override
|
||||
{
|
||||
return (m_state.load(std::memory_order_acquire) & 1) != 0;
|
||||
}
|
||||
uint64_t GetGeneration() const override
|
||||
uint64_t GetState() const override
|
||||
{
|
||||
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(
|
||||
_In_ int32_t deltaX,
|
||||
_In_ int32_t deltaY,
|
||||
_In_ int32_t wheel,
|
||||
_In_ uint32_t buttons) override;
|
||||
// Absolute coordinates are normalized to 0..32767 on each axis.
|
||||
bool SendMouseAbsolute(
|
||||
_In_range_(0, LG_INPUT_MOUSE_ABSOLUTE_MAX) uint16_t x,
|
||||
_In_range_(0, LG_INPUT_MOUSE_ABSOLUTE_MAX) uint16_t y,
|
||||
_In_ int32_t wheel,
|
||||
_In_ uint32_t buttons) override;
|
||||
// Keys are USB HID Keyboard/Keypad usage IDs; zero marks an empty slot.
|
||||
bool SendKeyboard(
|
||||
_In_ uint8_t modifiers,
|
||||
_In_reads_(LG_INPUT_KEYBOARD_KEY_COUNT) const uint8_t * keys) 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;
|
||||
|
||||
@@ -51,6 +51,7 @@ static constexpr int32_t MAX_MOUSE_WHEEL =
|
||||
INT8_MAX * MAX_SPLIT_REPORTS;
|
||||
static constexpr int32_t MIN_MOUSE_WHEEL =
|
||||
-INT8_MAX * MAX_SPLIT_REPORTS;
|
||||
static constexpr DWORD WAIT_FIRST_OBJECT_VALUE = 0;
|
||||
|
||||
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);
|
||||
if (state == WAIT_TIMEOUT)
|
||||
return true;
|
||||
if (state != WAIT_OBJECT_0)
|
||||
if (state != WAIT_FIRST_OBJECT_VALUE)
|
||||
{
|
||||
DEBUG_ERROR_HR(GetLastError(),
|
||||
"Failed to inspect LGMP input worker");
|
||||
@@ -144,13 +145,13 @@ bool CLGMPInputTransport::Start(IInputSink& sink)
|
||||
}
|
||||
|
||||
m_sink = &sink;
|
||||
m_sinkGeneration = sink.GetGeneration();
|
||||
m_sinkState = sink.GetState();
|
||||
m_thread = CreateThread(nullptr, 0, ThreadProc, this, 0, nullptr);
|
||||
if (!m_thread)
|
||||
{
|
||||
DEBUG_ERROR_HR(GetLastError(), "Failed to create LGMP input worker");
|
||||
m_sink = nullptr;
|
||||
m_sinkGeneration = 0;
|
||||
m_sinkState = 0;
|
||||
CloseHandle(m_pollTimer);
|
||||
CloseHandle(m_stopEvent);
|
||||
m_pollTimer = nullptr;
|
||||
@@ -163,18 +164,13 @@ bool CLGMPInputTransport::Start(IInputSink& sink)
|
||||
|
||||
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);
|
||||
|
||||
if (m_stopEvent)
|
||||
SetEvent(m_stopEvent);
|
||||
if (m_thread)
|
||||
WaitForSingleObject(m_thread, INFINITE);
|
||||
|
||||
if (m_thread)
|
||||
{
|
||||
CloseHandle(m_thread);
|
||||
@@ -195,7 +191,7 @@ void CLGMPInputTransport::Stop()
|
||||
m_ownerGeneration = 0;
|
||||
m_ownerSequence = 0;
|
||||
m_ownerDeadline = 0;
|
||||
m_sinkGeneration = 0;
|
||||
m_sinkState = 0;
|
||||
}
|
||||
|
||||
bool CLGMPInputTransport::IsOwner(
|
||||
@@ -208,13 +204,12 @@ bool CLGMPInputTransport::IsOwner(
|
||||
bool CLGMPInputTransport::Claim(
|
||||
uint32_t sourceClientID, const KVMFRInputMessage& message)
|
||||
{
|
||||
if (message.sequence != 1 || !m_sink || !m_sink->IsAvailable())
|
||||
if (message.sequence != 1 || !m_sink)
|
||||
return false;
|
||||
|
||||
const uint64_t sinkGeneration = m_sink->GetGeneration();
|
||||
if (sinkGeneration != m_sinkGeneration || !m_sink->Reset() ||
|
||||
!m_sink->IsAvailable() ||
|
||||
m_sink->GetGeneration() != sinkGeneration)
|
||||
const uint64_t sinkState = m_sink->GetState();
|
||||
if (!(sinkState & 1) || sinkState != m_sinkState ||
|
||||
!m_sink->Reset() || m_sink->GetState() != sinkState)
|
||||
return false;
|
||||
|
||||
m_ownerClientID = sourceClientID;
|
||||
@@ -255,10 +250,10 @@ void CLGMPInputTransport::CheckOwner()
|
||||
if (!m_sink)
|
||||
return;
|
||||
|
||||
const uint64_t generation = m_sink->GetGeneration();
|
||||
if (generation != m_sinkGeneration)
|
||||
const uint64_t state = m_sink->GetState();
|
||||
if (state != m_sinkState)
|
||||
{
|
||||
m_sinkGeneration = generation;
|
||||
m_sinkState = state;
|
||||
ReleaseOwner(true, "input endpoint changed");
|
||||
return;
|
||||
}
|
||||
@@ -266,7 +261,7 @@ void CLGMPInputTransport::CheckOwner()
|
||||
if (!m_ownerClientID)
|
||||
return;
|
||||
|
||||
if (!m_sink->IsAvailable())
|
||||
if (!(state & 1))
|
||||
{
|
||||
ReleaseOwner(true, "input unavailable");
|
||||
return;
|
||||
@@ -323,11 +318,11 @@ bool CLGMPInputTransport::ProcessMessage(
|
||||
uint32_t sourceClientID, const KVMFRInputMessage& message)
|
||||
{
|
||||
const bool owner = IsOwner(sourceClientID, message.generation);
|
||||
const uint64_t sinkGeneration = m_sink ?
|
||||
m_sink->GetGeneration() : m_sinkGeneration;
|
||||
if (sinkGeneration != m_sinkGeneration)
|
||||
const uint64_t sinkState = m_sink ?
|
||||
m_sink->GetState() : m_sinkState;
|
||||
if (sinkState != m_sinkState)
|
||||
{
|
||||
m_sinkGeneration = sinkGeneration;
|
||||
m_sinkState = sinkState;
|
||||
if (m_ownerClientID)
|
||||
{
|
||||
ReleaseOwner(true, "input endpoint changed");
|
||||
@@ -417,10 +412,10 @@ bool CLGMPInputTransport::ProcessMessage(
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint64_t deliveredGeneration = m_sink->GetGeneration();
|
||||
if (deliveredGeneration != m_sinkGeneration)
|
||||
const uint64_t deliveredState = m_sink->GetState();
|
||||
if (deliveredState != m_sinkState)
|
||||
{
|
||||
m_sinkGeneration = deliveredGeneration;
|
||||
m_sinkState = deliveredState;
|
||||
ReleaseOwner(true, "input endpoint changed");
|
||||
return false;
|
||||
}
|
||||
@@ -503,9 +498,9 @@ void CLGMPInputTransport::Thread()
|
||||
|
||||
const DWORD wait = WaitForMultipleObjects(
|
||||
_countof(waitHandles), waitHandles, FALSE, INFINITE);
|
||||
if (wait == WAIT_OBJECT_0)
|
||||
if (wait == WAIT_FIRST_OBJECT_VALUE)
|
||||
break;
|
||||
if (wait != WAIT_OBJECT_0 + 1)
|
||||
if (wait != WAIT_FIRST_OBJECT_VALUE + 1)
|
||||
{
|
||||
DEBUG_ERROR_HR(GetLastError(), "LGMP input worker wait failed");
|
||||
break;
|
||||
|
||||
@@ -54,7 +54,7 @@ private:
|
||||
uint32_t m_ownerGeneration = 0;
|
||||
uint32_t m_ownerSequence = 0;
|
||||
ULONGLONG m_ownerDeadline = 0;
|
||||
uint64_t m_sinkGeneration = 0;
|
||||
uint64_t m_sinkState = 0;
|
||||
|
||||
bool Initialize();
|
||||
void DeInit();
|
||||
|
||||
@@ -26,16 +26,21 @@
|
||||
#include "ipc/CInputPipeClient.h"
|
||||
|
||||
#include <hidport.h>
|
||||
#include <limits.h>
|
||||
#include <new>
|
||||
#include <string.h>
|
||||
|
||||
static constexpr USHORT LG_INPUT_VENDOR_ID = 0x0000;
|
||||
static constexpr USHORT LG_INPUT_PRODUCT_ID = 0x0000;
|
||||
static constexpr USHORT LG_INPUT_VERSION = 0x0001;
|
||||
static constexpr size_t REPORT_QUEUE_LENGTH = 64;
|
||||
static constexpr USHORT LG_INPUT_VENDOR_ID = 0x0000;
|
||||
static constexpr USHORT LG_INPUT_PRODUCT_ID = 0x0000;
|
||||
static constexpr USHORT LG_INPUT_VERSION = 0x0001;
|
||||
static constexpr size_t REPORT_QUEUE_LENGTH = 64;
|
||||
static constexpr size_t MOTION_COALESCE_THRESHOLD =
|
||||
REPORT_QUEUE_LENGTH / 2;
|
||||
|
||||
struct HIDQueuedReport
|
||||
{
|
||||
UCHAR data[sizeof(HIDKeyboardReport)];
|
||||
UCHAR data[HID_MAX_INPUT_REPORT_SIZE];
|
||||
bool pureMotion;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
@@ -49,8 +54,14 @@ struct HIDDeviceContext
|
||||
SRWLOCK reportLock;
|
||||
bool active;
|
||||
bool stopping;
|
||||
UCHAR mouseMode;
|
||||
size_t reportHead;
|
||||
size_t reportCount;
|
||||
uint32_t relativeButtons;
|
||||
uint32_t absoluteButtons;
|
||||
bool absoluteValid;
|
||||
uint16_t absoluteX;
|
||||
uint16_t absoluteY;
|
||||
HIDQueuedReport reports[REPORT_QUEUE_LENGTH];
|
||||
CInputPipeClient * inputPipe;
|
||||
};
|
||||
@@ -151,19 +162,114 @@ static void PopReport(
|
||||
--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(
|
||||
_Inout_ HIDDeviceContext * context,
|
||||
_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)
|
||||
return STATUS_BUFFER_OVERFLOW;
|
||||
{
|
||||
if (!CompactStaleMotion(context, reportId))
|
||||
return STATUS_BUFFER_OVERFLOW;
|
||||
}
|
||||
|
||||
const size_t index =
|
||||
(context->reportHead + context->reportCount) % REPORT_QUEUE_LENGTH;
|
||||
HIDQueuedReport * report = &context->reports[index];
|
||||
CopyMemory(report->data, data, size);
|
||||
report->size = size;
|
||||
report->size = size;
|
||||
report->pureMotion = pureMotion;
|
||||
++context->reportCount;
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
@@ -343,38 +449,136 @@ NTSTATUS CHIDDevice::SubmitReport(
|
||||
status = STATUS_DEVICE_NOT_READY;
|
||||
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);
|
||||
if (status == STATUS_NO_MORE_ENTRIES)
|
||||
{
|
||||
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)
|
||||
{
|
||||
status = CopyToRequest(request, report, size);
|
||||
WdfRequestComplete(request, status);
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
NTSTATUS CHIDDevice::ClearReports()
|
||||
NTSTATUS CHIDDevice::ResetReports()
|
||||
{
|
||||
CSRWSharedLock deviceLock(&s_deviceLock);
|
||||
HIDDeviceContext * context = s_device;
|
||||
if (!context)
|
||||
return STATUS_DEVICE_NOT_READY;
|
||||
uint16_t absoluteX = 0;
|
||||
uint16_t absoluteY = 0;
|
||||
bool absoluteValid = false;
|
||||
{
|
||||
CSRWSharedLock deviceLock(&s_deviceLock);
|
||||
HIDDeviceContext * context = s_device;
|
||||
if (!context)
|
||||
return STATUS_DEVICE_NOT_READY;
|
||||
|
||||
CSRWExclusiveLock reportLock(&context->reportLock);
|
||||
if (context->stopping)
|
||||
return STATUS_DEVICE_NOT_READY;
|
||||
CSRWExclusiveLock reportLock(&context->reportLock);
|
||||
if (context->stopping)
|
||||
return STATUS_DEVICE_NOT_READY;
|
||||
|
||||
context->reportHead = 0;
|
||||
context->reportCount = 0;
|
||||
return STATUS_SUCCESS;
|
||||
absoluteValid = context->absoluteValid;
|
||||
absoluteX = context->absoluteX;
|
||||
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(
|
||||
|
||||
@@ -30,5 +30,5 @@ public:
|
||||
static NTSTATUS SubmitReport(
|
||||
_In_reads_bytes_(size) const void * report,
|
||||
_In_ size_t size);
|
||||
static NTSTATUS ClearReports();
|
||||
static NTSTATUS ResetReports();
|
||||
};
|
||||
|
||||
@@ -31,15 +31,12 @@ static const uint8_t REPORT_DESCRIPTOR[] =
|
||||
0xA1, 0x00, // Collection (Physical)
|
||||
0x05, 0x09, // Usage Page (Button)
|
||||
0x19, 0x01, // Usage Minimum (Button 1)
|
||||
0x29, 0x05, // Usage Maximum (Button 5)
|
||||
0x29, 0x20, // Usage Maximum (Button 32)
|
||||
0x15, 0x00, // Logical Minimum (0)
|
||||
0x25, 0x01, // Logical Maximum (1)
|
||||
0x75, 0x01, // Report Size (1)
|
||||
0x95, 0x05, // Report Count (5)
|
||||
0x95, 0x20, // Report Count (32)
|
||||
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)
|
||||
0x09, 0x30, // Usage (X)
|
||||
0x09, 0x31, // Usage (Y)
|
||||
@@ -66,15 +63,12 @@ static const uint8_t REPORT_DESCRIPTOR[] =
|
||||
0xA1, 0x00, // Collection (Physical)
|
||||
0x05, 0x09, // Usage Page (Button)
|
||||
0x19, 0x01, // Usage Minimum (Button 1)
|
||||
0x29, 0x05, // Usage Maximum (Button 5)
|
||||
0x29, 0x20, // Usage Maximum (Button 32)
|
||||
0x15, 0x00, // Logical Minimum (0)
|
||||
0x25, 0x01, // Logical Maximum (1)
|
||||
0x75, 0x01, // Report Size (1)
|
||||
0x95, 0x05, // Report Count (5)
|
||||
0x95, 0x20, // Report Count (32)
|
||||
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)
|
||||
0x09, 0x30, // Usage (X)
|
||||
0x09, 0x31, // Usage (Y)
|
||||
|
||||
@@ -35,7 +35,7 @@ enum HIDReportId : uint8_t
|
||||
struct HIDMouseAbsoluteReport
|
||||
{
|
||||
uint8_t reportId;
|
||||
uint8_t buttons;
|
||||
uint32_t buttons;
|
||||
uint16_t x;
|
||||
uint16_t y;
|
||||
int8_t wheel;
|
||||
@@ -44,7 +44,7 @@ struct HIDMouseAbsoluteReport
|
||||
struct HIDMouseRelativeReport
|
||||
{
|
||||
uint8_t reportId;
|
||||
uint8_t buttons;
|
||||
uint32_t buttons;
|
||||
int16_t x;
|
||||
int16_t y;
|
||||
int8_t wheel;
|
||||
@@ -66,11 +66,14 @@ struct HIDKeyboardLedsReport
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
static_assert(sizeof(HIDMouseAbsoluteReport) == 7);
|
||||
static_assert(sizeof(HIDMouseRelativeReport) == 7);
|
||||
static_assert(sizeof(HIDMouseAbsoluteReport) == 10);
|
||||
static_assert(sizeof(HIDMouseRelativeReport) == 10);
|
||||
static_assert(sizeof(HIDKeyboardReport) == 9);
|
||||
static_assert(sizeof(HIDKeyboardLedsReport) == 2);
|
||||
|
||||
static constexpr size_t HID_MAX_INPUT_REPORT_SIZE =
|
||||
sizeof(HIDMouseAbsoluteReport);
|
||||
|
||||
const uint8_t * HIDGetReportDescriptor();
|
||||
size_t HIDGetReportDescriptorSize();
|
||||
size_t HIDGetInputReportSize(uint8_t reportId);
|
||||
|
||||
@@ -80,7 +80,7 @@
|
||||
<WppRecorderEnabled>true</WppRecorderEnabled>
|
||||
<WppScanConfigurationData Condition="'%(ClCompile.ScanConfigurationData)' == ''">Trace.h</WppScanConfigurationData>
|
||||
<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>
|
||||
<Link>
|
||||
<AdditionalDependencies>%(AdditionalDependencies);OneCoreUAP.lib</AdditionalDependencies>
|
||||
|
||||
@@ -43,7 +43,7 @@ void CInputPipeClient::Stop()
|
||||
m_endpoint.Stop();
|
||||
m_lastSequence = 0;
|
||||
if (wasRunning)
|
||||
CHIDDevice::ClearReports();
|
||||
CHIDDevice::ResetReports();
|
||||
}
|
||||
|
||||
void CInputPipeClient::OnPipeConnected()
|
||||
@@ -55,7 +55,7 @@ void CInputPipeClient::OnPipeConnected()
|
||||
void CInputPipeClient::OnPipeDisconnected()
|
||||
{
|
||||
m_lastSequence = 0;
|
||||
CHIDDevice::ClearReports();
|
||||
CHIDDevice::ResetReports();
|
||||
DEBUG_INFO("Disconnected from the LGIdd input transport; reconnecting");
|
||||
}
|
||||
|
||||
@@ -78,6 +78,7 @@ bool CInputPipeClient::OnPipeMessage(
|
||||
(m_lastSequence && message.sequence != m_lastSequence + 1))
|
||||
{
|
||||
DEBUG_WARN("LGInput pipe report sequence changed unexpectedly");
|
||||
CHIDDevice::ResetReports();
|
||||
return false;
|
||||
}
|
||||
bool handled = false;
|
||||
@@ -117,18 +118,41 @@ bool CInputPipeClient::HandleMouseRelative(
|
||||
|
||||
LGInputPipeMouseRelative input = {};
|
||||
memcpy(&input, payload, sizeof(input));
|
||||
if (input.wheel < LG_INPUT_MOUSE_WHEEL_MIN ||
|
||||
(input.buttons & ~LG_INPUT_MOUSE_BUTTON_MASK))
|
||||
if (input.deltaX < LG_INPUT_MOUSE_DELTA_MIN ||
|
||||
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;
|
||||
|
||||
const HIDMouseRelativeReport report = {
|
||||
HID_REPORT_ID_MOUSE_RELATIVE,
|
||||
input.buttons,
|
||||
input.deltaX,
|
||||
input.deltaY,
|
||||
input.wheel,
|
||||
};
|
||||
return SubmitReport(&report, sizeof(report));
|
||||
int32_t x = input.deltaX;
|
||||
int32_t y = input.deltaY;
|
||||
int32_t wheel = input.wheel;
|
||||
do
|
||||
{
|
||||
const int16_t reportX = x > INT16_MAX ? INT16_MAX :
|
||||
x < INT16_MIN ? INT16_MIN : static_cast<int16_t>(x);
|
||||
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(
|
||||
@@ -142,18 +166,30 @@ bool CInputPipeClient::HandleMouseAbsolute(
|
||||
memcpy(&input, payload, sizeof(input));
|
||||
if (input.x > LG_INPUT_MOUSE_ABSOLUTE_MAX ||
|
||||
input.y > LG_INPUT_MOUSE_ABSOLUTE_MAX ||
|
||||
input.wheel < LG_INPUT_MOUSE_WHEEL_MIN ||
|
||||
(input.buttons & ~LG_INPUT_MOUSE_BUTTON_MASK))
|
||||
input.wheel < LG_INPUT_MOUSE_WHEEL_MIN_TOTAL ||
|
||||
input.wheel > LG_INPUT_MOUSE_WHEEL_MAX ||
|
||||
input.reserved)
|
||||
return false;
|
||||
|
||||
const HIDMouseAbsoluteReport report = {
|
||||
HID_REPORT_ID_MOUSE_ABSOLUTE,
|
||||
input.buttons,
|
||||
input.x,
|
||||
input.y,
|
||||
input.wheel,
|
||||
};
|
||||
return SubmitReport(&report, sizeof(report));
|
||||
int32_t wheel = input.wheel;
|
||||
do
|
||||
{
|
||||
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 HIDMouseAbsoluteReport 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(
|
||||
@@ -166,6 +202,10 @@ bool CInputPipeClient::HandleKeyboard(
|
||||
LGInputPipeKeyboard input = {};
|
||||
memcpy(&input, payload, sizeof(input));
|
||||
|
||||
for (size_t i = 0; i < sizeof(input.reserved); ++i)
|
||||
if (input.reserved[i])
|
||||
return false;
|
||||
|
||||
HIDKeyboardReport report = {};
|
||||
report.reportId = HID_REPORT_ID_KEYBOARD;
|
||||
report.modifiers = input.modifiers;
|
||||
@@ -189,8 +229,16 @@ bool CInputPipeClient::SubmitReport(
|
||||
return false;
|
||||
|
||||
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_HR(status, "Failed to submit an LGInput HID report");
|
||||
{
|
||||
DEBUG_WARN("LGInput HID report queue overflowed; resetting input state");
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -32,7 +32,6 @@ public:
|
||||
|
||||
bool Start();
|
||||
void Stop();
|
||||
bool IsConnected() const { return m_endpoint.IsConnected(); }
|
||||
|
||||
private:
|
||||
void OnPipeConnected() override;
|
||||
|
||||
Reference in New Issue
Block a user