#pragma once #include #include #include #include // ============================================================================== // 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; // 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; // 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 report) -> motor_values; } // namespace xone::hid