[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:
Geoffrey McRae
2026-08-08 23:31:44 +10:00
parent 83c552fb9d
commit ced2fb531e
16 changed files with 820 additions and 382 deletions

View File

@@ -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;