diff --git a/idd/LGCommon/InputPipeProtocol.h b/idd/LGCommon/InputPipeProtocol.h index 869dc137..1d8ce664 100644 --- a/idd/LGCommon/InputPipeProtocol.h +++ b/idd/LGCommon/InputPipeProtocol.h @@ -27,15 +27,65 @@ 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 = 1; -static constexpr size_t LG_INPUT_PIPE_MAX_REPORT_SIZE = 64; +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; + +enum LGInputMouseButton : uint8_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_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_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, +}; enum LGInputPipeMessageType : uint16_t { - LG_INPUT_PIPE_MESSAGE_REPORT = 1, + LG_INPUT_PIPE_MESSAGE_MOUSE_ABSOLUTE = 1, + LG_INPUT_PIPE_MESSAGE_MOUSE_RELATIVE = 2, + 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; @@ -43,9 +93,15 @@ struct LGInputPipeMessage uint16_t type; uint32_t payloadSize; uint64_t sequence; - uint8_t payload[LG_INPUT_PIPE_MAX_REPORT_SIZE]; + uint8_t payload[LG_INPUT_PIPE_MAX_PAYLOAD_SIZE]; }; #pragma pack(pop) +static_assert(sizeof(LGInputPipeMouseRelative) == 6, + "LGInputPipeMouseRelative wire layout changed"); +static_assert(sizeof(LGInputPipeMouseAbsolute) == 6, + "LGInputPipeMouseAbsolute wire layout changed"); +static_assert(sizeof(LGInputPipeKeyboard) == 7, + "LGInputPipeKeyboard wire layout changed"); static_assert(sizeof(LGInputPipeMessage) == 84, "LGInputPipeMessage wire layout changed"); diff --git a/idd/LGIdd/ipc/CInputPipeServer.cpp b/idd/LGIdd/ipc/CInputPipeServer.cpp index bcf43fce..41651ca9 100644 --- a/idd/LGIdd/ipc/CInputPipeServer.cpp +++ b/idd/LGIdd/ipc/CInputPipeServer.cpp @@ -45,17 +45,88 @@ void CInputPipeServer::DeInit() m_endpoint.Stop(); } -bool CInputPipeServer::SendReport(const void * report, size_t size) +bool CInputPipeServer::SendMouseRelative( + int16_t deltaX, + int16_t deltaY, + int8_t wheel, + uint8_t buttons) { - if (!report || !size || size > LG_INPUT_PIPE_MAX_REPORT_SIZE) + if (wheel < LG_INPUT_MOUSE_WHEEL_MIN || + (buttons & ~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)); +} + +bool CInputPipeServer::SendMouseAbsolute( + uint16_t x, + uint16_t y, + int8_t wheel, + uint8_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)) + return false; + + const LGInputPipeMouseAbsolute payload = { + buttons, + x, + y, + wheel, + }; + return SendMessage( + LG_INPUT_PIPE_MESSAGE_MOUSE_ABSOLUTE, + &payload, + sizeof(payload)); +} + +bool CInputPipeServer::SendKeyboard( + uint8_t modifiers, + const uint8_t * keys) +{ + if (!keys) + return false; + + LGInputPipeKeyboard payload = {}; + payload.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]; + } + + return SendMessage( + LG_INPUT_PIPE_MESSAGE_KEYBOARD, + &payload, + sizeof(payload)); +} + +bool CInputPipeServer::SendMessage( + LGInputPipeMessageType type, + const void * payload, + size_t size) +{ + 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 = LG_INPUT_PIPE_MESSAGE_REPORT; + message.type = type; message.payloadSize = static_cast(size); - memcpy(message.payload, report, size); + memcpy(message.payload, payload, size); AcquireSRWLockExclusive(&m_sendLock); message.sequence = ++m_sequence; diff --git a/idd/LGIdd/ipc/CInputPipeServer.h b/idd/LGIdd/ipc/CInputPipeServer.h index 21705156..053bcda8 100644 --- a/idd/LGIdd/ipc/CInputPipeServer.h +++ b/idd/LGIdd/ipc/CInputPipeServer.h @@ -21,6 +21,7 @@ #pragma once #include "CPipeEndpoint.h" +#include "InputPipeProtocol.h" #include #include @@ -33,12 +34,29 @@ public: bool Init(); void DeInit(); - bool SendReport( - _In_reads_bytes_(size) const void * report, - _In_ size_t size); + // Mouse buttons use LGInputMouseButton bits. Wheel values are -127..127. + bool SendMouseRelative( + _In_ int16_t deltaX, + _In_ int16_t deltaY, + _In_ int8_t wheel, + _In_ uint8_t buttons); + // 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); + // 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); bool IsConnected() const { return m_endpoint.IsConnected(); } private: + bool SendMessage( + _In_ LGInputPipeMessageType type, + _In_reads_bytes_(size) const void * payload, + _In_ size_t size); bool OnPipeMessage(const void * message, size_t size) override; CPipeEndpoint m_endpoint; diff --git a/idd/LGInput/HIDReports.cpp b/idd/LGInput/HIDReports.cpp index 77507061..824eaf07 100644 --- a/idd/LGInput/HIDReports.cpp +++ b/idd/LGInput/HIDReports.cpp @@ -22,11 +22,11 @@ static const uint8_t REPORT_DESCRIPTOR[] = { - // Absolute tablet, report ID 1. + // Absolute mouse, report ID 1. 0x05, 0x01, // Usage Page (Generic Desktop) 0x09, 0x02, // Usage (Mouse) 0xA1, 0x01, // Collection (Application) - 0x85, HID_REPORT_ID_TABLET, + 0x85, HID_REPORT_ID_MOUSE_ABSOLUTE, 0x09, 0x01, // Usage (Pointer) 0xA1, 0x00, // Collection (Physical) 0x05, 0x09, // Usage Page (Button) @@ -48,6 +48,12 @@ static const uint8_t REPORT_DESCRIPTOR[] = 0x75, 0x10, // Report Size (16) 0x95, 0x02, // Report Count (2) 0x81, 0x02, // Input (Data, Variable, Absolute) + 0x09, 0x38, // Usage (Wheel) + 0x15, 0x81, // Logical Minimum (-127) + 0x25, 0x7F, // Logical Maximum (127) + 0x75, 0x08, // Report Size (8) + 0x95, 0x01, // Report Count (1) + 0x81, 0x06, // Input (Data, Variable, Relative) 0xC0, // End Collection 0xC0, // End Collection @@ -55,7 +61,7 @@ static const uint8_t REPORT_DESCRIPTOR[] = 0x05, 0x01, // Usage Page (Generic Desktop) 0x09, 0x02, // Usage (Mouse) 0xA1, 0x01, // Collection (Application) - 0x85, HID_REPORT_ID_MOUSE, + 0x85, HID_REPORT_ID_MOUSE_RELATIVE, 0x09, 0x01, // Usage (Pointer) 0xA1, 0x00, // Collection (Physical) 0x05, 0x09, // Usage Page (Button) @@ -136,11 +142,11 @@ size_t HIDGetInputReportSize(uint8_t reportId) { switch (reportId) { - case HID_REPORT_ID_TABLET: - return sizeof(HIDTabletReport); + case HID_REPORT_ID_MOUSE_ABSOLUTE: + return sizeof(HIDMouseAbsoluteReport); - case HID_REPORT_ID_MOUSE: - return sizeof(HIDMouseReport); + case HID_REPORT_ID_MOUSE_RELATIVE: + return sizeof(HIDMouseRelativeReport); case HID_REPORT_ID_KEYBOARD: return sizeof(HIDKeyboardReport); diff --git a/idd/LGInput/HIDReports.h b/idd/LGInput/HIDReports.h index 0282ade2..0ec5ae2e 100644 --- a/idd/LGInput/HIDReports.h +++ b/idd/LGInput/HIDReports.h @@ -25,22 +25,23 @@ enum HIDReportId : uint8_t { - HID_REPORT_ID_TABLET = 1, - HID_REPORT_ID_MOUSE = 2, - HID_REPORT_ID_KEYBOARD = 3, + HID_REPORT_ID_MOUSE_ABSOLUTE = 1, + HID_REPORT_ID_MOUSE_RELATIVE = 2, + HID_REPORT_ID_KEYBOARD = 3, }; #pragma pack(push, 1) -struct HIDTabletReport +struct HIDMouseAbsoluteReport { uint8_t reportId; uint8_t buttons; uint16_t x; uint16_t y; + int8_t wheel; }; -struct HIDMouseReport +struct HIDMouseRelativeReport { uint8_t reportId; uint8_t buttons; @@ -65,8 +66,8 @@ struct HIDKeyboardLedsReport #pragma pack(pop) -static_assert(sizeof(HIDTabletReport) == 6); -static_assert(sizeof(HIDMouseReport) == 7); +static_assert(sizeof(HIDMouseAbsoluteReport) == 7); +static_assert(sizeof(HIDMouseRelativeReport) == 7); static_assert(sizeof(HIDKeyboardReport) == 9); static_assert(sizeof(HIDKeyboardLedsReport) == 2); diff --git a/idd/LGInput/ipc/CInputPipeClient.cpp b/idd/LGInput/ipc/CInputPipeClient.cpp index 5af1e058..a829383a 100644 --- a/idd/LGInput/ipc/CInputPipeClient.cpp +++ b/idd/LGInput/ipc/CInputPipeClient.cpp @@ -23,6 +23,9 @@ #include "CDebug.h" #include "InputPipeProtocol.h" #include "../CHIDDevice.h" +#include "../HIDReports.h" + +#include bool CInputPipeClient::Start() { @@ -67,7 +70,6 @@ bool CInputPipeClient::OnPipeMessage( *static_cast(frame); if (message.magic != LG_INPUT_PIPE_MAGIC || message.version != LG_INPUT_PIPE_VERSION || - message.type != LG_INPUT_PIPE_MESSAGE_REPORT || !message.payloadSize || message.payloadSize > sizeof(message.payload)) return false; @@ -78,10 +80,111 @@ bool CInputPipeClient::OnPipeMessage( DEBUG_WARN("LGInput pipe report sequence changed unexpectedly"); return false; } - m_lastSequence = message.sequence; + bool handled = false; + switch (message.type) + { + case LG_INPUT_PIPE_MESSAGE_MOUSE_RELATIVE: + handled = HandleMouseRelative( + message.payload, message.payloadSize); + break; + + case LG_INPUT_PIPE_MESSAGE_MOUSE_ABSOLUTE: + handled = HandleMouseAbsolute( + message.payload, message.payloadSize); + break; + + case LG_INPUT_PIPE_MESSAGE_KEYBOARD: + handled = HandleKeyboard(message.payload, message.payloadSize); + break; + + default: + return false; + } + + if (!handled) + return false; + + m_lastSequence = message.sequence; + return true; +} + +bool CInputPipeClient::HandleMouseRelative( + const void * payload, + size_t size) +{ + if (size != sizeof(LGInputPipeMouseRelative)) + return false; + + LGInputPipeMouseRelative input = {}; + memcpy(&input, payload, sizeof(input)); + if (input.wheel < LG_INPUT_MOUSE_WHEEL_MIN || + (input.buttons & ~LG_INPUT_MOUSE_BUTTON_MASK)) + return false; + + const HIDMouseRelativeReport report = { + HID_REPORT_ID_MOUSE_RELATIVE, + input.buttons, + input.deltaX, + input.deltaY, + input.wheel, + }; + return SubmitReport(&report, sizeof(report)); +} + +bool CInputPipeClient::HandleMouseAbsolute( + const void * payload, + size_t size) +{ + if (size != sizeof(LGInputPipeMouseAbsolute)) + return false; + + LGInputPipeMouseAbsolute input = {}; + 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)) + return false; + + const HIDMouseAbsoluteReport report = { + HID_REPORT_ID_MOUSE_ABSOLUTE, + input.buttons, + input.x, + input.y, + input.wheel, + }; + return SubmitReport(&report, sizeof(report)); +} + +bool CInputPipeClient::HandleKeyboard( + const void * payload, + size_t size) +{ + if (size != sizeof(LGInputPipeKeyboard)) + return false; + + LGInputPipeKeyboard input = {}; + memcpy(&input, payload, sizeof(input)); + + HIDKeyboardReport report = {}; + report.reportId = HID_REPORT_ID_KEYBOARD; + report.modifiers = input.modifiers; + for (size_t i = 0; i < LG_INPUT_KEYBOARD_KEY_COUNT; ++i) + { + if (input.keys[i] > LG_INPUT_KEYBOARD_USAGE_MAX) + return false; + report.keys[i] = input.keys[i]; + } + + return SubmitReport(&report, sizeof(report)); +} + +bool CInputPipeClient::SubmitReport( + const void * report, + size_t size) +{ + const NTSTATUS status = CHIDDevice::SubmitReport(report, size); - const NTSTATUS status = - CHIDDevice::SubmitReport(message.payload, message.payloadSize); if (status == STATUS_INVALID_PARAMETER) return false; diff --git a/idd/LGInput/ipc/CInputPipeClient.h b/idd/LGInput/ipc/CInputPipeClient.h index 0d2fe2a0..7324ac3e 100644 --- a/idd/LGInput/ipc/CInputPipeClient.h +++ b/idd/LGInput/ipc/CInputPipeClient.h @@ -38,6 +38,10 @@ private: void OnPipeConnected() override; void OnPipeDisconnected() override; bool OnPipeMessage(const void * message, size_t size) override; + bool HandleMouseRelative(const void * payload, size_t size); + bool HandleMouseAbsolute(const void * payload, size_t size); + bool HandleKeyboard(const void * payload, size_t size); + bool SubmitReport(const void * report, size_t size); CPipeEndpoint m_endpoint; uint64_t m_lastSequence = 0;