feat: add Xbox One HID report layout and mapping
Phase 4 foundation, independent of the presentation mechanism: - hid/hid_report.hpp: standard Xbox One report layout (64-byte input: buttons/guide/triggers/sticks; 64-byte output: motors), the HID report descriptor, and GIP state to report mapping. - make_input_report(): remaps GIP button bits to the standard Xbox bitmask, scales 10-bit triggers to 8-bit, sticks pass through. - parse_output_report(): extracts motor intensities from output reports for the rumble relay; malformed reports stop the motors. - test_hid: unit tests for the mapping, scaling, and descriptor. Report packing verified with a descriptor walker: input and output are exactly 64 bytes each. Next step is presentation via DriverKit, which needs a Developer ID with the DriverKit entitlement. Co-Authored-By: qwen3.8-27b@q3_k_xl: implemented HID report layer
This commit is contained in:
@@ -7,10 +7,14 @@
|
||||
// Preferred path: DriverKit extension implementing IOHIDDriver with an
|
||||
// Xbox 360/One-compatible report descriptor (input reports from GIP, output
|
||||
// reports for rumble relayed back via GIP).
|
||||
//
|
||||
// The standard Xbox One report layout and its HID report descriptor live in
|
||||
// hid/hid_report.hpp; this module still needs the virtual device that
|
||||
// presents them to macOS.
|
||||
// ==============================================================================
|
||||
|
||||
namespace xone::hid {
|
||||
|
||||
// TODO(phase 4): report descriptor + virtual device presentation.
|
||||
// TODO(phase 4): virtual device presentation (DriverKit IOHIDDriver).
|
||||
|
||||
} // namespace xone::hid
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <span>
|
||||
|
||||
// ==============================================================================
|
||||
// Standard Xbox One HID report layout
|
||||
// ==============================================================================
|
||||
// The virtual gamepad presents the standard Xbox One controller report
|
||||
// layout so games that expect an Xbox controller (raw HID or via the
|
||||
// GameController framework) recognize it natively.
|
||||
//
|
||||
// Input report (ID 1, 64 bytes):
|
||||
// [0] report ID
|
||||
// [1..2] buttons, little-endian u16:
|
||||
// A,B,X,Y,LB,RB,LS,RS,Back,Start,DpadU,DpadD,DpadL,DpadR
|
||||
// [3] guide button (0/1)
|
||||
// [4] left trigger (8-bit, HID usage Z)
|
||||
// [5] right trigger (8-bit, HID usage Rz)
|
||||
// [6..7] left stick X (s16 LE, usage X)
|
||||
// [8..9] left stick Y (s16 LE, usage Y)
|
||||
// [10..11] right stick X (s16 LE, usage Rx)
|
||||
// [12..13] right stick Y (s16 LE, usage Ry)
|
||||
// [14..63] reserved (zero)
|
||||
//
|
||||
// Output report (ID 1, 64 bytes):
|
||||
// [0] report ID
|
||||
// [1] reserved
|
||||
// [2] left motor intensity (8-bit)
|
||||
// [3] right motor intensity (8-bit)
|
||||
// [4..63] reserved (zero)
|
||||
// ==============================================================================
|
||||
|
||||
namespace xone::hid {
|
||||
|
||||
constexpr std::uint8_t k_report_id = 1;
|
||||
constexpr std::size_t k_input_report_size = 64;
|
||||
constexpr std::size_t k_output_report_size = 64;
|
||||
|
||||
// Offsets within the input report.
|
||||
constexpr std::size_t k_in_buttons = 1; // u16 LE
|
||||
constexpr std::size_t k_in_guide = 3;
|
||||
constexpr std::size_t k_in_trigger_left = 4;
|
||||
constexpr std::size_t k_in_trigger_right = 5;
|
||||
constexpr std::size_t k_in_stick_left_x = 6; // s16 LE
|
||||
constexpr std::size_t k_in_stick_left_y = 8;
|
||||
constexpr std::size_t k_in_stick_right_x = 10;
|
||||
constexpr std::size_t k_in_stick_right_y = 12;
|
||||
|
||||
// Offsets within the output report.
|
||||
constexpr std::size_t k_out_motor_left = 2;
|
||||
constexpr std::size_t k_out_motor_right = 3;
|
||||
|
||||
// HID report descriptor matching the layout above (input + output).
|
||||
auto xbox_one_report_descriptor() -> std::span<std::uint8_t const>;
|
||||
|
||||
// Normalized controller input, as tracked from GIP events. Stick Y is
|
||||
// already oriented so up is positive; triggers are 10-bit values (0..1023)
|
||||
// in the low bits of the u16. Buttons use the GIP bitmask (A=bit4 ...
|
||||
// RS=bit15, Start=bit2, Select=bit3).
|
||||
struct gamepad_input {
|
||||
std::uint16_t buttons = 0;
|
||||
bool guide_down = false;
|
||||
std::uint16_t trigger_left = 0;
|
||||
std::uint16_t trigger_right = 0;
|
||||
std::int16_t stick_left_x = 0;
|
||||
std::int16_t stick_left_y = 0;
|
||||
std::int16_t stick_right_x = 0;
|
||||
std::int16_t stick_right_y = 0;
|
||||
};
|
||||
|
||||
// Build a standard Xbox One input report from normalized GIP state.
|
||||
auto make_input_report(gamepad_input const& in)
|
||||
-> std::array<std::uint8_t, k_input_report_size>;
|
||||
|
||||
// Motor intensities parsed from an output report (0..255 each).
|
||||
struct motor_values {
|
||||
std::uint8_t left = 0;
|
||||
std::uint8_t right = 0;
|
||||
};
|
||||
|
||||
// Parse a standard Xbox One output report. Returns zeroed motors (which
|
||||
// stops the rumble) if the report ID or size does not match.
|
||||
auto parse_output_report(std::span<std::uint8_t const> report)
|
||||
-> motor_values;
|
||||
|
||||
} // namespace xone::hid
|
||||
Reference in New Issue
Block a user