feat: show live controller input

Route paired controller QoS data through the existing GIP protocol layer and
expose the latest gamepad report through the C API. Add a SwiftUI monitor for
buttons, d-pad, triggers, and stick positions without registering a native HID
device.

Co-Authored-By: openai/gpt-5.6-luna: GIP bridge and live input monitor
This commit is contained in:
portersky
2026-08-29 13:33:32 +02:00
parent 325923751c
commit 9c1b6493d8
7 changed files with 462 additions and 40 deletions
+24 -2
View File
@@ -39,8 +39,8 @@ xone_dongle *xone_open(void);
// Kick off firmware load + radio init on a background thread. Non-blocking:
// returns 0 once started, -1 if the session is busy or already ready. If
// firmware_path is NULL, the image is selected per product ID
// (firmware/xone_dongle_<pid>.bin).
// firmware_path is NULL, the common upstream image
// (firmware/xow_dongle.bin) is used.
int xone_start(xone_dongle *d, const char *firmware_path);
// Close the session and release the dongle. Waits for any in-progress start.
@@ -72,6 +72,28 @@ int xone_controller_count(xone_dongle const *d);
// success, -1 if index is out of range.
int xone_controller_mac(xone_dongle const *d, int index, char *buf, int len);
// Snapshot of the latest standard Xbox controller input report. Stick values
// are signed 16-bit values; trigger values are unsigned 10-bit values. The
// buttons field uses the standard GIP gamepad bit assignments.
struct xone_controller_state {
uint16_t buttons;
bool guide_down;
bool gip_ready;
bool input_active;
uint16_t trigger_left;
uint16_t trigger_right;
int16_t stick_left_x;
int16_t stick_left_y;
int16_t stick_right_x;
int16_t stick_right_y;
uint32_t sequence;
};
// Copy the latest input state for the controller at index. Returns 0 on
// success, -1 if index is out of range or state is NULL.
int xone_controller_get_state(xone_dongle const *d, int index,
struct xone_controller_state *state);
// Enter or leave pairing mode (beacon pairing flag + LED blink).
void xone_set_pairing(xone_dongle *d, bool enable);
+6
View File
@@ -77,6 +77,12 @@ public:
// then bulk-writes it to EP OUT.
auto send_wlan(std::span<std::uint8_t const> frame) -> int;
// Send a GIP payload in an upstream QoS-data frame for one client.
auto send_client_frame(std::uint8_t wcid,
std::span<std::uint8_t const> addr,
std::span<std::uint8_t const> frame,
bool encrypted) -> int;
// Associate a controller: program its WCID MAC, ADD_CLIENT ms_command,
// and transmit an ASSOC_RESP mgmt frame.
auto associate_client(std::uint8_t wcid, std::span<std::uint8_t const> addr)
+3 -1
View File
@@ -458,10 +458,12 @@ constexpr std::uint16_t ieee80211_fctl_ftype = 0x0c;
constexpr std::uint16_t ieee80211_fctl_stype = 0xf0;
constexpr std::uint16_t ieee80211_ftype_mgmt = 0x00;
constexpr std::uint16_t ieee80211_ftype_data = 0x08;
constexpr std::uint16_t ieee80211_fctl_from_ds = 0x0200;
constexpr std::uint16_t ieee80211_fctl_protected = 0x4000;
constexpr std::uint16_t ieee80211_stype_assoc_req = 0x00;
constexpr std::uint16_t ieee80211_stype_assoc_resp = 0x10;
constexpr std::uint16_t ieee80211_stype_disassoc = 0xa0;
constexpr std::uint16_t ieee80211_stype_qos_data = 0x70;
constexpr std::uint16_t ieee80211_stype_qos_data = 0x80;
// MGMT subtype for reserved (pairing) frames.
constexpr std::uint16_t ieee80211_stype_wlan_reserved = 0x70;