1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
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// This driver supports the Nintendo Switch Joy-Cons pair controllers
22#include "SDL_internal.h"
23
24#ifdef SDL_JOYSTICK_HIDAPI
25
26#include "SDL_hidapijoystick_c.h"
27#include "../SDL_sysjoystick.h"
28
29static void HIDAPI_DriverCombined_RegisterHints(SDL_HintCallback callback, void *userdata)
30{
31}
32
33static void HIDAPI_DriverCombined_UnregisterHints(SDL_HintCallback callback, void *userdata)
34{
35}
36
37static bool HIDAPI_DriverCombined_IsEnabled(void)
38{
39 return true;
40}
41
42static bool HIDAPI_DriverCombined_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)
43{
44 // This is always explicitly created for combined devices
45 return false;
46}
47
48static bool HIDAPI_DriverCombined_InitDevice(SDL_HIDAPI_Device *device)
49{
50 return HIDAPI_JoystickConnected(device, NULL);
51}
52
53static int HIDAPI_DriverCombined_GetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id)
54{
55 return -1;
56}
57
58static void HIDAPI_DriverCombined_SetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id, int player_index)
59{
60}
61
62static bool HIDAPI_DriverCombined_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
63{
64 int i;
65 char *serial = NULL, *new_serial;
66 size_t serial_length = 0, new_length;
67
68 SDL_AssertJoysticksLocked();
69
70 for (i = 0; i < device->num_children; ++i) {
71 SDL_HIDAPI_Device *child = device->children[i];
72 if (!child->driver->OpenJoystick(child, joystick)) {
73 child->broken = true;
74
75 while (i-- > 0) {
76 child = device->children[i];
77 child->driver->CloseJoystick(child, joystick);
78 }
79 if (serial) {
80 SDL_free(serial);
81 }
82 return false;
83 }
84
85 // Extend the serial number with the child serial number
86 if (joystick->serial) {
87 new_length = serial_length + 1 + SDL_strlen(joystick->serial);
88 new_serial = (char *)SDL_realloc(serial, new_length);
89 if (new_serial) {
90 if (serial) {
91 SDL_strlcat(new_serial, ",", new_length);
92 SDL_strlcat(new_serial, joystick->serial, new_length);
93 } else {
94 SDL_strlcpy(new_serial, joystick->serial, new_length);
95 }
96 serial = new_serial;
97 serial_length = new_length;
98 }
99 SDL_free(joystick->serial);
100 joystick->serial = NULL;
101 }
102 }
103
104 // Update the joystick with the combined serial numbers
105 if (joystick->serial) {
106 SDL_free(joystick->serial);
107 }
108 joystick->serial = serial;
109
110 return true;
111}
112
113static bool HIDAPI_DriverCombined_RumbleJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble)
114{
115 int i;
116 bool result = false;
117
118 for (i = 0; i < device->num_children; ++i) {
119 SDL_HIDAPI_Device *child = device->children[i];
120 if (child->driver->RumbleJoystick(child, joystick, low_frequency_rumble, high_frequency_rumble)) {
121 result = true;
122 }
123 }
124 return result;
125}
126
127static bool HIDAPI_DriverCombined_RumbleJoystickTriggers(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble)
128{
129 int i;
130 bool result = false;
131
132 for (i = 0; i < device->num_children; ++i) {
133 SDL_HIDAPI_Device *child = device->children[i];
134 if (child->driver->RumbleJoystickTriggers(child, joystick, left_rumble, right_rumble)) {
135 result = true;
136 }
137 }
138 return result;
139}
140
141static Uint32 HIDAPI_DriverCombined_GetJoystickCapabilities(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
142{
143 int i;
144 Uint32 caps = 0;
145
146 for (i = 0; i < device->num_children; ++i) {
147 SDL_HIDAPI_Device *child = device->children[i];
148 caps |= child->driver->GetJoystickCapabilities(child, joystick);
149 }
150 return caps;
151}
152
153static bool HIDAPI_DriverCombined_SetJoystickLED(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint8 red, Uint8 green, Uint8 blue)
154{
155 int i;
156 bool result = false;
157
158 for (i = 0; i < device->num_children; ++i) {
159 SDL_HIDAPI_Device *child = device->children[i];
160 if (child->driver->SetJoystickLED(child, joystick, red, green, blue)) {
161 result = true;
162 }
163 }
164 return result;
165}
166
167static bool HIDAPI_DriverCombined_SendJoystickEffect(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, const void *data, int size)
168{
169 return SDL_Unsupported();
170}
171
172static bool HIDAPI_DriverCombined_SetJoystickSensorsEnabled(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, bool enabled)
173{
174 int i;
175 bool result = false;
176
177 for (i = 0; i < device->num_children; ++i) {
178 SDL_HIDAPI_Device *child = device->children[i];
179 if (child->driver->SetJoystickSensorsEnabled(child, joystick, enabled)) {
180 result = true;
181 }
182 }
183 return result;
184}
185
186static bool HIDAPI_DriverCombined_UpdateDevice(SDL_HIDAPI_Device *device)
187{
188 int i;
189 int result = true;
190
191 for (i = 0; i < device->num_children; ++i) {
192 SDL_HIDAPI_Device *child = device->children[i];
193 if (!child->driver->UpdateDevice(child)) {
194 result = false;
195 }
196 }
197 return result;
198}
199
200static void HIDAPI_DriverCombined_CloseJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
201{
202 int i;
203
204 for (i = 0; i < device->num_children; ++i) {
205 SDL_HIDAPI_Device *child = device->children[i];
206 child->driver->CloseJoystick(child, joystick);
207 }
208}
209
210static void HIDAPI_DriverCombined_FreeDevice(SDL_HIDAPI_Device *device)
211{
212}
213
214SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverCombined = {
215 "SDL_JOYSTICK_HIDAPI_COMBINED",
216 true,
217 HIDAPI_DriverCombined_RegisterHints,
218 HIDAPI_DriverCombined_UnregisterHints,
219 HIDAPI_DriverCombined_IsEnabled,
220 HIDAPI_DriverCombined_IsSupportedDevice,
221 HIDAPI_DriverCombined_InitDevice,
222 HIDAPI_DriverCombined_GetDevicePlayerIndex,
223 HIDAPI_DriverCombined_SetDevicePlayerIndex,
224 HIDAPI_DriverCombined_UpdateDevice,
225 HIDAPI_DriverCombined_OpenJoystick,
226 HIDAPI_DriverCombined_RumbleJoystick,
227 HIDAPI_DriverCombined_RumbleJoystickTriggers,
228 HIDAPI_DriverCombined_GetJoystickCapabilities,
229 HIDAPI_DriverCombined_SetJoystickLED,
230 HIDAPI_DriverCombined_SendJoystickEffect,
231 HIDAPI_DriverCombined_SetJoystickSensorsEnabled,
232 HIDAPI_DriverCombined_CloseJoystick,
233 HIDAPI_DriverCombined_FreeDevice,
234};
235
236#endif // SDL_JOYSTICK_HIDAPI
237