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 | #include "SDL_internal.h" |
22 | |
23 | #ifdef SDL_JOYSTICK_VIRTUAL |
24 | |
25 | // This is the virtual implementation of the SDL joystick API |
26 | |
27 | #include "SDL_virtualjoystick_c.h" |
28 | #include "../SDL_sysjoystick.h" |
29 | #include "../SDL_joystick_c.h" |
30 | |
31 | static joystick_hwdata *g_VJoys SDL_GUARDED_BY(SDL_joystick_lock) = NULL; |
32 | |
33 | static joystick_hwdata *VIRTUAL_HWDataForInstance(SDL_JoystickID instance_id) |
34 | { |
35 | joystick_hwdata *vjoy; |
36 | |
37 | SDL_AssertJoysticksLocked(); |
38 | |
39 | for (vjoy = g_VJoys; vjoy; vjoy = vjoy->next) { |
40 | if (instance_id == vjoy->instance_id) { |
41 | return vjoy; |
42 | } |
43 | } |
44 | return NULL; |
45 | } |
46 | |
47 | static joystick_hwdata *VIRTUAL_HWDataForIndex(int device_index) |
48 | { |
49 | joystick_hwdata *vjoy; |
50 | |
51 | SDL_AssertJoysticksLocked(); |
52 | |
53 | for (vjoy = g_VJoys; vjoy; vjoy = vjoy->next) { |
54 | if (device_index == 0) { |
55 | break; |
56 | } |
57 | --device_index; |
58 | } |
59 | return vjoy; |
60 | } |
61 | |
62 | static void VIRTUAL_FreeHWData(joystick_hwdata *hwdata) |
63 | { |
64 | joystick_hwdata *cur; |
65 | joystick_hwdata *prev = NULL; |
66 | |
67 | SDL_AssertJoysticksLocked(); |
68 | |
69 | if (!hwdata) { |
70 | return; |
71 | } |
72 | |
73 | if (hwdata->desc.Cleanup) { |
74 | hwdata->desc.Cleanup(hwdata->desc.userdata); |
75 | } |
76 | |
77 | // Remove hwdata from SDL-global list |
78 | for (cur = g_VJoys; cur; prev = cur, cur = cur->next) { |
79 | if (hwdata == cur) { |
80 | if (prev) { |
81 | prev->next = cur->next; |
82 | } else { |
83 | g_VJoys = cur->next; |
84 | } |
85 | break; |
86 | } |
87 | } |
88 | |
89 | if (hwdata->joystick) { |
90 | hwdata->joystick->hwdata = NULL; |
91 | hwdata->joystick = NULL; |
92 | } |
93 | if (hwdata->name) { |
94 | SDL_free(hwdata->name); |
95 | hwdata->name = NULL; |
96 | } |
97 | if (hwdata->axes) { |
98 | SDL_free((void *)hwdata->axes); |
99 | hwdata->axes = NULL; |
100 | } |
101 | if (hwdata->buttons) { |
102 | SDL_free(hwdata->buttons); |
103 | hwdata->buttons = NULL; |
104 | } |
105 | if (hwdata->hats) { |
106 | SDL_free(hwdata->hats); |
107 | hwdata->hats = NULL; |
108 | } |
109 | if (hwdata->balls) { |
110 | SDL_free(hwdata->balls); |
111 | hwdata->balls = NULL; |
112 | } |
113 | if (hwdata->touchpads) { |
114 | for (Uint16 i = 0; i < hwdata->desc.ntouchpads; ++i) { |
115 | SDL_free(hwdata->touchpads[i].fingers); |
116 | hwdata->touchpads[i].fingers = NULL; |
117 | } |
118 | SDL_free(hwdata->touchpads); |
119 | hwdata->touchpads = NULL; |
120 | } |
121 | if (hwdata->sensors) { |
122 | SDL_free(hwdata->sensors); |
123 | hwdata->sensors = NULL; |
124 | } |
125 | if (hwdata->sensor_events) { |
126 | SDL_free(hwdata->sensor_events); |
127 | hwdata->sensor_events = NULL; |
128 | } |
129 | SDL_free(hwdata); |
130 | } |
131 | |
132 | SDL_JoystickID SDL_JoystickAttachVirtualInner(const SDL_VirtualJoystickDesc *desc) |
133 | { |
134 | joystick_hwdata *hwdata = NULL; |
135 | const char *name = NULL; |
136 | int axis_triggerleft = -1; |
137 | int axis_triggerright = -1; |
138 | |
139 | SDL_AssertJoysticksLocked(); |
140 | |
141 | if (!desc) { |
142 | SDL_InvalidParamError("desc" ); |
143 | return 0; |
144 | } |
145 | if (desc->version < sizeof(*desc)) { |
146 | // Update this to handle older versions of this interface |
147 | SDL_SetError("Invalid desc, should be initialized with SDL_INIT_INTERFACE()" ); |
148 | return 0; |
149 | } |
150 | |
151 | hwdata = (joystick_hwdata *)SDL_calloc(1, sizeof(joystick_hwdata)); |
152 | if (!hwdata) { |
153 | VIRTUAL_FreeHWData(hwdata); |
154 | return 0; |
155 | } |
156 | SDL_copyp(&hwdata->desc, desc); |
157 | hwdata->desc.touchpads = NULL; |
158 | hwdata->desc.sensors = NULL; |
159 | |
160 | if (hwdata->desc.name) { |
161 | name = hwdata->desc.name; |
162 | } else { |
163 | switch (hwdata->desc.type) { |
164 | case SDL_JOYSTICK_TYPE_GAMEPAD: |
165 | name = "Virtual Controller" ; |
166 | break; |
167 | case SDL_JOYSTICK_TYPE_WHEEL: |
168 | name = "Virtual Wheel" ; |
169 | break; |
170 | case SDL_JOYSTICK_TYPE_ARCADE_STICK: |
171 | name = "Virtual Arcade Stick" ; |
172 | break; |
173 | case SDL_JOYSTICK_TYPE_FLIGHT_STICK: |
174 | name = "Virtual Flight Stick" ; |
175 | break; |
176 | case SDL_JOYSTICK_TYPE_DANCE_PAD: |
177 | name = "Virtual Dance Pad" ; |
178 | break; |
179 | case SDL_JOYSTICK_TYPE_GUITAR: |
180 | name = "Virtual Guitar" ; |
181 | break; |
182 | case SDL_JOYSTICK_TYPE_DRUM_KIT: |
183 | name = "Virtual Drum Kit" ; |
184 | break; |
185 | case SDL_JOYSTICK_TYPE_ARCADE_PAD: |
186 | name = "Virtual Arcade Pad" ; |
187 | break; |
188 | case SDL_JOYSTICK_TYPE_THROTTLE: |
189 | name = "Virtual Throttle" ; |
190 | break; |
191 | default: |
192 | name = "Virtual Joystick" ; |
193 | break; |
194 | } |
195 | } |
196 | hwdata->name = SDL_strdup(name); |
197 | |
198 | if (hwdata->desc.type == SDL_JOYSTICK_TYPE_GAMEPAD) { |
199 | int i, axis; |
200 | |
201 | if (hwdata->desc.button_mask == 0) { |
202 | for (i = 0; i < hwdata->desc.nbuttons && i < sizeof(hwdata->desc.button_mask) * 8; ++i) { |
203 | hwdata->desc.button_mask |= (1 << i); |
204 | } |
205 | } |
206 | |
207 | if (hwdata->desc.axis_mask == 0) { |
208 | if (hwdata->desc.naxes >= 2) { |
209 | hwdata->desc.axis_mask |= ((1 << SDL_GAMEPAD_AXIS_LEFTX) | (1 << SDL_GAMEPAD_AXIS_LEFTY)); |
210 | } |
211 | if (hwdata->desc.naxes >= 4) { |
212 | hwdata->desc.axis_mask |= ((1 << SDL_GAMEPAD_AXIS_RIGHTX) | (1 << SDL_GAMEPAD_AXIS_RIGHTY)); |
213 | } |
214 | if (hwdata->desc.naxes >= 6) { |
215 | hwdata->desc.axis_mask |= ((1 << SDL_GAMEPAD_AXIS_LEFT_TRIGGER) | (1 << SDL_GAMEPAD_AXIS_RIGHT_TRIGGER)); |
216 | } |
217 | } |
218 | |
219 | // Find the trigger axes |
220 | axis = 0; |
221 | for (i = 0; axis < hwdata->desc.naxes && i < SDL_GAMEPAD_AXIS_COUNT; ++i) { |
222 | if (hwdata->desc.axis_mask & (1 << i)) { |
223 | if (i == SDL_GAMEPAD_AXIS_LEFT_TRIGGER) { |
224 | axis_triggerleft = axis; |
225 | } |
226 | if (i == SDL_GAMEPAD_AXIS_RIGHT_TRIGGER) { |
227 | axis_triggerright = axis; |
228 | } |
229 | ++axis; |
230 | } |
231 | } |
232 | } |
233 | |
234 | hwdata->guid = SDL_CreateJoystickGUID(SDL_HARDWARE_BUS_VIRTUAL, hwdata->desc.vendor_id, hwdata->desc.product_id, 0, NULL, name, 'v', (Uint8)hwdata->desc.type); |
235 | |
236 | // Allocate fields for different control-types |
237 | if (hwdata->desc.naxes > 0) { |
238 | hwdata->axes = (Sint16 *)SDL_calloc(hwdata->desc.naxes, sizeof(*hwdata->axes)); |
239 | if (!hwdata->axes) { |
240 | VIRTUAL_FreeHWData(hwdata); |
241 | return 0; |
242 | } |
243 | |
244 | // Trigger axes are at minimum value at rest |
245 | if (axis_triggerleft >= 0) { |
246 | hwdata->axes[axis_triggerleft] = SDL_JOYSTICK_AXIS_MIN; |
247 | } |
248 | if (axis_triggerright >= 0) { |
249 | hwdata->axes[axis_triggerright] = SDL_JOYSTICK_AXIS_MIN; |
250 | } |
251 | } |
252 | if (hwdata->desc.nbuttons > 0) { |
253 | hwdata->buttons = (bool *)SDL_calloc(hwdata->desc.nbuttons, sizeof(*hwdata->buttons)); |
254 | if (!hwdata->buttons) { |
255 | VIRTUAL_FreeHWData(hwdata); |
256 | return 0; |
257 | } |
258 | } |
259 | if (hwdata->desc.nhats > 0) { |
260 | hwdata->hats = (Uint8 *)SDL_calloc(hwdata->desc.nhats, sizeof(*hwdata->hats)); |
261 | if (!hwdata->hats) { |
262 | VIRTUAL_FreeHWData(hwdata); |
263 | return 0; |
264 | } |
265 | } |
266 | if (hwdata->desc.nballs > 0) { |
267 | hwdata->balls = (SDL_JoystickBallData *)SDL_calloc(hwdata->desc.nballs, sizeof(*hwdata->balls)); |
268 | if (!hwdata->balls) { |
269 | VIRTUAL_FreeHWData(hwdata); |
270 | return 0; |
271 | } |
272 | } |
273 | if (hwdata->desc.ntouchpads > 0) { |
274 | if (!desc->touchpads) { |
275 | VIRTUAL_FreeHWData(hwdata); |
276 | SDL_SetError("desc missing touchpad descriptions" ); |
277 | return 0; |
278 | } |
279 | hwdata->touchpads = (SDL_JoystickTouchpadInfo *)SDL_calloc(hwdata->desc.ntouchpads, sizeof(*hwdata->touchpads)); |
280 | if (!hwdata->touchpads) { |
281 | VIRTUAL_FreeHWData(hwdata); |
282 | return 0; |
283 | } |
284 | for (Uint16 i = 0; i < hwdata->desc.ntouchpads; ++i) { |
285 | const SDL_VirtualJoystickTouchpadDesc *touchpad_desc = &desc->touchpads[i]; |
286 | hwdata->touchpads[i].nfingers = touchpad_desc->nfingers; |
287 | hwdata->touchpads[i].fingers = (SDL_JoystickTouchpadFingerInfo *)SDL_calloc(touchpad_desc->nfingers, sizeof(*hwdata->touchpads[i].fingers)); |
288 | if (!hwdata->touchpads[i].fingers) { |
289 | VIRTUAL_FreeHWData(hwdata); |
290 | return 0; |
291 | } |
292 | } |
293 | } |
294 | if (hwdata->desc.nsensors > 0) { |
295 | if (!desc->sensors) { |
296 | VIRTUAL_FreeHWData(hwdata); |
297 | SDL_SetError("desc missing sensor descriptions" ); |
298 | return 0; |
299 | } |
300 | hwdata->sensors = (SDL_JoystickSensorInfo *)SDL_calloc(hwdata->desc.nsensors, sizeof(*hwdata->sensors)); |
301 | if (!hwdata->sensors) { |
302 | VIRTUAL_FreeHWData(hwdata); |
303 | return 0; |
304 | } |
305 | for (Uint16 i = 0; i < hwdata->desc.nsensors; ++i) { |
306 | const SDL_VirtualJoystickSensorDesc *sensor_desc = &desc->sensors[i]; |
307 | hwdata->sensors[i].type = sensor_desc->type; |
308 | hwdata->sensors[i].rate = sensor_desc->rate; |
309 | } |
310 | } |
311 | |
312 | // Allocate an instance ID for this device |
313 | hwdata->instance_id = SDL_GetNextObjectID(); |
314 | |
315 | // Add virtual joystick to SDL-global lists |
316 | if (g_VJoys) { |
317 | joystick_hwdata *last; |
318 | |
319 | for (last = g_VJoys; last->next; last = last->next) { |
320 | } |
321 | last->next = hwdata; |
322 | } else { |
323 | g_VJoys = hwdata; |
324 | } |
325 | SDL_PrivateJoystickAdded(hwdata->instance_id); |
326 | |
327 | return hwdata->instance_id; |
328 | } |
329 | |
330 | bool SDL_JoystickDetachVirtualInner(SDL_JoystickID instance_id) |
331 | { |
332 | joystick_hwdata *hwdata = VIRTUAL_HWDataForInstance(instance_id); |
333 | if (!hwdata) { |
334 | return SDL_SetError("Virtual joystick data not found" ); |
335 | } |
336 | VIRTUAL_FreeHWData(hwdata); |
337 | SDL_PrivateJoystickRemoved(instance_id); |
338 | return true; |
339 | } |
340 | |
341 | bool SDL_SetJoystickVirtualAxisInner(SDL_Joystick *joystick, int axis, Sint16 value) |
342 | { |
343 | joystick_hwdata *hwdata; |
344 | |
345 | SDL_AssertJoysticksLocked(); |
346 | |
347 | if (!joystick || !joystick->hwdata) { |
348 | return SDL_SetError("Invalid joystick" ); |
349 | } |
350 | |
351 | hwdata = (joystick_hwdata *)joystick->hwdata; |
352 | if (axis < 0 || axis >= hwdata->desc.naxes) { |
353 | return SDL_SetError("Invalid axis index" ); |
354 | } |
355 | |
356 | hwdata->axes[axis] = value; |
357 | hwdata->changes |= AXES_CHANGED; |
358 | |
359 | return true; |
360 | } |
361 | |
362 | bool SDL_SetJoystickVirtualBallInner(SDL_Joystick *joystick, int ball, Sint16 xrel, Sint16 yrel) |
363 | { |
364 | joystick_hwdata *hwdata; |
365 | |
366 | SDL_AssertJoysticksLocked(); |
367 | |
368 | if (!joystick || !joystick->hwdata) { |
369 | return SDL_SetError("Invalid joystick" ); |
370 | } |
371 | |
372 | hwdata = (joystick_hwdata *)joystick->hwdata; |
373 | if (ball < 0 || ball >= hwdata->desc.nballs) { |
374 | return SDL_SetError("Invalid ball index" ); |
375 | } |
376 | |
377 | hwdata->balls[ball].dx += xrel; |
378 | hwdata->balls[ball].dx = SDL_clamp(hwdata->balls[ball].dx, SDL_MIN_SINT16, SDL_MAX_SINT16); |
379 | hwdata->balls[ball].dy += yrel; |
380 | hwdata->balls[ball].dy = SDL_clamp(hwdata->balls[ball].dy, SDL_MIN_SINT16, SDL_MAX_SINT16); |
381 | hwdata->changes |= BALLS_CHANGED; |
382 | |
383 | return true; |
384 | } |
385 | |
386 | bool SDL_SetJoystickVirtualButtonInner(SDL_Joystick *joystick, int button, bool down) |
387 | { |
388 | joystick_hwdata *hwdata; |
389 | |
390 | SDL_AssertJoysticksLocked(); |
391 | |
392 | if (!joystick || !joystick->hwdata) { |
393 | return SDL_SetError("Invalid joystick" ); |
394 | } |
395 | |
396 | hwdata = (joystick_hwdata *)joystick->hwdata; |
397 | if (button < 0 || button >= hwdata->desc.nbuttons) { |
398 | return SDL_SetError("Invalid button index" ); |
399 | } |
400 | |
401 | hwdata->buttons[button] = down; |
402 | hwdata->changes |= BUTTONS_CHANGED; |
403 | |
404 | return true; |
405 | } |
406 | |
407 | bool SDL_SetJoystickVirtualHatInner(SDL_Joystick *joystick, int hat, Uint8 value) |
408 | { |
409 | joystick_hwdata *hwdata; |
410 | |
411 | SDL_AssertJoysticksLocked(); |
412 | |
413 | if (!joystick || !joystick->hwdata) { |
414 | return SDL_SetError("Invalid joystick" ); |
415 | } |
416 | |
417 | hwdata = (joystick_hwdata *)joystick->hwdata; |
418 | if (hat < 0 || hat >= hwdata->desc.nhats) { |
419 | return SDL_SetError("Invalid hat index" ); |
420 | } |
421 | |
422 | hwdata->hats[hat] = value; |
423 | hwdata->changes |= HATS_CHANGED; |
424 | |
425 | return true; |
426 | } |
427 | |
428 | bool SDL_SetJoystickVirtualTouchpadInner(SDL_Joystick *joystick, int touchpad, int finger, bool down, float x, float y, float pressure) |
429 | { |
430 | joystick_hwdata *hwdata; |
431 | |
432 | SDL_AssertJoysticksLocked(); |
433 | |
434 | if (!joystick || !joystick->hwdata) { |
435 | return SDL_SetError("Invalid joystick" ); |
436 | } |
437 | |
438 | hwdata = (joystick_hwdata *)joystick->hwdata; |
439 | if (touchpad < 0 || touchpad >= hwdata->desc.ntouchpads) { |
440 | return SDL_SetError("Invalid touchpad index" ); |
441 | } |
442 | if (finger < 0 || finger >= hwdata->touchpads[touchpad].nfingers) { |
443 | return SDL_SetError("Invalid finger index" ); |
444 | } |
445 | |
446 | SDL_JoystickTouchpadFingerInfo *info = &hwdata->touchpads[touchpad].fingers[finger]; |
447 | info->down = down; |
448 | info->x = x; |
449 | info->y = y; |
450 | info->pressure = pressure; |
451 | hwdata->changes |= TOUCHPADS_CHANGED; |
452 | |
453 | return true; |
454 | } |
455 | |
456 | bool SDL_SendJoystickVirtualSensorDataInner(SDL_Joystick *joystick, SDL_SensorType type, Uint64 sensor_timestamp, const float *data, int num_values) |
457 | { |
458 | joystick_hwdata *hwdata; |
459 | |
460 | SDL_AssertJoysticksLocked(); |
461 | |
462 | if (!joystick || !joystick->hwdata) { |
463 | return SDL_SetError("Invalid joystick" ); |
464 | } |
465 | |
466 | hwdata = (joystick_hwdata *)joystick->hwdata; |
467 | if (hwdata->num_sensor_events == hwdata->max_sensor_events) { |
468 | int new_max_sensor_events = (hwdata->max_sensor_events + 1); |
469 | VirtualSensorEvent *sensor_events = (VirtualSensorEvent *)SDL_realloc(hwdata->sensor_events, new_max_sensor_events * sizeof(*sensor_events)); |
470 | if (!sensor_events) { |
471 | return false; |
472 | } |
473 | hwdata->sensor_events = sensor_events; |
474 | hwdata->max_sensor_events = hwdata->max_sensor_events; |
475 | } |
476 | |
477 | VirtualSensorEvent *event = &hwdata->sensor_events[hwdata->num_sensor_events++]; |
478 | event->type = type; |
479 | event->sensor_timestamp = sensor_timestamp; |
480 | event->num_values = SDL_min(num_values, SDL_arraysize(event->data)); |
481 | SDL_memcpy(event->data, data, (event->num_values * sizeof(*event->data))); |
482 | |
483 | return true; |
484 | } |
485 | |
486 | static bool VIRTUAL_JoystickInit(void) |
487 | { |
488 | return true; |
489 | } |
490 | |
491 | static int VIRTUAL_JoystickGetCount(void) |
492 | { |
493 | joystick_hwdata *cur; |
494 | int count = 0; |
495 | |
496 | SDL_AssertJoysticksLocked(); |
497 | |
498 | for (cur = g_VJoys; cur; cur = cur->next) { |
499 | ++count; |
500 | } |
501 | return count; |
502 | } |
503 | |
504 | static void VIRTUAL_JoystickDetect(void) |
505 | { |
506 | } |
507 | |
508 | static bool VIRTUAL_JoystickIsDevicePresent(Uint16 vendor_id, Uint16 product_id, Uint16 version, const char *name) |
509 | { |
510 | // We don't override any other drivers... or do we? |
511 | return false; |
512 | } |
513 | |
514 | static const char *VIRTUAL_JoystickGetDeviceName(int device_index) |
515 | { |
516 | joystick_hwdata *hwdata = VIRTUAL_HWDataForIndex(device_index); |
517 | if (!hwdata) { |
518 | return NULL; |
519 | } |
520 | return hwdata->name; |
521 | } |
522 | |
523 | static const char *VIRTUAL_JoystickGetDevicePath(int device_index) |
524 | { |
525 | return NULL; |
526 | } |
527 | |
528 | static int VIRTUAL_JoystickGetDeviceSteamVirtualGamepadSlot(int device_index) |
529 | { |
530 | return -1; |
531 | } |
532 | |
533 | static int VIRTUAL_JoystickGetDevicePlayerIndex(int device_index) |
534 | { |
535 | return -1; |
536 | } |
537 | |
538 | static void VIRTUAL_JoystickSetDevicePlayerIndex(int device_index, int player_index) |
539 | { |
540 | joystick_hwdata *hwdata = VIRTUAL_HWDataForIndex(device_index); |
541 | |
542 | if (hwdata && hwdata->desc.SetPlayerIndex) { |
543 | hwdata->desc.SetPlayerIndex(hwdata->desc.userdata, player_index); |
544 | } |
545 | } |
546 | |
547 | static SDL_GUID VIRTUAL_JoystickGetDeviceGUID(int device_index) |
548 | { |
549 | joystick_hwdata *hwdata = VIRTUAL_HWDataForIndex(device_index); |
550 | if (!hwdata) { |
551 | SDL_GUID guid; |
552 | SDL_zero(guid); |
553 | return guid; |
554 | } |
555 | return hwdata->guid; |
556 | } |
557 | |
558 | static SDL_JoystickID VIRTUAL_JoystickGetDeviceInstanceID(int device_index) |
559 | { |
560 | joystick_hwdata *hwdata = VIRTUAL_HWDataForIndex(device_index); |
561 | if (!hwdata) { |
562 | return true; |
563 | } |
564 | return hwdata->instance_id; |
565 | } |
566 | |
567 | static bool VIRTUAL_JoystickOpen(SDL_Joystick *joystick, int device_index) |
568 | { |
569 | joystick_hwdata *hwdata; |
570 | |
571 | SDL_AssertJoysticksLocked(); |
572 | |
573 | hwdata = VIRTUAL_HWDataForIndex(device_index); |
574 | if (!hwdata) { |
575 | return SDL_SetError("No such device" ); |
576 | } |
577 | joystick->hwdata = hwdata; |
578 | joystick->naxes = hwdata->desc.naxes; |
579 | joystick->nbuttons = hwdata->desc.nbuttons; |
580 | joystick->nhats = hwdata->desc.nhats; |
581 | hwdata->joystick = joystick; |
582 | |
583 | for (Uint16 i = 0; i < hwdata->desc.ntouchpads; ++i) { |
584 | const SDL_JoystickTouchpadInfo *touchpad = &hwdata->touchpads[i]; |
585 | SDL_PrivateJoystickAddTouchpad(joystick, touchpad->nfingers); |
586 | } |
587 | for (Uint16 i = 0; i < hwdata->desc.nsensors; ++i) { |
588 | const SDL_JoystickSensorInfo *sensor = &hwdata->sensors[i]; |
589 | SDL_PrivateJoystickAddSensor(joystick, sensor->type, sensor->rate); |
590 | } |
591 | |
592 | if (hwdata->desc.SetLED) { |
593 | SDL_SetBooleanProperty(SDL_GetJoystickProperties(joystick), SDL_PROP_JOYSTICK_CAP_RGB_LED_BOOLEAN, true); |
594 | } |
595 | if (hwdata->desc.Rumble) { |
596 | SDL_SetBooleanProperty(SDL_GetJoystickProperties(joystick), SDL_PROP_JOYSTICK_CAP_RUMBLE_BOOLEAN, true); |
597 | } |
598 | if (hwdata->desc.RumbleTriggers) { |
599 | SDL_SetBooleanProperty(SDL_GetJoystickProperties(joystick), SDL_PROP_JOYSTICK_CAP_TRIGGER_RUMBLE_BOOLEAN, true); |
600 | } |
601 | return true; |
602 | } |
603 | |
604 | static bool VIRTUAL_JoystickRumble(SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble) |
605 | { |
606 | bool result; |
607 | |
608 | SDL_AssertJoysticksLocked(); |
609 | |
610 | if (joystick->hwdata) { |
611 | joystick_hwdata *hwdata = joystick->hwdata; |
612 | if (hwdata->desc.Rumble) { |
613 | result = hwdata->desc.Rumble(hwdata->desc.userdata, low_frequency_rumble, high_frequency_rumble); |
614 | } else { |
615 | result = SDL_Unsupported(); |
616 | } |
617 | } else { |
618 | result = SDL_SetError("Rumble failed, device disconnected" ); |
619 | } |
620 | |
621 | return result; |
622 | } |
623 | |
624 | static bool VIRTUAL_JoystickRumbleTriggers(SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble) |
625 | { |
626 | bool result; |
627 | |
628 | SDL_AssertJoysticksLocked(); |
629 | |
630 | if (joystick->hwdata) { |
631 | joystick_hwdata *hwdata = joystick->hwdata; |
632 | if (hwdata->desc.RumbleTriggers) { |
633 | result = hwdata->desc.RumbleTriggers(hwdata->desc.userdata, left_rumble, right_rumble); |
634 | } else { |
635 | result = SDL_Unsupported(); |
636 | } |
637 | } else { |
638 | result = SDL_SetError("Rumble failed, device disconnected" ); |
639 | } |
640 | |
641 | return result; |
642 | } |
643 | |
644 | static bool VIRTUAL_JoystickSetLED(SDL_Joystick *joystick, Uint8 red, Uint8 green, Uint8 blue) |
645 | { |
646 | bool result; |
647 | |
648 | SDL_AssertJoysticksLocked(); |
649 | |
650 | if (joystick->hwdata) { |
651 | joystick_hwdata *hwdata = joystick->hwdata; |
652 | if (hwdata->desc.SetLED) { |
653 | result = hwdata->desc.SetLED(hwdata->desc.userdata, red, green, blue); |
654 | } else { |
655 | result = SDL_Unsupported(); |
656 | } |
657 | } else { |
658 | result = SDL_SetError("SetLED failed, device disconnected" ); |
659 | } |
660 | |
661 | return result; |
662 | } |
663 | |
664 | static bool VIRTUAL_JoystickSendEffect(SDL_Joystick *joystick, const void *data, int size) |
665 | { |
666 | bool result; |
667 | |
668 | SDL_AssertJoysticksLocked(); |
669 | |
670 | if (joystick->hwdata) { |
671 | joystick_hwdata *hwdata = joystick->hwdata; |
672 | if (hwdata->desc.SendEffect) { |
673 | result = hwdata->desc.SendEffect(hwdata->desc.userdata, data, size); |
674 | } else { |
675 | result = SDL_Unsupported(); |
676 | } |
677 | } else { |
678 | result = SDL_SetError("SendEffect failed, device disconnected" ); |
679 | } |
680 | |
681 | return result; |
682 | } |
683 | |
684 | static bool VIRTUAL_JoystickSetSensorsEnabled(SDL_Joystick *joystick, bool enabled) |
685 | { |
686 | bool result; |
687 | |
688 | SDL_AssertJoysticksLocked(); |
689 | |
690 | if (joystick->hwdata) { |
691 | joystick_hwdata *hwdata = joystick->hwdata; |
692 | if (hwdata->desc.SetSensorsEnabled) { |
693 | result = hwdata->desc.SetSensorsEnabled(hwdata->desc.userdata, enabled); |
694 | } else { |
695 | result = true; |
696 | } |
697 | if (result) { |
698 | hwdata->sensors_enabled = enabled; |
699 | } |
700 | } else { |
701 | result = SDL_SetError("SetSensorsEnabled failed, device disconnected" ); |
702 | } |
703 | |
704 | return result; |
705 | } |
706 | |
707 | static void VIRTUAL_JoystickUpdate(SDL_Joystick *joystick) |
708 | { |
709 | joystick_hwdata *hwdata; |
710 | Uint64 timestamp = SDL_GetTicksNS(); |
711 | |
712 | SDL_AssertJoysticksLocked(); |
713 | |
714 | if (!joystick) { |
715 | return; |
716 | } |
717 | if (!joystick->hwdata) { |
718 | return; |
719 | } |
720 | |
721 | hwdata = (joystick_hwdata *)joystick->hwdata; |
722 | |
723 | if (hwdata->desc.Update) { |
724 | hwdata->desc.Update(hwdata->desc.userdata); |
725 | } |
726 | |
727 | if (hwdata->changes & AXES_CHANGED) { |
728 | for (Uint8 i = 0; i < hwdata->desc.naxes; ++i) { |
729 | SDL_SendJoystickAxis(timestamp, joystick, i, hwdata->axes[i]); |
730 | } |
731 | } |
732 | if (hwdata->changes & BALLS_CHANGED) { |
733 | for (Uint8 i = 0; i < hwdata->desc.nballs; ++i) { |
734 | SDL_JoystickBallData *ball = &hwdata->balls[i]; |
735 | if (ball->dx || ball->dy) { |
736 | SDL_SendJoystickBall(timestamp, joystick, i, (Sint16)ball->dx, (Sint16)ball->dy); |
737 | ball->dx = 0; |
738 | ball->dy = 0; |
739 | } |
740 | } |
741 | } |
742 | if (hwdata->changes & BUTTONS_CHANGED) { |
743 | for (Uint8 i = 0; i < hwdata->desc.nbuttons; ++i) { |
744 | SDL_SendJoystickButton(timestamp, joystick, i, hwdata->buttons[i]); |
745 | } |
746 | } |
747 | if (hwdata->changes & HATS_CHANGED) { |
748 | for (Uint8 i = 0; i < hwdata->desc.nhats; ++i) { |
749 | SDL_SendJoystickHat(timestamp, joystick, i, hwdata->hats[i]); |
750 | } |
751 | } |
752 | if (hwdata->changes & TOUCHPADS_CHANGED) { |
753 | for (Uint16 i = 0; i < hwdata->desc.ntouchpads; ++i) { |
754 | const SDL_JoystickTouchpadInfo *touchpad = &hwdata->touchpads[i]; |
755 | for (int j = 0; j < touchpad->nfingers; ++j) { |
756 | const SDL_JoystickTouchpadFingerInfo *finger = &touchpad->fingers[j]; |
757 | SDL_SendJoystickTouchpad(timestamp, joystick, i, j, finger->down, finger->x, finger->y, finger->pressure); |
758 | } |
759 | } |
760 | } |
761 | if (hwdata->num_sensor_events > 0) { |
762 | if (hwdata->sensors_enabled) { |
763 | for (int i = 0; i < hwdata->num_sensor_events; ++i) { |
764 | const VirtualSensorEvent *event = &hwdata->sensor_events[i]; |
765 | SDL_SendJoystickSensor(timestamp, joystick, event->type, event->sensor_timestamp, event->data, event->num_values); |
766 | } |
767 | } |
768 | hwdata->num_sensor_events = 0; |
769 | } |
770 | hwdata->changes = 0; |
771 | } |
772 | |
773 | static void VIRTUAL_JoystickClose(SDL_Joystick *joystick) |
774 | { |
775 | SDL_AssertJoysticksLocked(); |
776 | |
777 | if (joystick->hwdata) { |
778 | joystick_hwdata *hwdata = joystick->hwdata; |
779 | hwdata->joystick = NULL; |
780 | joystick->hwdata = NULL; |
781 | } |
782 | } |
783 | |
784 | static void VIRTUAL_JoystickQuit(void) |
785 | { |
786 | SDL_AssertJoysticksLocked(); |
787 | |
788 | while (g_VJoys) { |
789 | VIRTUAL_FreeHWData(g_VJoys); |
790 | } |
791 | } |
792 | |
793 | static bool VIRTUAL_JoystickGetGamepadMapping(int device_index, SDL_GamepadMapping *out) |
794 | { |
795 | joystick_hwdata *hwdata = VIRTUAL_HWDataForIndex(device_index); |
796 | Uint8 current_button = 0; |
797 | Uint8 current_axis = 0; |
798 | |
799 | if (!hwdata || hwdata->desc.type != SDL_JOYSTICK_TYPE_GAMEPAD) { |
800 | return false; |
801 | } |
802 | |
803 | if (current_button < hwdata->desc.nbuttons && (hwdata->desc.button_mask & (1 << SDL_GAMEPAD_BUTTON_SOUTH))) { |
804 | out->a.kind = EMappingKind_Button; |
805 | out->a.target = current_button++; |
806 | } |
807 | |
808 | if (current_button < hwdata->desc.nbuttons && (hwdata->desc.button_mask & (1 << SDL_GAMEPAD_BUTTON_EAST))) { |
809 | out->b.kind = EMappingKind_Button; |
810 | out->b.target = current_button++; |
811 | } |
812 | |
813 | if (current_button < hwdata->desc.nbuttons && (hwdata->desc.button_mask & (1 << SDL_GAMEPAD_BUTTON_WEST))) { |
814 | out->x.kind = EMappingKind_Button; |
815 | out->x.target = current_button++; |
816 | } |
817 | |
818 | if (current_button < hwdata->desc.nbuttons && (hwdata->desc.button_mask & (1 << SDL_GAMEPAD_BUTTON_NORTH))) { |
819 | out->y.kind = EMappingKind_Button; |
820 | out->y.target = current_button++; |
821 | } |
822 | |
823 | if (current_button < hwdata->desc.nbuttons && (hwdata->desc.button_mask & (1 << SDL_GAMEPAD_BUTTON_BACK))) { |
824 | out->back.kind = EMappingKind_Button; |
825 | out->back.target = current_button++; |
826 | } |
827 | |
828 | if (current_button < hwdata->desc.nbuttons && (hwdata->desc.button_mask & (1 << SDL_GAMEPAD_BUTTON_GUIDE))) { |
829 | out->guide.kind = EMappingKind_Button; |
830 | out->guide.target = current_button++; |
831 | } |
832 | |
833 | if (current_button < hwdata->desc.nbuttons && (hwdata->desc.button_mask & (1 << SDL_GAMEPAD_BUTTON_START))) { |
834 | out->start.kind = EMappingKind_Button; |
835 | out->start.target = current_button++; |
836 | } |
837 | |
838 | if (current_button < hwdata->desc.nbuttons && (hwdata->desc.button_mask & (1 << SDL_GAMEPAD_BUTTON_LEFT_STICK))) { |
839 | out->leftstick.kind = EMappingKind_Button; |
840 | out->leftstick.target = current_button++; |
841 | } |
842 | |
843 | if (current_button < hwdata->desc.nbuttons && (hwdata->desc.button_mask & (1 << SDL_GAMEPAD_BUTTON_RIGHT_STICK))) { |
844 | out->rightstick.kind = EMappingKind_Button; |
845 | out->rightstick.target = current_button++; |
846 | } |
847 | |
848 | if (current_button < hwdata->desc.nbuttons && (hwdata->desc.button_mask & (1 << SDL_GAMEPAD_BUTTON_LEFT_SHOULDER))) { |
849 | out->leftshoulder.kind = EMappingKind_Button; |
850 | out->leftshoulder.target = current_button++; |
851 | } |
852 | |
853 | if (current_button < hwdata->desc.nbuttons && (hwdata->desc.button_mask & (1 << SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER))) { |
854 | out->rightshoulder.kind = EMappingKind_Button; |
855 | out->rightshoulder.target = current_button++; |
856 | } |
857 | |
858 | if (current_button < hwdata->desc.nbuttons && (hwdata->desc.button_mask & (1 << SDL_GAMEPAD_BUTTON_DPAD_UP))) { |
859 | out->dpup.kind = EMappingKind_Button; |
860 | out->dpup.target = current_button++; |
861 | } |
862 | |
863 | if (current_button < hwdata->desc.nbuttons && (hwdata->desc.button_mask & (1 << SDL_GAMEPAD_BUTTON_DPAD_DOWN))) { |
864 | out->dpdown.kind = EMappingKind_Button; |
865 | out->dpdown.target = current_button++; |
866 | } |
867 | |
868 | if (current_button < hwdata->desc.nbuttons && (hwdata->desc.button_mask & (1 << SDL_GAMEPAD_BUTTON_DPAD_LEFT))) { |
869 | out->dpleft.kind = EMappingKind_Button; |
870 | out->dpleft.target = current_button++; |
871 | } |
872 | |
873 | if (current_button < hwdata->desc.nbuttons && (hwdata->desc.button_mask & (1 << SDL_GAMEPAD_BUTTON_DPAD_RIGHT))) { |
874 | out->dpright.kind = EMappingKind_Button; |
875 | out->dpright.target = current_button++; |
876 | } |
877 | |
878 | if (current_button < hwdata->desc.nbuttons && (hwdata->desc.button_mask & (1 << SDL_GAMEPAD_BUTTON_MISC1))) { |
879 | out->misc1.kind = EMappingKind_Button; |
880 | out->misc1.target = current_button++; |
881 | } |
882 | |
883 | if (current_button < hwdata->desc.nbuttons && (hwdata->desc.button_mask & (1 << SDL_GAMEPAD_BUTTON_RIGHT_PADDLE1))) { |
884 | out->right_paddle1.kind = EMappingKind_Button; |
885 | out->right_paddle1.target = current_button++; |
886 | } |
887 | |
888 | if (current_button < hwdata->desc.nbuttons && (hwdata->desc.button_mask & (1 << SDL_GAMEPAD_BUTTON_LEFT_PADDLE1))) { |
889 | out->left_paddle1.kind = EMappingKind_Button; |
890 | out->left_paddle1.target = current_button++; |
891 | } |
892 | |
893 | if (current_button < hwdata->desc.nbuttons && (hwdata->desc.button_mask & (1 << SDL_GAMEPAD_BUTTON_RIGHT_PADDLE2))) { |
894 | out->right_paddle2.kind = EMappingKind_Button; |
895 | out->right_paddle2.target = current_button++; |
896 | } |
897 | |
898 | if (current_button < hwdata->desc.nbuttons && (hwdata->desc.button_mask & (1 << SDL_GAMEPAD_BUTTON_LEFT_PADDLE2))) { |
899 | out->left_paddle2.kind = EMappingKind_Button; |
900 | out->left_paddle2.target = current_button++; |
901 | } |
902 | |
903 | if (current_button < hwdata->desc.nbuttons && (hwdata->desc.button_mask & (1 << SDL_GAMEPAD_BUTTON_TOUCHPAD))) { |
904 | out->touchpad.kind = EMappingKind_Button; |
905 | out->touchpad.target = current_button++; |
906 | } |
907 | |
908 | if (current_button < hwdata->desc.nbuttons && (hwdata->desc.button_mask & (1 << SDL_GAMEPAD_BUTTON_MISC2))) { |
909 | out->misc2.kind = EMappingKind_Button; |
910 | out->misc2.target = current_button++; |
911 | } |
912 | |
913 | if (current_button < hwdata->desc.nbuttons && (hwdata->desc.button_mask & (1 << SDL_GAMEPAD_BUTTON_MISC3))) { |
914 | out->misc3.kind = EMappingKind_Button; |
915 | out->misc3.target = current_button++; |
916 | } |
917 | |
918 | if (current_button < hwdata->desc.nbuttons && (hwdata->desc.button_mask & (1 << SDL_GAMEPAD_BUTTON_MISC4))) { |
919 | out->misc4.kind = EMappingKind_Button; |
920 | out->misc4.target = current_button++; |
921 | } |
922 | |
923 | if (current_button < hwdata->desc.nbuttons && (hwdata->desc.button_mask & (1 << SDL_GAMEPAD_BUTTON_MISC5))) { |
924 | out->misc5.kind = EMappingKind_Button; |
925 | out->misc5.target = current_button++; |
926 | } |
927 | |
928 | if (current_button < hwdata->desc.nbuttons && (hwdata->desc.button_mask & (1 << SDL_GAMEPAD_BUTTON_MISC6))) { |
929 | out->misc6.kind = EMappingKind_Button; |
930 | out->misc6.target = current_button++; |
931 | } |
932 | |
933 | if (current_axis < hwdata->desc.naxes && (hwdata->desc.axis_mask & (1 << SDL_GAMEPAD_AXIS_LEFTX))) { |
934 | out->leftx.kind = EMappingKind_Axis; |
935 | out->leftx.target = current_axis++; |
936 | } |
937 | |
938 | if (current_axis < hwdata->desc.naxes && (hwdata->desc.axis_mask & (1 << SDL_GAMEPAD_AXIS_LEFTY))) { |
939 | out->lefty.kind = EMappingKind_Axis; |
940 | out->lefty.target = current_axis++; |
941 | } |
942 | |
943 | if (current_axis < hwdata->desc.naxes && (hwdata->desc.axis_mask & (1 << SDL_GAMEPAD_AXIS_RIGHTX))) { |
944 | out->rightx.kind = EMappingKind_Axis; |
945 | out->rightx.target = current_axis++; |
946 | } |
947 | |
948 | if (current_axis < hwdata->desc.naxes && (hwdata->desc.axis_mask & (1 << SDL_GAMEPAD_AXIS_RIGHTY))) { |
949 | out->righty.kind = EMappingKind_Axis; |
950 | out->righty.target = current_axis++; |
951 | } |
952 | |
953 | if (current_axis < hwdata->desc.naxes && (hwdata->desc.axis_mask & (1 << SDL_GAMEPAD_AXIS_LEFT_TRIGGER))) { |
954 | out->lefttrigger.kind = EMappingKind_Axis; |
955 | out->lefttrigger.target = current_axis++; |
956 | } |
957 | |
958 | if (current_axis < hwdata->desc.naxes && (hwdata->desc.axis_mask & (1 << SDL_GAMEPAD_AXIS_RIGHT_TRIGGER))) { |
959 | out->righttrigger.kind = EMappingKind_Axis; |
960 | out->righttrigger.target = current_axis++; |
961 | } |
962 | |
963 | return true; |
964 | } |
965 | |
966 | SDL_JoystickDriver SDL_VIRTUAL_JoystickDriver = { |
967 | VIRTUAL_JoystickInit, |
968 | VIRTUAL_JoystickGetCount, |
969 | VIRTUAL_JoystickDetect, |
970 | VIRTUAL_JoystickIsDevicePresent, |
971 | VIRTUAL_JoystickGetDeviceName, |
972 | VIRTUAL_JoystickGetDevicePath, |
973 | VIRTUAL_JoystickGetDeviceSteamVirtualGamepadSlot, |
974 | VIRTUAL_JoystickGetDevicePlayerIndex, |
975 | VIRTUAL_JoystickSetDevicePlayerIndex, |
976 | VIRTUAL_JoystickGetDeviceGUID, |
977 | VIRTUAL_JoystickGetDeviceInstanceID, |
978 | VIRTUAL_JoystickOpen, |
979 | VIRTUAL_JoystickRumble, |
980 | VIRTUAL_JoystickRumbleTriggers, |
981 | VIRTUAL_JoystickSetLED, |
982 | VIRTUAL_JoystickSendEffect, |
983 | VIRTUAL_JoystickSetSensorsEnabled, |
984 | VIRTUAL_JoystickUpdate, |
985 | VIRTUAL_JoystickClose, |
986 | VIRTUAL_JoystickQuit, |
987 | VIRTUAL_JoystickGetGamepadMapping |
988 | }; |
989 | |
990 | #endif // SDL_JOYSTICK_VIRTUAL |
991 | |