Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/thirdparty/sdl/joystick/hidapi/SDL_hidapi_xbox360w.c
9912 views
1
/*
2
Simple DirectMedia Layer
3
Copyright (C) 1997-2025 Sam Lantinga <[email protected]>
4
5
This software is provided 'as-is', without any express or implied
6
warranty. In no event will the authors be held liable for any damages
7
arising from the use of this software.
8
9
Permission is granted to anyone to use this software for any purpose,
10
including commercial applications, and to alter it and redistribute it
11
freely, subject to the following restrictions:
12
13
1. The origin of this software must not be misrepresented; you must not
14
claim that you wrote the original software. If you use this software
15
in a product, an acknowledgment in the product documentation would be
16
appreciated but is not required.
17
2. Altered source versions must be plainly marked as such, and must not be
18
misrepresented as being the original software.
19
3. This notice may not be removed or altered from any source distribution.
20
*/
21
#include "SDL_internal.h"
22
23
#ifdef SDL_JOYSTICK_HIDAPI
24
25
#include "../../SDL_hints_c.h"
26
#include "../SDL_sysjoystick.h"
27
#include "SDL_hidapijoystick_c.h"
28
#include "SDL_hidapi_rumble.h"
29
30
#ifdef SDL_JOYSTICK_HIDAPI_XBOX360
31
32
// Define this if you want to log all packets from the controller
33
// #define DEBUG_XBOX_PROTOCOL
34
35
typedef struct
36
{
37
SDL_HIDAPI_Device *device;
38
bool connected;
39
int player_index;
40
bool player_lights;
41
Uint8 last_state[USB_PACKET_LENGTH];
42
} SDL_DriverXbox360W_Context;
43
44
static void HIDAPI_DriverXbox360W_RegisterHints(SDL_HintCallback callback, void *userdata)
45
{
46
SDL_AddHintCallback(SDL_HINT_JOYSTICK_HIDAPI_XBOX, callback, userdata);
47
SDL_AddHintCallback(SDL_HINT_JOYSTICK_HIDAPI_XBOX_360, callback, userdata);
48
SDL_AddHintCallback(SDL_HINT_JOYSTICK_HIDAPI_XBOX_360_WIRELESS, callback, userdata);
49
}
50
51
static void HIDAPI_DriverXbox360W_UnregisterHints(SDL_HintCallback callback, void *userdata)
52
{
53
SDL_RemoveHintCallback(SDL_HINT_JOYSTICK_HIDAPI_XBOX, callback, userdata);
54
SDL_RemoveHintCallback(SDL_HINT_JOYSTICK_HIDAPI_XBOX_360, callback, userdata);
55
SDL_RemoveHintCallback(SDL_HINT_JOYSTICK_HIDAPI_XBOX_360_WIRELESS, callback, userdata);
56
}
57
58
static bool HIDAPI_DriverXbox360W_IsEnabled(void)
59
{
60
return SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI_XBOX_360_WIRELESS,
61
SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI_XBOX_360, SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI_XBOX, SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI, SDL_HIDAPI_DEFAULT))));
62
}
63
64
static bool HIDAPI_DriverXbox360W_IsSupportedDevice(SDL_HIDAPI_Device *device, const char *name, SDL_GamepadType type, Uint16 vendor_id, Uint16 product_id, Uint16 version, int interface_number, int interface_class, int interface_subclass, int interface_protocol)
65
{
66
const int XB360W_IFACE_PROTOCOL = 129; // Wireless
67
68
if ((vendor_id == USB_VENDOR_MICROSOFT && (product_id == USB_PRODUCT_XBOX360_WIRELESS_RECEIVER_THIRDPARTY2 || product_id == USB_PRODUCT_XBOX360_WIRELESS_RECEIVER_THIRDPARTY1 || product_id == USB_PRODUCT_XBOX360_WIRELESS_RECEIVER) && interface_protocol == 0) ||
69
(type == SDL_GAMEPAD_TYPE_XBOX360 && interface_protocol == XB360W_IFACE_PROTOCOL)) {
70
return true;
71
}
72
return false;
73
}
74
75
static bool SetSlotLED(SDL_hid_device *dev, Uint8 slot, bool on)
76
{
77
const bool blink = false;
78
Uint8 mode = on ? ((blink ? 0x02 : 0x06) + slot) : 0;
79
Uint8 led_packet[] = { 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
80
81
led_packet[3] = 0x40 + (mode % 0x0e);
82
if (SDL_hid_write(dev, led_packet, sizeof(led_packet)) != sizeof(led_packet)) {
83
return false;
84
}
85
return true;
86
}
87
88
static void UpdateSlotLED(SDL_DriverXbox360W_Context *ctx)
89
{
90
if (ctx->player_lights && ctx->player_index >= 0) {
91
SetSlotLED(ctx->device->dev, (ctx->player_index % 4), true);
92
} else {
93
SetSlotLED(ctx->device->dev, 0, false);
94
}
95
}
96
97
static void SDLCALL SDL_PlayerLEDHintChanged(void *userdata, const char *name, const char *oldValue, const char *hint)
98
{
99
SDL_DriverXbox360W_Context *ctx = (SDL_DriverXbox360W_Context *)userdata;
100
bool player_lights = SDL_GetStringBoolean(hint, true);
101
102
if (player_lights != ctx->player_lights) {
103
ctx->player_lights = player_lights;
104
105
UpdateSlotLED(ctx);
106
HIDAPI_UpdateDeviceProperties(ctx->device);
107
}
108
}
109
110
static void UpdatePowerLevel(SDL_Joystick *joystick, Uint8 level)
111
{
112
int percent = (int)SDL_roundf((level / 255.0f) * 100.0f);
113
SDL_SendJoystickPowerInfo(joystick, SDL_POWERSTATE_ON_BATTERY, percent);
114
}
115
116
static bool HIDAPI_DriverXbox360W_InitDevice(SDL_HIDAPI_Device *device)
117
{
118
SDL_DriverXbox360W_Context *ctx;
119
120
// Requests controller presence information from the wireless dongle
121
const Uint8 init_packet[] = { 0x08, 0x00, 0x0F, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
122
123
HIDAPI_SetDeviceName(device, "Xbox 360 Wireless Controller");
124
125
ctx = (SDL_DriverXbox360W_Context *)SDL_calloc(1, sizeof(*ctx));
126
if (!ctx) {
127
return false;
128
}
129
ctx->device = device;
130
131
device->context = ctx;
132
133
if (SDL_hid_write(device->dev, init_packet, sizeof(init_packet)) != sizeof(init_packet)) {
134
SDL_SetError("Couldn't write init packet");
135
return false;
136
}
137
138
device->type = SDL_GAMEPAD_TYPE_XBOX360;
139
140
return true;
141
}
142
143
static int HIDAPI_DriverXbox360W_GetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id)
144
{
145
return -1;
146
}
147
148
static void HIDAPI_DriverXbox360W_SetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id, int player_index)
149
{
150
SDL_DriverXbox360W_Context *ctx = (SDL_DriverXbox360W_Context *)device->context;
151
152
if (!ctx) {
153
return;
154
}
155
156
ctx->player_index = player_index;
157
158
UpdateSlotLED(ctx);
159
}
160
161
static bool HIDAPI_DriverXbox360W_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
162
{
163
SDL_DriverXbox360W_Context *ctx = (SDL_DriverXbox360W_Context *)device->context;
164
165
SDL_AssertJoysticksLocked();
166
167
SDL_zeroa(ctx->last_state);
168
169
// Initialize player index (needed for setting LEDs)
170
ctx->player_index = SDL_GetJoystickPlayerIndex(joystick);
171
ctx->player_lights = SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI_XBOX_360_PLAYER_LED, true);
172
UpdateSlotLED(ctx);
173
174
SDL_AddHintCallback(SDL_HINT_JOYSTICK_HIDAPI_XBOX_360_PLAYER_LED,
175
SDL_PlayerLEDHintChanged, ctx);
176
177
// Initialize the joystick capabilities
178
joystick->nbuttons = 11;
179
joystick->naxes = SDL_GAMEPAD_AXIS_COUNT;
180
joystick->nhats = 1;
181
joystick->connection_state = SDL_JOYSTICK_CONNECTION_WIRELESS;
182
183
return true;
184
}
185
186
static bool HIDAPI_DriverXbox360W_RumbleJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble)
187
{
188
Uint8 rumble_packet[] = { 0x00, 0x01, 0x0f, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
189
190
rumble_packet[5] = (low_frequency_rumble >> 8);
191
rumble_packet[6] = (high_frequency_rumble >> 8);
192
193
if (SDL_HIDAPI_SendRumble(device, rumble_packet, sizeof(rumble_packet)) != sizeof(rumble_packet)) {
194
return SDL_SetError("Couldn't send rumble packet");
195
}
196
return true;
197
}
198
199
static bool HIDAPI_DriverXbox360W_RumbleJoystickTriggers(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble)
200
{
201
return SDL_Unsupported();
202
}
203
204
static Uint32 HIDAPI_DriverXbox360W_GetJoystickCapabilities(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
205
{
206
SDL_DriverXbox360W_Context *ctx = (SDL_DriverXbox360W_Context *)device->context;
207
Uint32 result = SDL_JOYSTICK_CAP_RUMBLE;
208
209
if (ctx->player_lights) {
210
result |= SDL_JOYSTICK_CAP_PLAYER_LED;
211
}
212
return result;
213
}
214
215
static bool HIDAPI_DriverXbox360W_SetJoystickLED(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint8 red, Uint8 green, Uint8 blue)
216
{
217
return SDL_Unsupported();
218
}
219
220
static bool HIDAPI_DriverXbox360W_SendJoystickEffect(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, const void *data, int size)
221
{
222
return SDL_Unsupported();
223
}
224
225
static bool HIDAPI_DriverXbox360W_SetJoystickSensorsEnabled(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, bool enabled)
226
{
227
return SDL_Unsupported();
228
}
229
230
static void HIDAPI_DriverXbox360W_HandleStatePacket(SDL_Joystick *joystick, SDL_hid_device *dev, SDL_DriverXbox360W_Context *ctx, Uint8 *data, int size)
231
{
232
Sint16 axis;
233
const bool invert_y_axes = true;
234
Uint64 timestamp = SDL_GetTicksNS();
235
236
if (ctx->last_state[2] != data[2]) {
237
Uint8 hat = 0;
238
239
if (data[2] & 0x01) {
240
hat |= SDL_HAT_UP;
241
}
242
if (data[2] & 0x02) {
243
hat |= SDL_HAT_DOWN;
244
}
245
if (data[2] & 0x04) {
246
hat |= SDL_HAT_LEFT;
247
}
248
if (data[2] & 0x08) {
249
hat |= SDL_HAT_RIGHT;
250
}
251
SDL_SendJoystickHat(timestamp, joystick, 0, hat);
252
253
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_START, ((data[2] & 0x10) != 0));
254
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_BACK, ((data[2] & 0x20) != 0));
255
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_STICK, ((data[2] & 0x40) != 0));
256
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_STICK, ((data[2] & 0x80) != 0));
257
}
258
259
if (ctx->last_state[3] != data[3]) {
260
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER, ((data[3] & 0x01) != 0));
261
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER, ((data[3] & 0x02) != 0));
262
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_GUIDE, ((data[3] & 0x04) != 0));
263
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_SOUTH, ((data[3] & 0x10) != 0));
264
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_EAST, ((data[3] & 0x20) != 0));
265
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_WEST, ((data[3] & 0x40) != 0));
266
SDL_SendJoystickButton(timestamp, joystick, SDL_GAMEPAD_BUTTON_NORTH, ((data[3] & 0x80) != 0));
267
}
268
269
axis = ((int)data[4] * 257) - 32768;
270
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFT_TRIGGER, axis);
271
axis = ((int)data[5] * 257) - 32768;
272
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHT_TRIGGER, axis);
273
axis = SDL_Swap16LE(*(Sint16 *)(&data[6]));
274
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, axis);
275
axis = SDL_Swap16LE(*(Sint16 *)(&data[8]));
276
if (invert_y_axes) {
277
axis = ~axis;
278
}
279
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, axis);
280
axis = SDL_Swap16LE(*(Sint16 *)(&data[10]));
281
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTX, axis);
282
axis = SDL_Swap16LE(*(Sint16 *)(&data[12]));
283
if (invert_y_axes) {
284
axis = ~axis;
285
}
286
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTY, axis);
287
288
SDL_memcpy(ctx->last_state, data, SDL_min(size, sizeof(ctx->last_state)));
289
}
290
291
static bool HIDAPI_DriverXbox360W_UpdateDevice(SDL_HIDAPI_Device *device)
292
{
293
SDL_DriverXbox360W_Context *ctx = (SDL_DriverXbox360W_Context *)device->context;
294
SDL_Joystick *joystick = NULL;
295
Uint8 data[USB_PACKET_LENGTH];
296
int size;
297
298
if (device->num_joysticks > 0) {
299
joystick = SDL_GetJoystickFromID(device->joysticks[0]);
300
}
301
302
while ((size = SDL_hid_read_timeout(device->dev, data, sizeof(data), 0)) > 0) {
303
#ifdef DEBUG_XBOX_PROTOCOL
304
HIDAPI_DumpPacket("Xbox 360 wireless packet: size = %d", data, size);
305
#endif
306
if (size == 2 && data[0] == 0x08) {
307
bool connected = (data[1] & 0x80) ? true : false;
308
#ifdef DEBUG_JOYSTICK
309
SDL_Log("Connected = %s", connected ? "TRUE" : "FALSE");
310
#endif
311
if (connected != ctx->connected) {
312
ctx->connected = connected;
313
314
if (connected) {
315
SDL_JoystickID joystickID;
316
317
HIDAPI_JoystickConnected(device, &joystickID);
318
319
} else if (device->num_joysticks > 0) {
320
HIDAPI_JoystickDisconnected(device, device->joysticks[0]);
321
}
322
}
323
} else if (size == 29 && data[0] == 0x00 && data[1] == 0x0f && data[2] == 0x00 && data[3] == 0xf0) {
324
// Serial number is data[7-13]
325
#ifdef DEBUG_JOYSTICK
326
SDL_Log("Battery status (initial): %d", data[17]);
327
#endif
328
if (joystick) {
329
UpdatePowerLevel(joystick, data[17]);
330
}
331
} else if (size == 29 && data[0] == 0x00 && data[1] == 0x00 && data[2] == 0x00 && data[3] == 0x13) {
332
#ifdef DEBUG_JOYSTICK
333
SDL_Log("Battery status: %d", data[4]);
334
#endif
335
if (joystick) {
336
UpdatePowerLevel(joystick, data[4]);
337
}
338
} else if (size == 29 && data[0] == 0x00 && (data[1] & 0x01) == 0x01) {
339
if (joystick) {
340
HIDAPI_DriverXbox360W_HandleStatePacket(joystick, device->dev, ctx, data + 4, size - 4);
341
}
342
}
343
}
344
345
if (size < 0 && device->num_joysticks > 0) {
346
// Read error, device is disconnected
347
HIDAPI_JoystickDisconnected(device, device->joysticks[0]);
348
}
349
return (size >= 0);
350
}
351
352
static void HIDAPI_DriverXbox360W_CloseJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
353
{
354
SDL_DriverXbox360W_Context *ctx = (SDL_DriverXbox360W_Context *)device->context;
355
356
SDL_RemoveHintCallback(SDL_HINT_JOYSTICK_HIDAPI_XBOX_360_PLAYER_LED,
357
SDL_PlayerLEDHintChanged, ctx);
358
}
359
360
static void HIDAPI_DriverXbox360W_FreeDevice(SDL_HIDAPI_Device *device)
361
{
362
}
363
364
SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverXbox360W = {
365
SDL_HINT_JOYSTICK_HIDAPI_XBOX_360_WIRELESS,
366
true,
367
HIDAPI_DriverXbox360W_RegisterHints,
368
HIDAPI_DriverXbox360W_UnregisterHints,
369
HIDAPI_DriverXbox360W_IsEnabled,
370
HIDAPI_DriverXbox360W_IsSupportedDevice,
371
HIDAPI_DriverXbox360W_InitDevice,
372
HIDAPI_DriverXbox360W_GetDevicePlayerIndex,
373
HIDAPI_DriverXbox360W_SetDevicePlayerIndex,
374
HIDAPI_DriverXbox360W_UpdateDevice,
375
HIDAPI_DriverXbox360W_OpenJoystick,
376
HIDAPI_DriverXbox360W_RumbleJoystick,
377
HIDAPI_DriverXbox360W_RumbleJoystickTriggers,
378
HIDAPI_DriverXbox360W_GetJoystickCapabilities,
379
HIDAPI_DriverXbox360W_SetJoystickLED,
380
HIDAPI_DriverXbox360W_SendJoystickEffect,
381
HIDAPI_DriverXbox360W_SetJoystickSensorsEnabled,
382
HIDAPI_DriverXbox360W_CloseJoystick,
383
HIDAPI_DriverXbox360W_FreeDevice,
384
};
385
386
#endif // SDL_JOYSTICK_HIDAPI_XBOX360
387
388
#endif // SDL_JOYSTICK_HIDAPI
389
390