SDL  2.0
SDL_sysjoystick.c
Go to the documentation of this file.
1 /*
2  Simple DirectMedia Layer
3  Copyright (C) 1997-2018 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 #include "SDL_joystick.h"
31 #include "SDL_assert.h"
32 #include "SDL_timer.h"
33 #include "SDL_log.h"
34 #include "SDL_sysjoystick_c.h"
35 #include "../SDL_joystick_c.h"
36 
37 static SDL_joylist_item * JoystickByIndex(int index);
38 
39 static SDL_joylist_item *SDL_joylist = NULL;
40 static SDL_joylist_item *SDL_joylist_tail = NULL;
41 static int numjoysticks = 0;
42 static int instance_counter = 0;
43 
44 static EM_BOOL
45 Emscripten_JoyStickConnected(int eventType, const EmscriptenGamepadEvent *gamepadEvent, void *userData)
46 {
47  int i;
48 
49  SDL_joylist_item *item;
50 
51  if (JoystickByIndex(gamepadEvent->index) != NULL) {
52  return 1;
53  }
54 
55  item = (SDL_joylist_item *) SDL_malloc(sizeof (SDL_joylist_item));
56  if (item == NULL) {
57  return 1;
58  }
59 
60  SDL_zerop(item);
61  item->index = gamepadEvent->index;
62 
63  item->name = SDL_strdup(gamepadEvent->id);
64  if ( item->name == NULL ) {
65  SDL_free(item);
66  return 1;
67  }
68 
69  item->mapping = SDL_strdup(gamepadEvent->mapping);
70  if ( item->mapping == NULL ) {
71  SDL_free(item->name);
72  SDL_free(item);
73  return 1;
74  }
75 
76  item->naxes = gamepadEvent->numAxes;
77  item->nbuttons = gamepadEvent->numButtons;
78  item->device_instance = instance_counter++;
79 
80  item->timestamp = gamepadEvent->timestamp;
81 
82  for( i = 0; i < item->naxes; i++) {
83  item->axis[i] = gamepadEvent->axis[i];
84  }
85 
86  for( i = 0; i < item->nbuttons; i++) {
87  item->analogButton[i] = gamepadEvent->analogButton[i];
88  item->digitalButton[i] = gamepadEvent->digitalButton[i];
89  }
90 
91  if (SDL_joylist_tail == NULL) {
92  SDL_joylist = SDL_joylist_tail = item;
93  } else {
94  SDL_joylist_tail->next = item;
95  SDL_joylist_tail = item;
96  }
97 
98  ++numjoysticks;
99 
101 
102 #ifdef DEBUG_JOYSTICK
103  SDL_Log("Number of joysticks is %d", numjoysticks);
104 #endif
105 
106 #ifdef DEBUG_JOYSTICK
107  SDL_Log("Added joystick with index %d", item->index);
108 #endif
109 
110  return 1;
111 }
112 
113 static EM_BOOL
114 Emscripten_JoyStickDisconnected(int eventType, const EmscriptenGamepadEvent *gamepadEvent, void *userData)
115 {
116  SDL_joylist_item *item = SDL_joylist;
117  SDL_joylist_item *prev = NULL;
118 
119  while (item != NULL) {
120  if (item->index == gamepadEvent->index) {
121  break;
122  }
123  prev = item;
124  item = item->next;
125  }
126 
127  if (item == NULL) {
128  return 1;
129  }
130 
131  if (item->joystick) {
132  item->joystick->hwdata = NULL;
133  }
134 
135  if (prev != NULL) {
136  prev->next = item->next;
137  } else {
138  SDL_assert(SDL_joylist == item);
139  SDL_joylist = item->next;
140  }
141  if (item == SDL_joylist_tail) {
142  SDL_joylist_tail = prev;
143  }
144 
145  /* Need to decrement the joystick count before we post the event */
146  --numjoysticks;
147 
148  SDL_PrivateJoystickRemoved(item->device_instance);
149 
150 #ifdef DEBUG_JOYSTICK
151  SDL_Log("Removed joystick with id %d", item->device_instance);
152 #endif
153  SDL_free(item->name);
154  SDL_free(item->mapping);
155  SDL_free(item);
156  return 1;
157 }
158 
159 /* Function to perform any system-specific joystick related cleanup */
160 static void
161 EMSCRIPTEN_JoystickQuit(void)
162 {
163  SDL_joylist_item *item = NULL;
164  SDL_joylist_item *next = NULL;
165 
166  for (item = SDL_joylist; item; item = next) {
167  next = item->next;
168  SDL_free(item->mapping);
169  SDL_free(item->name);
170  SDL_free(item);
171  }
172 
173  SDL_joylist = SDL_joylist_tail = NULL;
174 
175  numjoysticks = 0;
176  instance_counter = 0;
177 
178  emscripten_set_gamepadconnected_callback(NULL, 0, NULL);
179  emscripten_set_gamepaddisconnected_callback(NULL, 0, NULL);
180 }
181 
182 /* Function to scan the system for joysticks.
183  * It should return 0, or -1 on an unrecoverable fatal error.
184  */
185 static int
186 EMSCRIPTEN_JoystickInit(void)
187 {
188  int retval, i, numjs;
189  EmscriptenGamepadEvent gamepadState;
190 
191  numjoysticks = 0;
192  numjs = emscripten_get_num_gamepads();
193 
194  /* Check if gamepad is supported by browser */
195  if (numjs == EMSCRIPTEN_RESULT_NOT_SUPPORTED) {
196  return SDL_SetError("Gamepads not supported");
197  }
198 
199  /* handle already connected gamepads */
200  if (numjs > 0) {
201  for(i = 0; i < numjs; i++) {
202  retval = emscripten_get_gamepad_status(i, &gamepadState);
203  if (retval == EMSCRIPTEN_RESULT_SUCCESS) {
204  Emscripten_JoyStickConnected(EMSCRIPTEN_EVENT_GAMEPADCONNECTED,
205  &gamepadState,
206  NULL);
207  }
208  }
209  }
210 
211  retval = emscripten_set_gamepadconnected_callback(NULL,
212  0,
213  Emscripten_JoyStickConnected);
214 
215  if(retval != EMSCRIPTEN_RESULT_SUCCESS) {
216  EMSCRIPTEN_JoystickQuit();
217  return SDL_SetError("Could not set gamepad connect callback");
218  }
219 
220  retval = emscripten_set_gamepaddisconnected_callback(NULL,
221  0,
222  Emscripten_JoyStickDisconnected);
223  if(retval != EMSCRIPTEN_RESULT_SUCCESS) {
224  EMSCRIPTEN_JoystickQuit();
225  return SDL_SetError("Could not set gamepad disconnect callback");
226  }
227 
228  return 0;
229 }
230 
231 /* Returns item matching given SDL device index. */
232 static SDL_joylist_item *
233 JoystickByDeviceIndex(int device_index)
234 {
235  SDL_joylist_item *item = SDL_joylist;
236 
237  while (0 < device_index) {
238  --device_index;
239  item = item->next;
240  }
241 
242  return item;
243 }
244 
245 /* Returns item matching given HTML gamepad index. */
246 static SDL_joylist_item *
247 JoystickByIndex(int index)
248 {
249  SDL_joylist_item *item = SDL_joylist;
250 
251  if (index < 0) {
252  return NULL;
253  }
254 
255  while (item != NULL) {
256  if (item->index == index) {
257  break;
258  }
259  item = item->next;
260  }
261 
262  return item;
263 }
264 
265 static int
266 EMSCRIPTEN_JoystickGetCount(void)
267 {
268  return numjoysticks;
269 }
270 
271 static void
272 EMSCRIPTEN_JoystickDetect(void)
273 {
274 }
275 
276 static const char *
277 EMSCRIPTEN_JoystickGetDeviceName(int device_index)
278 {
279  return JoystickByDeviceIndex(device_index)->name;
280 }
281 
282 static int
283 EMSCRIPTEN_JoystickGetDevicePlayerIndex(int device_index)
284 {
285  return -1;
286 }
287 
288 static SDL_JoystickID
289 EMSCRIPTEN_JoystickGetDeviceInstanceID(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 static int
300 EMSCRIPTEN_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 update the state of a joystick - called as a device poll.
327  * This function shouldn't update the joystick structure directly,
328  * but instead should call SDL_PrivateJoystick*() to deliver events
329  * and update joystick device state.
330  */
331 static void
332 EMSCRIPTEN_JoystickUpdate(SDL_Joystick * joystick)
333 {
334  EmscriptenGamepadEvent gamepadState;
335  SDL_joylist_item *item = (SDL_joylist_item *) joystick->hwdata;
336  int i, result, buttonState;
337 
338  if (item) {
339  result = emscripten_get_gamepad_status(item->index, &gamepadState);
340  if( result == EMSCRIPTEN_RESULT_SUCCESS) {
341  if(gamepadState.timestamp == 0 || gamepadState.timestamp != item->timestamp) {
342  for(i = 0; i < item->nbuttons; i++) {
343  if(item->digitalButton[i] != gamepadState.digitalButton[i]) {
344  buttonState = gamepadState.digitalButton[i]? SDL_PRESSED: SDL_RELEASED;
345  SDL_PrivateJoystickButton(item->joystick, i, buttonState);
346  }
347 
348  /* store values to compare them in the next update */
349  item->analogButton[i] = gamepadState.analogButton[i];
350  item->digitalButton[i] = gamepadState.digitalButton[i];
351  }
352 
353  for(i = 0; i < item->naxes; i++) {
354  if(item->axis[i] != gamepadState.axis[i]) {
355  /* do we need to do conversion? */
356  SDL_PrivateJoystickAxis(item->joystick, i,
357  (Sint16) (32767.*gamepadState.axis[i]));
358  }
359 
360  /* store to compare in next update */
361  item->axis[i] = gamepadState.axis[i];
362  }
363 
364  item->timestamp = gamepadState.timestamp;
365  }
366  }
367  }
368 }
369 
370 /* Function to close a joystick after use */
371 static void
372 EMSCRIPTEN_JoystickClose(SDL_Joystick * joystick)
373 {
374  SDL_joylist_item *item = (SDL_joylist_item *) joystick->hwdata;
375  if (item) {
376  item->joystick = NULL;
377  }
378 }
379 
380 static SDL_JoystickGUID
381 EMSCRIPTEN_JoystickGetDeviceGUID(int device_index)
382 {
384  /* the GUID is just the first 16 chars of the name for now */
385  const char *name = EMSCRIPTEN_JoystickGetDeviceName(device_index);
386  SDL_zero(guid);
387  SDL_memcpy(&guid, name, SDL_min(sizeof(guid), SDL_strlen(name)));
388  return guid;
389 }
390 
391 static int
392 EMSCRIPTEN_JoystickRumble(SDL_Joystick * joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms)
393 {
394  return SDL_Unsupported();
395 }
396 
398 {
399  EMSCRIPTEN_JoystickInit,
400  EMSCRIPTEN_JoystickGetCount,
401  EMSCRIPTEN_JoystickDetect,
402  EMSCRIPTEN_JoystickGetDeviceName,
403  EMSCRIPTEN_JoystickGetDevicePlayerIndex,
404  EMSCRIPTEN_JoystickGetDeviceGUID,
405  EMSCRIPTEN_JoystickGetDeviceInstanceID,
406  EMSCRIPTEN_JoystickOpen,
407  EMSCRIPTEN_JoystickRumble,
408  EMSCRIPTEN_JoystickUpdate,
409  EMSCRIPTEN_JoystickClose,
410  EMSCRIPTEN_JoystickQuit,
411 };
412 
413 #endif /* SDL_JOYSTICK_EMSCRIPTEN */
414 
415 /* vi: set ts=4 sw=4 expandtab: */
#define SDL_min(x, y)
Definition: SDL_stdinc.h:406
void SDL_PrivateJoystickRemoved(SDL_JoystickID device_instance)
Definition: SDL_joystick.c:800
GLuint64EXT * result
SDL_JoystickGUID guid
SDL_Joystick * joystick
int SDL_PrivateJoystickButton(SDL_Joystick *joystick, Uint8 button, Uint8 state)
Definition: SDL_joystick.c:961
uint16_t Uint16
Definition: SDL_stdinc.h:191
#define SDL_zerop(x)
Definition: SDL_stdinc.h:417
int SDL_PrivateJoystickAxis(SDL_Joystick *joystick, Uint8 axis, Sint16 value)
Definition: SDL_joystick.c:828
GLuint const GLchar * name
return Display return Display Bool Bool int int int return Display XEvent Bool(*) XPointer return Display return Display Drawable _Xconst char unsigned int unsigned int return Display Pixmap Pixmap XColor XColor unsigned int unsigned int return Display _Xconst char char int char return Display Visual unsigned int int int char unsigned int unsigned int in i)
Definition: SDL_x11sym.h:50
SDL_bool retval
#define SDL_Log
#define SDL_memcpy
Sint32 SDL_JoystickID
Definition: SDL_joystick.h:81
#define SDL_free
#define SDL_zero(x)
Definition: SDL_stdinc.h:416
GLuint index
#define SDL_assert(condition)
Definition: SDL_assert.h:169
void SDL_PrivateJoystickAdded(SDL_JoystickID device_instance)
Definition: SDL_joystick.c:751
#define NULL
Definition: begin_code.h:164
#define SDL_SetError
#define SDL_strlen
#define SDL_strdup
uint32_t Uint32
Definition: SDL_stdinc.h:203
static int numjoysticks
#define SDL_malloc
#define SDL_PRESSED
Definition: SDL_events.h:50
#define SDL_RELEASED
Definition: SDL_events.h:49
SDL_JoystickDriver SDL_EMSCRIPTEN_JoystickDriver
#define SDL_Unsupported()
Definition: SDL_error.h:53
int16_t Sint16
Definition: SDL_stdinc.h:185