1 /*
2   Simple DirectMedia Layer
3   Copyright (C) 1997-2020 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 #if defined(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 extern SDL_JoystickDriver SDL_VIRTUAL_JoystickDriver;
32 
33 static joystick_hwdata * g_VJoys = NULL;
34 
35 
36 static joystick_hwdata *
VIRTUAL_HWDataForIndex(int device_index)37 VIRTUAL_HWDataForIndex(int device_index)
38 {
39     joystick_hwdata *vjoy = g_VJoys;
40     while (vjoy) {
41         if (device_index == 0)
42             break;
43         --device_index;
44         vjoy = vjoy->next;
45     }
46     return vjoy;
47 }
48 
49 
50 static void
VIRTUAL_FreeHWData(joystick_hwdata * hwdata)51 VIRTUAL_FreeHWData(joystick_hwdata *hwdata)
52 {
53     joystick_hwdata * cur = g_VJoys;
54     joystick_hwdata * prev = NULL;
55 
56     if (!hwdata) {
57         return;
58     }
59     if (hwdata->axes) {
60         SDL_free((void *)hwdata->axes);
61         hwdata->axes = NULL;
62     }
63     if (hwdata->buttons) {
64         SDL_free((void *)hwdata->buttons);
65         hwdata->buttons = NULL;
66     }
67     if (hwdata->hats) {
68         SDL_free(hwdata->hats);
69         hwdata->hats = NULL;
70     }
71 
72     /* Remove hwdata from SDL-global list */
73     while (cur) {
74         if (hwdata == cur) {
75             if (prev) {
76                 prev->next = cur->next;
77             } else {
78                 g_VJoys = cur->next;
79             }
80             break;
81         }
82         prev = cur;
83         cur = cur->next;
84     }
85 
86     SDL_free(hwdata);
87 }
88 
89 
90 int
SDL_JoystickAttachVirtualInner(SDL_JoystickType type,int naxes,int nbuttons,int nhats)91 SDL_JoystickAttachVirtualInner(SDL_JoystickType type,
92                                int naxes,
93                                int nbuttons,
94                                int nhats)
95 {
96     joystick_hwdata *hwdata = NULL;
97     int device_index = -1;
98 
99     hwdata = SDL_calloc(1, sizeof(joystick_hwdata));
100     if (!hwdata) {
101         VIRTUAL_FreeHWData(hwdata);
102         return SDL_OutOfMemory();
103     }
104 
105     hwdata->naxes = naxes;
106     hwdata->nbuttons = nbuttons;
107     hwdata->nhats = nhats;
108     hwdata->name = "Virtual Joystick";
109 
110     /* Note that this is a Virtual device and what subtype it is */
111     hwdata->guid.data[14] = 'v';
112     hwdata->guid.data[15] = (Uint8)type;
113 
114     /* Allocate fields for different control-types */
115     if (naxes > 0) {
116         hwdata->axes = SDL_calloc(naxes, sizeof(Sint16));
117         if (!hwdata->axes) {
118             VIRTUAL_FreeHWData(hwdata);
119             return SDL_OutOfMemory();
120         }
121     }
122     if (nbuttons > 0) {
123         hwdata->buttons = SDL_calloc(nbuttons, sizeof(Uint8));
124         if (!hwdata->buttons) {
125             VIRTUAL_FreeHWData(hwdata);
126             return SDL_OutOfMemory();
127         }
128     }
129     if (nhats > 0) {
130         hwdata->hats = SDL_calloc(nhats, sizeof(Uint8));
131         if (!hwdata->hats) {
132             VIRTUAL_FreeHWData(hwdata);
133             return SDL_OutOfMemory();
134         }
135     }
136 
137     /* Allocate an instance ID for this device */
138     hwdata->instance_id = SDL_GetNextJoystickInstanceID();
139 
140     /* Add virtual joystick to SDL-global lists */
141     hwdata->next = g_VJoys;
142     g_VJoys = hwdata;
143     SDL_PrivateJoystickAdded(hwdata->instance_id);
144 
145     /* Return the new virtual-device's index */
146     device_index = SDL_JoystickGetDeviceIndexFromInstanceID(hwdata->instance_id);
147     return device_index;
148 }
149 
150 
151 int
SDL_JoystickDetachVirtualInner(int device_index)152 SDL_JoystickDetachVirtualInner(int device_index)
153 {
154     SDL_JoystickID instance_id;
155     joystick_hwdata *hwdata = VIRTUAL_HWDataForIndex(device_index);
156     if (!hwdata) {
157         return SDL_SetError("Virtual joystick data not found");
158     }
159     instance_id = hwdata->instance_id;
160     VIRTUAL_FreeHWData(hwdata);
161     SDL_PrivateJoystickRemoved(instance_id);
162     return 0;
163 }
164 
165 
166 int
SDL_JoystickSetVirtualAxisInner(SDL_Joystick * joystick,int axis,Sint16 value)167 SDL_JoystickSetVirtualAxisInner(SDL_Joystick * joystick, int axis, Sint16 value)
168 {
169     joystick_hwdata *hwdata;
170 
171     SDL_LockJoysticks();
172 
173     if (!joystick || !joystick->hwdata) {
174         SDL_UnlockJoysticks();
175         return SDL_SetError("Invalid joystick");
176     }
177 
178     hwdata = (joystick_hwdata *)joystick->hwdata;
179     if (axis < 0 || axis >= hwdata->nbuttons) {
180         SDL_UnlockJoysticks();
181         return SDL_SetError("Invalid axis index");
182     }
183 
184     hwdata->axes[axis] = value;
185 
186     SDL_UnlockJoysticks();
187     return 0;
188 }
189 
190 
191 int
SDL_JoystickSetVirtualButtonInner(SDL_Joystick * joystick,int button,Uint8 value)192 SDL_JoystickSetVirtualButtonInner(SDL_Joystick * joystick, int button, Uint8 value)
193 {
194     joystick_hwdata *hwdata;
195 
196     SDL_LockJoysticks();
197 
198     if (!joystick || !joystick->hwdata) {
199         SDL_UnlockJoysticks();
200         return SDL_SetError("Invalid joystick");
201     }
202 
203     hwdata = (joystick_hwdata *)joystick->hwdata;
204     if (button < 0 || button >= hwdata->nbuttons) {
205         SDL_UnlockJoysticks();
206         return SDL_SetError("Invalid button index");
207     }
208 
209     hwdata->buttons[button] = value;
210 
211     SDL_UnlockJoysticks();
212     return 0;
213 }
214 
215 
216 int
SDL_JoystickSetVirtualHatInner(SDL_Joystick * joystick,int hat,Uint8 value)217 SDL_JoystickSetVirtualHatInner(SDL_Joystick * joystick, int hat, Uint8 value)
218 {
219     joystick_hwdata *hwdata;
220 
221     SDL_LockJoysticks();
222 
223     if (!joystick || !joystick->hwdata) {
224         SDL_UnlockJoysticks();
225         return SDL_SetError("Invalid joystick");
226     }
227 
228     hwdata = (joystick_hwdata *)joystick->hwdata;
229     if (hat < 0 || hat >= hwdata->nbuttons) {
230         SDL_UnlockJoysticks();
231         return SDL_SetError("Invalid hat index");
232     }
233 
234     hwdata->hats[hat] = value;
235 
236     SDL_UnlockJoysticks();
237     return 0;
238 }
239 
240 
241 static int
VIRTUAL_JoystickInit(void)242 VIRTUAL_JoystickInit(void)
243 {
244     return 0;
245 }
246 
247 
248 static int
VIRTUAL_JoystickGetCount(void)249 VIRTUAL_JoystickGetCount(void)
250 {
251     int count = 0;
252     joystick_hwdata *cur = g_VJoys;
253     while (cur) {
254         ++count;
255         cur = cur->next;
256     }
257     return count;
258 }
259 
260 
261 static void
VIRTUAL_JoystickDetect(void)262 VIRTUAL_JoystickDetect(void)
263 {
264 }
265 
266 
267 static const char *
VIRTUAL_JoystickGetDeviceName(int device_index)268 VIRTUAL_JoystickGetDeviceName(int device_index)
269 {
270     joystick_hwdata *hwdata = VIRTUAL_HWDataForIndex(device_index);
271     if (!hwdata) {
272         return NULL;
273     }
274     return hwdata->name ? hwdata->name : "";
275 }
276 
277 
278 static int
VIRTUAL_JoystickGetDevicePlayerIndex(int device_index)279 VIRTUAL_JoystickGetDevicePlayerIndex(int device_index)
280 {
281     return -1;
282 }
283 
284 
285 static void
VIRTUAL_JoystickSetDevicePlayerIndex(int device_index,int player_index)286 VIRTUAL_JoystickSetDevicePlayerIndex(int device_index, int player_index)
287 {
288 }
289 
290 
291 static SDL_JoystickGUID
VIRTUAL_JoystickGetDeviceGUID(int device_index)292 VIRTUAL_JoystickGetDeviceGUID(int device_index)
293 {
294     joystick_hwdata *hwdata = VIRTUAL_HWDataForIndex(device_index);
295     if (!hwdata) {
296         SDL_JoystickGUID guid;
297         SDL_zero(guid);
298         return guid;
299     }
300     return hwdata->guid;
301 }
302 
303 
304 static SDL_JoystickID
VIRTUAL_JoystickGetDeviceInstanceID(int device_index)305 VIRTUAL_JoystickGetDeviceInstanceID(int device_index)
306 {
307     joystick_hwdata *hwdata = VIRTUAL_HWDataForIndex(device_index);
308     if (!hwdata) {
309         return -1;
310     }
311     return hwdata->instance_id;
312 }
313 
314 
315 static int
VIRTUAL_JoystickOpen(SDL_Joystick * joystick,int device_index)316 VIRTUAL_JoystickOpen(SDL_Joystick * joystick, int device_index)
317 {
318     joystick_hwdata *hwdata = VIRTUAL_HWDataForIndex(device_index);
319     if (!hwdata) {
320         return SDL_SetError("No such device");
321     }
322     if (hwdata->opened) {
323         return SDL_SetError("Joystick already opened");
324     }
325     joystick->instance_id = hwdata->instance_id;
326     joystick->hwdata = hwdata;
327     joystick->naxes = hwdata->naxes;
328     joystick->nbuttons = hwdata->nbuttons;
329     joystick->nhats = hwdata->nhats;
330     hwdata->opened = SDL_TRUE;
331     return 0;
332 }
333 
334 
335 static int
VIRTUAL_JoystickRumble(SDL_Joystick * joystick,Uint16 low_frequency_rumble,Uint16 high_frequency_rumble)336 VIRTUAL_JoystickRumble(SDL_Joystick * joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble)
337 {
338     return SDL_Unsupported();
339 }
340 
341 
342 static void
VIRTUAL_JoystickUpdate(SDL_Joystick * joystick)343 VIRTUAL_JoystickUpdate(SDL_Joystick * joystick)
344 {
345     joystick_hwdata *hwdata;
346     int i;
347 
348     if (!joystick) {
349         return;
350     }
351     if (!joystick->hwdata) {
352         return;
353     }
354 
355     hwdata = (joystick_hwdata *)joystick->hwdata;
356 
357     for (i = 0; i < hwdata->naxes; ++i) {
358         SDL_PrivateJoystickAxis(joystick, i, hwdata->axes[i]);
359     }
360     for (i = 0; i < hwdata->nbuttons; ++i) {
361         SDL_PrivateJoystickButton(joystick, i, hwdata->buttons[i]);
362     }
363     for (i = 0; i < hwdata->nhats; ++i) {
364         SDL_PrivateJoystickHat(joystick, i, hwdata->hats[i]);
365     }
366 }
367 
368 
369 static void
VIRTUAL_JoystickClose(SDL_Joystick * joystick)370 VIRTUAL_JoystickClose(SDL_Joystick * joystick)
371 {
372     joystick_hwdata *hwdata;
373 
374     if (!joystick) {
375         return;
376     }
377     if (!joystick->hwdata) {
378         return;
379     }
380 
381     hwdata = (joystick_hwdata *)joystick->hwdata;
382     hwdata->opened = SDL_FALSE;
383 }
384 
385 
386 static void
VIRTUAL_JoystickQuit(void)387 VIRTUAL_JoystickQuit(void)
388 {
389     while (g_VJoys) {
390         VIRTUAL_FreeHWData(g_VJoys);
391     }
392 }
393 
394 static SDL_bool
VIRTUAL_JoystickGetGamepadMapping(int device_index,SDL_GamepadMapping * out)395 VIRTUAL_JoystickGetGamepadMapping(int device_index, SDL_GamepadMapping *out)
396 {
397     return SDL_FALSE;
398 }
399 
400 SDL_JoystickDriver SDL_VIRTUAL_JoystickDriver =
401 {
402     VIRTUAL_JoystickInit,
403     VIRTUAL_JoystickGetCount,
404     VIRTUAL_JoystickDetect,
405     VIRTUAL_JoystickGetDeviceName,
406     VIRTUAL_JoystickGetDevicePlayerIndex,
407     VIRTUAL_JoystickSetDevicePlayerIndex,
408     VIRTUAL_JoystickGetDeviceGUID,
409     VIRTUAL_JoystickGetDeviceInstanceID,
410     VIRTUAL_JoystickOpen,
411     VIRTUAL_JoystickRumble,
412     VIRTUAL_JoystickUpdate,
413     VIRTUAL_JoystickClose,
414     VIRTUAL_JoystickQuit,
415     VIRTUAL_JoystickGetGamepadMapping
416 };
417 
418 #endif /* SDL_JOYSTICK_VIRTUAL || SDL_JOYSTICK_DISABLED */
419 
420 /* vi: set ts=4 sw=4 expandtab: */
421