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
31static joystick_hwdata *g_VJoys SDL_GUARDED_BY(SDL_joystick_lock) = NULL;
32
33static 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
47static 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
62static 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
132SDL_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
330bool 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
341bool 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
362bool 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
386bool 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
407bool 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
428bool 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
456bool 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
486static bool VIRTUAL_JoystickInit(void)
487{
488 return true;
489}
490
491static 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
504static void VIRTUAL_JoystickDetect(void)
505{
506}
507
508static 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
514static 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
523static const char *VIRTUAL_JoystickGetDevicePath(int device_index)
524{
525 return NULL;
526}
527
528static int VIRTUAL_JoystickGetDeviceSteamVirtualGamepadSlot(int device_index)
529{
530 return -1;
531}
532
533static int VIRTUAL_JoystickGetDevicePlayerIndex(int device_index)
534{
535 return -1;
536}
537
538static 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
547static 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
558static 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
567static 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
604static 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
624static 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
644static 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
664static 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
684static 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
707static 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
773static 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
784static void VIRTUAL_JoystickQuit(void)
785{
786 SDL_AssertJoysticksLocked();
787
788 while (g_VJoys) {
789 VIRTUAL_FreeHWData(g_VJoys);
790 }
791}
792
793static 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
966SDL_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