mirror of
https://github.com/gnif/LookingGlass.git
synced 2026-08-09 08:41:31 +00:00
[idd] input: receive LGMP input reports
Add an optional input transport and a dedicated LGMP receiver that validates source ownership, generations, ordered sequences, and leases. Poll input outside the 10 ms control timer. Forward mouse and keyboard state through the LGInput pipe, and neutralize the endpoint before ownership changes or transport failures can leave input held.
This commit is contained in:
@@ -21,17 +21,34 @@
|
||||
#include "ipc/CInputPipeServer.h"
|
||||
|
||||
#include "CDebug.h"
|
||||
#include "CSRWLock.h"
|
||||
#include "InputPipeProtocol.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
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;
|
||||
|
||||
bool CInputPipeServer::Init()
|
||||
{
|
||||
AcquireSRWLockExclusive(&m_sendLock);
|
||||
m_sequence = 0;
|
||||
ReleaseSRWLockExclusive(&m_sendLock);
|
||||
m_state.store(0, std::memory_order_release);
|
||||
{
|
||||
CSRWExclusiveLock lock(&m_sendLock);
|
||||
m_sequence = 0;
|
||||
m_mouseMode = MouseMode::NONE;
|
||||
m_absoluteValid = false;
|
||||
m_absoluteX = 0;
|
||||
m_absoluteY = 0;
|
||||
}
|
||||
|
||||
m_endpoint.SetHandler(this);
|
||||
return m_endpoint.Start(
|
||||
@@ -42,53 +59,134 @@ bool CInputPipeServer::Init()
|
||||
|
||||
void CInputPipeServer::DeInit()
|
||||
{
|
||||
Invalidate();
|
||||
m_endpoint.Stop();
|
||||
}
|
||||
|
||||
bool CInputPipeServer::SendMouseRelative(
|
||||
int16_t deltaX,
|
||||
int16_t deltaY,
|
||||
int8_t wheel,
|
||||
uint8_t buttons)
|
||||
int32_t deltaX,
|
||||
int32_t deltaY,
|
||||
int32_t wheel,
|
||||
uint32_t buttons)
|
||||
{
|
||||
if (wheel < LG_INPUT_MOUSE_WHEEL_MIN ||
|
||||
(buttons & ~LG_INPUT_MOUSE_BUTTON_MASK))
|
||||
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)))
|
||||
return false;
|
||||
|
||||
const LGInputPipeMouseRelative payload = {
|
||||
buttons,
|
||||
deltaX,
|
||||
deltaY,
|
||||
wheel,
|
||||
};
|
||||
return SendMessage(
|
||||
LG_INPUT_PIPE_MESSAGE_MOUSE_RELATIVE,
|
||||
&payload,
|
||||
sizeof(payload));
|
||||
CSRWExclusiveLock lock(&m_sendLock);
|
||||
if (!IsAvailable())
|
||||
return false;
|
||||
|
||||
if (m_mouseMode == MouseMode::ABSOLUTE)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
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;
|
||||
}
|
||||
while (deltaX || deltaY || wheel);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CInputPipeServer::SendMouseAbsolute(
|
||||
uint16_t x,
|
||||
uint16_t y,
|
||||
int8_t wheel,
|
||||
uint8_t buttons)
|
||||
int32_t wheel,
|
||||
uint32_t buttons)
|
||||
{
|
||||
if (x > LG_INPUT_MOUSE_ABSOLUTE_MAX ||
|
||||
y > LG_INPUT_MOUSE_ABSOLUTE_MAX ||
|
||||
wheel < LG_INPUT_MOUSE_WHEEL_MIN ||
|
||||
(buttons & ~LG_INPUT_MOUSE_BUTTON_MASK))
|
||||
wheel < MIN_MOUSE_WHEEL || wheel > MAX_MOUSE_WHEEL ||
|
||||
(buttons & ~static_cast<uint32_t>(LG_INPUT_MOUSE_BUTTON_MASK)))
|
||||
return false;
|
||||
|
||||
const LGInputPipeMouseAbsolute payload = {
|
||||
buttons,
|
||||
x,
|
||||
y,
|
||||
wheel,
|
||||
};
|
||||
return SendMessage(
|
||||
LG_INPUT_PIPE_MESSAGE_MOUSE_ABSOLUTE,
|
||||
&payload,
|
||||
sizeof(payload));
|
||||
CSRWExclusiveLock lock(&m_sendLock);
|
||||
if (!IsAvailable())
|
||||
return false;
|
||||
|
||||
if (m_mouseMode == MouseMode::RELATIVE)
|
||||
{
|
||||
const LGInputPipeMouseRelative neutral = {};
|
||||
if (!SendMessageLocked(
|
||||
LG_INPUT_PIPE_MESSAGE_MOUSE_RELATIVE,
|
||||
&neutral,
|
||||
sizeof(neutral)))
|
||||
{
|
||||
Invalidate();
|
||||
return false;
|
||||
}
|
||||
m_mouseMode = MouseMode::NONE;
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
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;
|
||||
}
|
||||
while (wheel);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CInputPipeServer::SendKeyboard(
|
||||
@@ -107,13 +205,58 @@ bool CInputPipeServer::SendKeyboard(
|
||||
payload.keys[i] = keys[i];
|
||||
}
|
||||
|
||||
return SendMessage(
|
||||
LG_INPUT_PIPE_MESSAGE_KEYBOARD,
|
||||
&payload,
|
||||
sizeof(payload));
|
||||
CSRWExclusiveLock lock(&m_sendLock);
|
||||
const bool sent = IsAvailable() &&
|
||||
SendMessageLocked(
|
||||
LG_INPUT_PIPE_MESSAGE_KEYBOARD,
|
||||
&payload,
|
||||
sizeof(payload));
|
||||
if (!sent)
|
||||
Invalidate();
|
||||
return sent;
|
||||
}
|
||||
|
||||
bool CInputPipeServer::SendMessage(
|
||||
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)))
|
||||
return false;
|
||||
|
||||
m_mouseMode = MouseMode::NONE;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CInputPipeServer::SendMessageLocked(
|
||||
LGInputPipeMessageType type,
|
||||
const void * payload,
|
||||
size_t size)
|
||||
@@ -128,13 +271,40 @@ bool CInputPipeServer::SendMessage(
|
||||
message.payloadSize = static_cast<uint32_t>(size);
|
||||
memcpy(message.payload, payload, size);
|
||||
|
||||
AcquireSRWLockExclusive(&m_sendLock);
|
||||
message.sequence = ++m_sequence;
|
||||
const bool sent = m_endpoint.Send(&message, sizeof(message));
|
||||
ReleaseSRWLockExclusive(&m_sendLock);
|
||||
return sent;
|
||||
}
|
||||
|
||||
void CInputPipeServer::Invalidate()
|
||||
{
|
||||
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))
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
void CInputPipeServer::OnPipeConnected()
|
||||
{
|
||||
Invalidate();
|
||||
|
||||
CSRWExclusiveLock lock(&m_sendLock);
|
||||
const bool ready = ResetLocked();
|
||||
if (!ready)
|
||||
{
|
||||
DEBUG_WARN("Failed to neutralize the LGInput endpoint");
|
||||
return;
|
||||
}
|
||||
|
||||
m_state.fetch_add(1, std::memory_order_acq_rel);
|
||||
}
|
||||
|
||||
void CInputPipeServer::OnPipeDisconnected()
|
||||
{
|
||||
Invalidate();
|
||||
}
|
||||
|
||||
bool CInputPipeServer::OnPipeMessage(
|
||||
const void * message,
|
||||
size_t size)
|
||||
|
||||
@@ -22,11 +22,13 @@
|
||||
|
||||
#include "CPipeEndpoint.h"
|
||||
#include "InputPipeProtocol.h"
|
||||
#include "input/IInputSink.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
class CInputPipeServer : private IPipeEndpointHandler
|
||||
class CInputPipeServer : public IInputSink, private IPipeEndpointHandler
|
||||
{
|
||||
public:
|
||||
~CInputPipeServer() { DeInit(); }
|
||||
@@ -34,34 +36,61 @@ public:
|
||||
bool Init();
|
||||
void DeInit();
|
||||
|
||||
// Mouse buttons use LGInputMouseButton bits. Wheel values are -127..127.
|
||||
bool IsAvailable() 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);
|
||||
}
|
||||
|
||||
// Mouse buttons use LGInputMouseButton bits. Values outside the pipe report
|
||||
// ranges are split into multiple reports without losing motion.
|
||||
bool SendMouseRelative(
|
||||
_In_ int16_t deltaX,
|
||||
_In_ int16_t deltaY,
|
||||
_In_ int8_t wheel,
|
||||
_In_ uint8_t buttons);
|
||||
_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_ int8_t wheel,
|
||||
_In_ uint8_t buttons);
|
||||
_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);
|
||||
_In_reads_(LG_INPUT_KEYBOARD_KEY_COUNT) const uint8_t * keys) override;
|
||||
bool Reset() override;
|
||||
bool IsConnected() const { return m_endpoint.IsConnected(); }
|
||||
|
||||
private:
|
||||
bool SendMessage(
|
||||
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;
|
||||
SRWLOCK m_sendLock = SRWLOCK_INIT;
|
||||
uint64_t m_sequence = 0;
|
||||
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;
|
||||
|
||||
Reference in New Issue
Block a user