fafca4c265
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
90 lines
3.2 KiB
C++
90 lines
3.2 KiB
C++
#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
|