change SDL 1.2 to SDL 2.0
[platform/upstream/SDL.git] / src / joystick / emscripten / SDL_sysjoystick.c
1 /*
2   Simple DirectMedia Layer
3   Copyright (C) 1997-2016 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
22 #include "../../SDL_internal.h"
23
24 #ifdef SDL_JOYSTICK_EMSCRIPTEN
25
26 #include <stdio.h>              /* For the definition of NULL */
27 #include "SDL_error.h"
28 #include "SDL_events.h"
29
30 #if !SDL_EVENTS_DISABLED
31 #include "../../events/SDL_events_c.h"
32 #endif
33
34 #include "SDL_joystick.h"
35 #include "SDL_hints.h"
36 #include "SDL_assert.h"
37 #include "SDL_timer.h"
38 #include "SDL_log.h"
39 #include "SDL_sysjoystick_c.h"
40 #include "../SDL_joystick_c.h"
41
42 static SDL_joylist_item * JoystickByIndex(int index);
43
44 static SDL_joylist_item *SDL_joylist = NULL;
45 static SDL_joylist_item *SDL_joylist_tail = NULL;
46 static int numjoysticks = 0;
47 static int instance_counter = 0;
48
49 EM_BOOL
50 Emscripten_JoyStickConnected(int eventType, const EmscriptenGamepadEvent *gamepadEvent, void *userData)
51 {
52     int i;
53
54     SDL_joylist_item *item;
55
56     if (JoystickByIndex(gamepadEvent->index) != NULL) {
57       return 1;
58     }
59
60 #if !SDL_EVENTS_DISABLED
61     SDL_Event event;
62 #endif
63
64     item = (SDL_joylist_item *) SDL_malloc(sizeof (SDL_joylist_item));
65     if (item == NULL) {
66         return 1;
67     }
68
69     SDL_zerop(item);
70     item->index = gamepadEvent->index;
71
72     item->name = SDL_strdup(gamepadEvent->id);
73     if ( item->name == NULL ) {
74         SDL_free(item);
75         return 1;
76     }
77
78     item->mapping = SDL_strdup(gamepadEvent->mapping);
79     if ( item->mapping == NULL ) {
80         SDL_free(item->name);
81         SDL_free(item);
82         return 1;
83     }
84
85     item->naxes = gamepadEvent->numAxes;
86     item->nbuttons = gamepadEvent->numButtons;
87     item->device_instance = instance_counter++;
88
89     item->timestamp = gamepadEvent->timestamp;
90
91     for( i = 0; i < item->naxes; i++) {
92         item->axis[i] = gamepadEvent->axis[i];
93     }
94
95     for( i = 0; i < item->nbuttons; i++) {
96         item->analogButton[i] = gamepadEvent->analogButton[i];
97         item->digitalButton[i] = gamepadEvent->digitalButton[i];
98     }
99
100     if (SDL_joylist_tail == NULL) {
101         SDL_joylist = SDL_joylist_tail = item;
102     } else {
103         SDL_joylist_tail->next = item;
104         SDL_joylist_tail = item;
105     }
106
107     ++numjoysticks;
108 #ifdef DEBUG_JOYSTICK
109     SDL_Log("Number of joysticks is %d", numjoysticks);
110 #endif
111 #if !SDL_EVENTS_DISABLED
112     event.type = SDL_JOYDEVICEADDED;
113
114     if (SDL_GetEventState(event.type) == SDL_ENABLE) {
115         event.jdevice.which = numjoysticks - 1;
116         if ( (SDL_EventOK == NULL) ||
117              (*SDL_EventOK) (SDL_EventOKParam, &event) ) {
118             SDL_PushEvent(&event);
119         }
120     }
121 #endif /* !SDL_EVENTS_DISABLED */
122
123 #ifdef DEBUG_JOYSTICK
124     SDL_Log("Added joystick with index %d", item->index);
125 #endif
126
127     return 1;
128 }
129
130 EM_BOOL
131 Emscripten_JoyStickDisconnected(int eventType, const EmscriptenGamepadEvent *gamepadEvent, void *userData)
132 {
133     SDL_joylist_item *item = SDL_joylist;
134     SDL_joylist_item *prev = NULL;
135 #if !SDL_EVENTS_DISABLED
136     SDL_Event event;
137 #endif
138
139     while (item != NULL) {
140         if (item->index == gamepadEvent->index) {
141             break;
142         }
143         prev = item;
144         item = item->next;
145     }
146
147     if (item == NULL) {
148         return 1;
149     }
150
151     if (item->joystick) {
152         item->joystick->hwdata = NULL;
153     }
154
155     if (prev != NULL) {
156         prev->next = item->next;
157     } else {
158         SDL_assert(SDL_joylist == item);
159         SDL_joylist = item->next;
160     }
161     if (item == SDL_joylist_tail) {
162         SDL_joylist_tail = prev;
163     }
164
165     /* Need to decrement the joystick count before we post the event */
166     --numjoysticks;
167
168 #if !SDL_EVENTS_DISABLED
169     event.type = SDL_JOYDEVICEREMOVED;
170
171     if (SDL_GetEventState(event.type) == SDL_ENABLE) {
172         event.jdevice.which = item->device_instance;
173         if ( (SDL_EventOK == NULL) ||
174              (*SDL_EventOK) (SDL_EventOKParam, &event) ) {
175             SDL_PushEvent(&event);
176         }
177     }
178 #endif /* !SDL_EVENTS_DISABLED */
179
180 #ifdef DEBUG_JOYSTICK
181     SDL_Log("Removed joystick with id %d", item->device_instance);
182 #endif
183     SDL_free(item->name);
184     SDL_free(item->mapping);
185     SDL_free(item);
186     return 1;
187 }
188
189 /* Function to scan the system for joysticks.
190  * It should return 0, or -1 on an unrecoverable fatal error.
191  */
192 int
193 SDL_SYS_JoystickInit(void)
194 {
195     int retval, i, numjs;
196     EmscriptenGamepadEvent gamepadState;
197
198     numjoysticks = 0;
199     numjs = emscripten_get_num_gamepads();
200
201     /* Check if gamepad is supported by browser */
202     if (numjs == EMSCRIPTEN_RESULT_NOT_SUPPORTED) {
203         return SDL_SetError("Gamepads not supported");
204     }
205
206     /* handle already connected gamepads */
207     if (numjs > 0) {
208         for(i = 0; i < numjs; i++) {
209             retval = emscripten_get_gamepad_status(i, &gamepadState);
210             if (retval == EMSCRIPTEN_RESULT_SUCCESS) {
211                 Emscripten_JoyStickConnected(EMSCRIPTEN_EVENT_GAMEPADCONNECTED,
212                                              &gamepadState,
213                                              NULL);
214             }
215         }
216     }
217
218     retval = emscripten_set_gamepadconnected_callback(NULL,
219                                                       0,
220                                                       Emscripten_JoyStickConnected);
221
222     if(retval != EMSCRIPTEN_RESULT_SUCCESS) {
223         SDL_SYS_JoystickQuit();
224         return SDL_SetError("Could not set gamepad connect callback");
225     }
226
227     retval = emscripten_set_gamepaddisconnected_callback(NULL,
228                                                          0,
229                                                          Emscripten_JoyStickDisconnected);
230     if(retval != EMSCRIPTEN_RESULT_SUCCESS) {
231         SDL_SYS_JoystickQuit();
232         return SDL_SetError("Could not set gamepad disconnect callback");
233     }
234
235     return 0;
236 }
237
238 /* Returns item matching given SDL device index. */
239 static SDL_joylist_item *
240 JoystickByDeviceIndex(int device_index)
241 {
242     SDL_joylist_item *item = SDL_joylist;
243
244     while (0 < device_index) {
245         --device_index;
246         item = item->next;
247     }
248
249     return item;
250 }
251
252 /* Returns item matching given HTML gamepad index. */
253 static SDL_joylist_item *
254 JoystickByIndex(int index)
255 {
256     SDL_joylist_item *item = SDL_joylist;
257
258     if (index < 0) {
259         return NULL;
260     }
261
262     while (item != NULL) {
263         if (item->index == index) {
264             break;
265         }
266         item = item->next;
267     }
268
269     return item;
270 }
271
272 int SDL_SYS_NumJoysticks()
273 {
274     return numjoysticks;
275 }
276
277 void SDL_SYS_JoystickDetect()
278 {
279 }
280
281 /* Function to get the device-dependent name of a joystick */
282 const char *
283 SDL_SYS_JoystickNameForDeviceIndex(int device_index)
284 {
285     return JoystickByDeviceIndex(device_index)->name;
286 }
287
288 /* Function to perform the mapping from device index to the instance id for this index */
289 SDL_JoystickID SDL_SYS_GetInstanceIdOfDeviceIndex(int device_index)
290 {
291     return JoystickByDeviceIndex(device_index)->device_instance;
292 }
293
294 /* Function to open a joystick for use.
295    The joystick to open is specified by the device index.
296    This should fill the nbuttons and naxes fields of the joystick structure.
297    It returns 0, or -1 if there is an error.
298  */
299 int
300 SDL_SYS_JoystickOpen(SDL_Joystick * joystick, int device_index)
301 {
302     SDL_joylist_item *item = JoystickByDeviceIndex(device_index);
303
304     if (item == NULL ) {
305         return SDL_SetError("No such device");
306     }
307
308     if (item->joystick != NULL) {
309         return SDL_SetError("Joystick already opened");
310     }
311
312     joystick->instance_id = item->device_instance;
313     joystick->hwdata = (struct joystick_hwdata *) item;
314     item->joystick = joystick;
315
316     /* HTML5 Gamepad API doesn't say anything about these */
317     joystick->nhats = 0;
318     joystick->nballs = 0;
319
320     joystick->nbuttons = item->nbuttons;
321     joystick->naxes = item->naxes;
322
323     return (0);
324 }
325
326 /* Function to determine if this joystick is attached to the system right now */
327 SDL_bool SDL_SYS_JoystickAttached(SDL_Joystick *joystick)
328 {
329     return joystick->hwdata != NULL;
330 }
331
332 /* Function to update the state of a joystick - called as a device poll.
333  * This function shouldn't update the joystick structure directly,
334  * but instead should call SDL_PrivateJoystick*() to deliver events
335  * and update joystick device state.
336  */
337 void
338 SDL_SYS_JoystickUpdate(SDL_Joystick * joystick)
339 {
340     EmscriptenGamepadEvent gamepadState;
341     SDL_joylist_item *item = (SDL_joylist_item *) joystick->hwdata;
342     int i, result, buttonState;
343
344     if (item) {
345         result = emscripten_get_gamepad_status(item->index, &gamepadState);
346         if( result == EMSCRIPTEN_RESULT_SUCCESS) {
347             if(gamepadState.timestamp == 0 || gamepadState.timestamp != item->timestamp) {
348                 for(i = 0; i < item->nbuttons; i++) {
349                     if(item->digitalButton[i] != gamepadState.digitalButton[i]) {
350                         buttonState = gamepadState.digitalButton[i]? SDL_PRESSED: SDL_RELEASED;
351                         SDL_PrivateJoystickButton(item->joystick, i, buttonState);
352                     }
353
354                     /* store values to compare them in the next update */
355                     item->analogButton[i] = gamepadState.analogButton[i];
356                     item->digitalButton[i] = gamepadState.digitalButton[i];
357                 }
358
359                 for(i = 0; i < item->naxes; i++) {
360                     if(item->axis[i] != gamepadState.axis[i]) {
361                         // do we need to do conversion?
362                         SDL_PrivateJoystickAxis(item->joystick, i,
363                                                   (Sint16) (32767.*gamepadState.axis[i]));
364                     }
365
366                     /* store to compare in next update */
367                     item->axis[i] = gamepadState.axis[i];
368                 }
369
370                 item->timestamp = gamepadState.timestamp;
371             }
372         }
373     }
374 }
375
376 /* Function to close a joystick after use */
377 void
378 SDL_SYS_JoystickClose(SDL_Joystick * joystick)
379 {
380 }
381
382 /* Function to perform any system-specific joystick related cleanup */
383 void
384 SDL_SYS_JoystickQuit(void)
385 {
386     SDL_joylist_item *item = NULL;
387     SDL_joylist_item *next = NULL;
388
389     for (item = SDL_joylist; item; item = next) {
390         next = item->next;
391         SDL_free(item->mapping);
392         SDL_free(item->name);
393         SDL_free(item);
394     }
395
396     SDL_joylist = SDL_joylist_tail = NULL;
397
398     numjoysticks = 0;
399     instance_counter = 0;
400
401     emscripten_set_gamepadconnected_callback(NULL, 0, NULL);
402     emscripten_set_gamepaddisconnected_callback(NULL, 0, NULL);
403 }
404
405 SDL_JoystickGUID
406 SDL_SYS_JoystickGetDeviceGUID(int device_index)
407 {
408     SDL_JoystickGUID guid;
409     /* the GUID is just the first 16 chars of the name for now */
410     const char *name = SDL_SYS_JoystickNameForDeviceIndex(device_index);
411     SDL_zero(guid);
412     SDL_memcpy(&guid, name, SDL_min(sizeof(guid), SDL_strlen(name)));
413     return guid;
414 }
415
416 SDL_JoystickGUID
417 SDL_SYS_JoystickGetGUID(SDL_Joystick * joystick)
418 {
419     SDL_JoystickGUID guid;
420     /* the GUID is just the first 16 chars of the name for now */
421     const char *name = joystick->name;
422     SDL_zero(guid);
423     SDL_memcpy(&guid, name, SDL_min(sizeof(guid), SDL_strlen(name)));
424     return guid;
425 }
426
427 #endif /* SDL_JOYSTICK_EMSCRIPTEN */