SDL  2.0
SDL_sysjoystick.m
Go to the documentation of this file.
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 /* This is the iOS implementation of the SDL joystick API */
24 #include "SDL_sysjoystick_c.h"
25 
26 /* needed for SDL_IPHONE_MAX_GFORCE macro */
27 #include "../../../include/SDL_config_iphoneos.h"
28 
29 #include "SDL_assert.h"
30 #include "SDL_events.h"
31 #include "SDL_joystick.h"
32 #include "SDL_hints.h"
33 #include "SDL_stdinc.h"
34 #include "../SDL_sysjoystick.h"
35 #include "../SDL_joystick_c.h"
36 
37 
38 #if !SDL_EVENTS_DISABLED
39 #include "../../events/SDL_events_c.h"
40 #endif
41 
42 #if !TARGET_OS_TV
43 #import <CoreMotion/CoreMotion.h>
44 #endif
45 
46 #ifdef SDL_JOYSTICK_MFI
47 #import <GameController/GameController.h>
48 
49 static id connectObserver = nil;
50 static id disconnectObserver = nil;
51 
52 #include <Availability.h>
53 #include <objc/message.h>
54 
55 /* remove compilation warnings for strict builds by defining these selectors, even though
56  * they are only ever used indirectly through objc_msgSend
57  */
59 #if (__IPHONE_OS_VERSION_MAX_ALLOWED < 121000) || (__APPLETV_OS_VERSION_MAX_ALLOWED < 121000) || (__MAC_OS_VERSION_MAX_ALLOWED < 1401000)
60 @property (nonatomic, readonly, nullable) GCControllerButtonInput *leftThumbstickButton;
61 @property (nonatomic, readonly, nullable) GCControllerButtonInput *rightThumbstickButton;
62 #endif
63 #if (__IPHONE_OS_VERSION_MAX_ALLOWED < 130000) || (__APPLETV_OS_VERSION_MAX_ALLOWED < 130000) || (__MAC_OS_VERSION_MAX_ALLOWED < 1500000)
64 @property (nonatomic, readonly) GCControllerButtonInput *buttonMenu;
65 @property (nonatomic, readonly, nullable) GCControllerButtonInput *buttonOptions;
66 #endif
67 @end
69 #if (__IPHONE_OS_VERSION_MAX_ALLOWED < 130000) || (__APPLETV_OS_VERSION_MAX_ALLOWED < 130000) || (__MAC_OS_VERSION_MAX_ALLOWED < 1500000)
70 @property (nonatomic, readonly) GCControllerButtonInput *buttonMenu;
71 #endif
72 @end
73 
74 #endif /* SDL_JOYSTICK_MFI */
75 
76 #if !TARGET_OS_TV
77 static const char *accelerometerName = "iOS Accelerometer";
78 static CMMotionManager *motionManager = nil;
79 #endif /* !TARGET_OS_TV */
80 
82 
83 static int numjoysticks = 0;
85 
87 GetDeviceForIndex(int device_index)
88 {
90  int i = 0;
91 
92  while (i < device_index) {
93  if (device == NULL) {
94  return NULL;
95  }
96  device = device->next;
97  i++;
98  }
99 
100  return device;
101 }
102 
103 static void
105 {
106 #ifdef SDL_JOYSTICK_MFI
107  const Uint16 VENDOR_APPLE = 0x05AC;
108  const Uint16 VENDOR_MICROSOFT = 0x045e;
109  const Uint16 VENDOR_SONY = 0x054C;
110  Uint16 *guid16 = (Uint16 *)device->guid.data;
111  Uint16 vendor = 0;
112  Uint16 product = 0;
113  Uint8 subtype = 0;
114 
115  const char *name = NULL;
116  /* Explicitly retain the controller because SDL_JoystickDeviceItem is a
117  * struct, and ARC doesn't work with structs. */
118  device->controller = (__bridge GCController *) CFBridgingRetain(controller);
119 
120  if (controller.vendorName) {
121  name = controller.vendorName.UTF8String;
122  }
123 
124  if (!name) {
125  name = "MFi Gamepad";
126  }
127 
128  device->name = SDL_strdup(name);
129 
130  if (controller.extendedGamepad) {
131  GCExtendedGamepad *gamepad = controller.extendedGamepad;
132  BOOL is_xbox = [controller.vendorName containsString: @"Xbox"];
133  BOOL is_ps4 = [controller.vendorName containsString: @"DUALSHOCK"];
134 #if TARGET_OS_TV
135  BOOL is_MFi = (!is_xbox && !is_ps4);
136 #endif
137  int nbuttons = 0;
138 
139  /* These buttons are part of the original MFi spec */
140  device->button_mask |= (1 << SDL_CONTROLLER_BUTTON_A);
141  device->button_mask |= (1 << SDL_CONTROLLER_BUTTON_B);
142  device->button_mask |= (1 << SDL_CONTROLLER_BUTTON_X);
143  device->button_mask |= (1 << SDL_CONTROLLER_BUTTON_Y);
144  device->button_mask |= (1 << SDL_CONTROLLER_BUTTON_LEFTSHOULDER);
145  device->button_mask |= (1 << SDL_CONTROLLER_BUTTON_RIGHTSHOULDER);
146  nbuttons += 6;
147 
148  /* These buttons are available on some newer controllers */
149 #pragma clang diagnostic push
150 #pragma clang diagnostic ignored "-Wunguarded-availability-new"
151  if ([gamepad respondsToSelector:@selector(leftThumbstickButton)] && gamepad.leftThumbstickButton) {
152  device->button_mask |= (1 << SDL_CONTROLLER_BUTTON_LEFTSTICK);
153  ++nbuttons;
154  }
155  if ([gamepad respondsToSelector:@selector(rightThumbstickButton)] && gamepad.rightThumbstickButton) {
156  device->button_mask |= (1 << SDL_CONTROLLER_BUTTON_RIGHTSTICK);
157  ++nbuttons;
158  }
159  if ([gamepad respondsToSelector:@selector(buttonOptions)] && gamepad.buttonOptions) {
160  device->button_mask |= (1 << SDL_CONTROLLER_BUTTON_BACK);
161  ++nbuttons;
162  }
163  BOOL has_direct_menu = [gamepad respondsToSelector:@selector(buttonMenu)] && gamepad.buttonMenu;
164 #if TARGET_OS_TV
165  /* On tvOS MFi controller menu button brings you to the home screen */
166  if (is_MFi) {
167  has_direct_menu = FALSE;
168  }
169 #endif
170  device->button_mask |= (1 << SDL_CONTROLLER_BUTTON_START);
171  ++nbuttons;
172  if (!has_direct_menu) {
173  device->uses_pause_handler = SDL_TRUE;
174  }
175 #pragma clang diagnostic pop
176 
177  if (is_xbox) {
178  vendor = VENDOR_MICROSOFT;
179  product = 0x02E0; /* Assume Xbox One S BLE Controller unless/until GCController flows VID/PID */
180  } else if (is_ps4) {
181  vendor = VENDOR_SONY;
182  product = 0x09CC; /* Assume DS4 Slim unless/until GCController flows VID/PID */
183  } else {
184  vendor = VENDOR_APPLE;
185  product = 1;
186  subtype = 1;
187  }
188 
189  device->naxes = 6; /* 2 thumbsticks and 2 triggers */
190  device->nhats = 1; /* d-pad */
191  device->nbuttons = nbuttons;
192 
193  } else if (controller.gamepad) {
194  int nbuttons = 0;
195 
196  /* These buttons are part of the original MFi spec */
197  device->button_mask |= (1 << SDL_CONTROLLER_BUTTON_A);
198  device->button_mask |= (1 << SDL_CONTROLLER_BUTTON_B);
199  device->button_mask |= (1 << SDL_CONTROLLER_BUTTON_X);
200  device->button_mask |= (1 << SDL_CONTROLLER_BUTTON_Y);
201  device->button_mask |= (1 << SDL_CONTROLLER_BUTTON_LEFTSHOULDER);
202  device->button_mask |= (1 << SDL_CONTROLLER_BUTTON_RIGHTSHOULDER);
203  device->button_mask |= (1 << SDL_CONTROLLER_BUTTON_START);
204  nbuttons += 7;
205  device->uses_pause_handler = SDL_TRUE;
206 
207  vendor = VENDOR_APPLE;
208  product = 2;
209  subtype = 2;
210  device->naxes = 0; /* no traditional analog inputs */
211  device->nhats = 1; /* d-pad */
212  device->nbuttons = nbuttons;
213  }
214 #if TARGET_OS_TV
215  else if (controller.microGamepad) {
216  int nbuttons = 0;
217 
218  device->button_mask |= (1 << SDL_CONTROLLER_BUTTON_A);
219  device->button_mask |= (1 << SDL_CONTROLLER_BUTTON_B); /* Button X on microGamepad */
220  nbuttons += 2;
221 
222  device->button_mask |= (1 << SDL_CONTROLLER_BUTTON_START);
223  ++nbuttons;
224  device->uses_pause_handler = SDL_TRUE;
225 
226  vendor = VENDOR_APPLE;
227  product = 3;
228  subtype = 3;
229  device->naxes = 2; /* treat the touch surface as two axes */
230  device->nhats = 0; /* apparently the touch surface-as-dpad is buggy */
231  device->nbuttons = nbuttons;
232 
233  controller.microGamepad.allowsRotation = SDL_GetHintBoolean(SDL_HINT_APPLE_TV_REMOTE_ALLOW_ROTATION, SDL_FALSE);
234  }
235 #endif /* TARGET_OS_TV */
236 
237  /* We only need 16 bits for each of these; space them out to fill 128. */
238  /* Byteswap so devices get same GUID on little/big endian platforms. */
240  *guid16++ = 0;
241  *guid16++ = SDL_SwapLE16(vendor);
242  *guid16++ = 0;
243  *guid16++ = SDL_SwapLE16(product);
244  *guid16++ = 0;
245 
246  *guid16++ = SDL_SwapLE16(device->button_mask);
247 
248  if (subtype != 0) {
249  /* Note that this is an MFI controller and what subtype it is */
250  device->guid.data[14] = 'm';
251  device->guid.data[15] = subtype;
252  }
253 
254  /* This will be set when the first button press of the controller is
255  * detected. */
256  controller.playerIndex = -1;
257 
258 #endif /* SDL_JOYSTICK_MFI */
259 }
260 
261 static void
262 IOS_AddJoystickDevice(GCController *controller, SDL_bool accelerometer)
263 {
265 
266 #if TARGET_OS_TV
268  /* Ignore devices that aren't actually controllers (e.g. remotes), they'll be handled as keyboard input */
269  if (controller && !controller.extendedGamepad && !controller.gamepad && controller.microGamepad) {
270  return;
271  }
272  }
273 #endif
274 
275  while (device != NULL) {
276  if (device->controller == controller) {
277  return;
278  }
279  device = device->next;
280  }
281 
283  if (device == NULL) {
284  return;
285  }
286 
287  device->accelerometer = accelerometer;
288  device->instance_id = SDL_GetNextJoystickInstanceID();
289 
290  if (accelerometer) {
291 #if TARGET_OS_TV
292  SDL_free(device);
293  return;
294 #else
296  device->naxes = 3; /* Device acceleration in the x, y, and z axes. */
297  device->nhats = 0;
298  device->nbuttons = 0;
299 
300  /* Use the accelerometer name as a GUID. */
301  SDL_memcpy(&device->guid.data, device->name, SDL_min(sizeof(SDL_JoystickGUID), SDL_strlen(device->name)));
302 #endif /* TARGET_OS_TV */
303  } else if (controller) {
304  IOS_AddMFIJoystickDevice(device, controller);
305  }
306 
307  if (deviceList == NULL) {
308  deviceList = device;
309  } else {
310  SDL_JoystickDeviceItem *lastdevice = deviceList;
311  while (lastdevice->next != NULL) {
312  lastdevice = lastdevice->next;
313  }
314  lastdevice->next = device;
315  }
316 
317  ++numjoysticks;
318 
319  SDL_PrivateJoystickAdded(device->instance_id);
320 }
321 
322 static SDL_JoystickDeviceItem *
324 {
328 
329  if (device == NULL) {
330  return NULL;
331  }
332 
333  next = device->next;
334 
335  while (item != NULL) {
336  if (item == device) {
337  break;
338  }
339  prev = item;
340  item = item->next;
341  }
342 
343  /* Unlink the device item from the device list. */
344  if (prev) {
345  prev->next = device->next;
346  } else if (device == deviceList) {
347  deviceList = device->next;
348  }
349 
350  if (device->joystick) {
351  device->joystick->hwdata = NULL;
352  }
353 
354 #ifdef SDL_JOYSTICK_MFI
355  @autoreleasepool {
356  if (device->controller) {
357  /* The controller was explicitly retained in the struct, so it
358  * should be explicitly released before freeing the struct. */
359  GCController *controller = CFBridgingRelease((__bridge CFTypeRef)(device->controller));
360  controller.controllerPausedHandler = nil;
361  device->controller = nil;
362  }
363  }
364 #endif /* SDL_JOYSTICK_MFI */
365 
366  --numjoysticks;
367 
368  SDL_PrivateJoystickRemoved(device->instance_id);
369 
370  SDL_free(device->name);
371  SDL_free(device);
372 
373  return next;
374 }
375 
376 #if TARGET_OS_TV
377 static void SDLCALL
378 SDL_AppleTVRemoteRotationHintChanged(void *udata, const char *name, const char *oldValue, const char *newValue)
379 {
380  BOOL allowRotation = newValue != NULL && *newValue != '0';
381 
382  @autoreleasepool {
383  for (GCController *controller in [GCController controllers]) {
384  if (controller.microGamepad) {
385  controller.microGamepad.allowsRotation = allowRotation;
386  }
387  }
388  }
389 }
390 #endif /* TARGET_OS_TV */
391 
392 static int
394 {
395  @autoreleasepool {
396  NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
397 
398 #if !TARGET_OS_TV
400  /* Default behavior, accelerometer as joystick */
402  }
403 #endif /* !TARGET_OS_TV */
404 
405 #ifdef SDL_JOYSTICK_MFI
406  /* GameController.framework was added in iOS 7. */
407  if (![GCController class]) {
408  return 0;
409  }
410 
411  for (GCController *controller in [GCController controllers]) {
412  IOS_AddJoystickDevice(controller, SDL_FALSE);
413  }
414 
415 #if TARGET_OS_TV
417  SDL_AppleTVRemoteRotationHintChanged, NULL);
418 #endif /* TARGET_OS_TV */
419 
420  connectObserver = [center addObserverForName:GCControllerDidConnectNotification
421  object:nil
422  queue:nil
423  usingBlock:^(NSNotification *note) {
424  GCController *controller = note.object;
425  IOS_AddJoystickDevice(controller, SDL_FALSE);
426  }];
427 
428  disconnectObserver = [center addObserverForName:GCControllerDidDisconnectNotification
429  object:nil
430  queue:nil
431  usingBlock:^(NSNotification *note) {
432  GCController *controller = note.object;
433  SDL_JoystickDeviceItem *device = deviceList;
434  while (device != NULL) {
435  if (device->controller == controller) {
436  IOS_RemoveJoystickDevice(device);
437  break;
438  }
439  device = device->next;
440  }
441  }];
442 #endif /* SDL_JOYSTICK_MFI */
443  }
444 
445  return 0;
446 }
447 
448 static int
450 {
451  return numjoysticks;
452 }
453 
454 static void
456 {
457 }
458 
459 static const char *
460 IOS_JoystickGetDeviceName(int device_index)
461 {
463  return device ? device->name : "Unknown";
464 }
465 
466 static int
468 {
470  return device ? (int)device->controller.playerIndex : -1;
471 }
472 
473 static void
474 IOS_JoystickSetDevicePlayerIndex(int device_index, int player_index)
475 {
477  if (device) {
478  device->controller.playerIndex = player_index;
479  }
480 }
481 
482 static SDL_JoystickGUID
483 IOS_JoystickGetDeviceGUID( int device_index )
484 {
486  SDL_JoystickGUID guid;
487  if (device) {
488  guid = device->guid;
489  } else {
490  SDL_zero(guid);
491  }
492  return guid;
493 }
494 
495 static SDL_JoystickID
497 {
499  return device ? device->instance_id : -1;
500 }
501 
502 static int
503 IOS_JoystickOpen(SDL_Joystick * joystick, int device_index)
504 {
506  if (device == NULL) {
507  return SDL_SetError("Could not open Joystick: no hardware device for the specified index");
508  }
509 
510  joystick->hwdata = device;
511  joystick->instance_id = device->instance_id;
512 
513  joystick->naxes = device->naxes;
514  joystick->nhats = device->nhats;
515  joystick->nbuttons = device->nbuttons;
516  joystick->nballs = 0;
517 
518  device->joystick = joystick;
519 
520  @autoreleasepool {
521  if (device->accelerometer) {
522 #if !TARGET_OS_TV
523  if (motionManager == nil) {
524  motionManager = [[CMMotionManager alloc] init];
525  }
526 
527  /* Shorter times between updates can significantly increase CPU usage. */
528  motionManager.accelerometerUpdateInterval = 0.1;
529  [motionManager startAccelerometerUpdates];
530 #endif /* !TARGET_OS_TV */
531  } else {
532 #ifdef SDL_JOYSTICK_MFI
533  if (device->uses_pause_handler) {
534  GCController *controller = device->controller;
535  controller.controllerPausedHandler = ^(GCController *c) {
536  if (joystick->hwdata) {
537  ++joystick->hwdata->num_pause_presses;
538  }
539  };
540  }
541 #endif /* SDL_JOYSTICK_MFI */
542  }
543  }
544  if (device->remote) {
546  }
547 
548  return 0;
549 }
550 
551 static void
552 IOS_AccelerometerUpdate(SDL_Joystick * joystick)
553 {
554 #if !TARGET_OS_TV
555  const float maxgforce = SDL_IPHONE_MAX_GFORCE;
556  const SInt16 maxsint16 = 0x7FFF;
557  CMAcceleration accel;
558 
559  @autoreleasepool {
560  if (!motionManager.isAccelerometerActive) {
561  return;
562  }
563 
564  accel = motionManager.accelerometerData.acceleration;
565  }
566 
567  /*
568  Convert accelerometer data from floating point to Sint16, which is what
569  the joystick system expects.
570 
571  To do the conversion, the data is first clamped onto the interval
572  [-SDL_IPHONE_MAX_G_FORCE, SDL_IPHONE_MAX_G_FORCE], then the data is multiplied
573  by MAX_SINT16 so that it is mapped to the full range of an Sint16.
574 
575  You can customize the clamped range of this function by modifying the
576  SDL_IPHONE_MAX_GFORCE macro in SDL_config_iphoneos.h.
577 
578  Once converted to Sint16, the accelerometer data no longer has coherent
579  units. You can convert the data back to units of g-force by multiplying
580  it in your application's code by SDL_IPHONE_MAX_GFORCE / 0x7FFF.
581  */
582 
583  /* clamp the data */
584  accel.x = SDL_min(SDL_max(accel.x, -maxgforce), maxgforce);
585  accel.y = SDL_min(SDL_max(accel.y, -maxgforce), maxgforce);
586  accel.z = SDL_min(SDL_max(accel.z, -maxgforce), maxgforce);
587 
588  /* pass in data mapped to range of SInt16 */
589  SDL_PrivateJoystickAxis(joystick, 0, (accel.x / maxgforce) * maxsint16);
590  SDL_PrivateJoystickAxis(joystick, 1, -(accel.y / maxgforce) * maxsint16);
591  SDL_PrivateJoystickAxis(joystick, 2, (accel.z / maxgforce) * maxsint16);
592 #endif /* !TARGET_OS_TV */
593 }
594 
595 #ifdef SDL_JOYSTICK_MFI
596 static Uint8
597 IOS_MFIJoystickHatStateForDPad(GCControllerDirectionPad *dpad)
598 {
599  Uint8 hat = 0;
600 
601  if (dpad.up.isPressed) {
602  hat |= SDL_HAT_UP;
603  } else if (dpad.down.isPressed) {
604  hat |= SDL_HAT_DOWN;
605  }
606 
607  if (dpad.left.isPressed) {
608  hat |= SDL_HAT_LEFT;
609  } else if (dpad.right.isPressed) {
610  hat |= SDL_HAT_RIGHT;
611  }
612 
613  if (hat == 0) {
614  return SDL_HAT_CENTERED;
615  }
616 
617  return hat;
618 }
619 #endif
620 
621 static void
622 IOS_MFIJoystickUpdate(SDL_Joystick * joystick)
623 {
624 #if SDL_JOYSTICK_MFI
625  @autoreleasepool {
626  GCController *controller = joystick->hwdata->controller;
627  Uint8 hatstate = SDL_HAT_CENTERED;
628  int i;
629  int pause_button_index = 0;
630 
631  if (controller.extendedGamepad) {
632  GCExtendedGamepad *gamepad = controller.extendedGamepad;
633 
634  /* Axis order matches the XInput Windows mappings. */
635  Sint16 axes[] = {
636  (Sint16) (gamepad.leftThumbstick.xAxis.value * 32767),
637  (Sint16) (gamepad.leftThumbstick.yAxis.value * -32767),
638  (Sint16) ((gamepad.leftTrigger.value * 65535) - 32768),
639  (Sint16) (gamepad.rightThumbstick.xAxis.value * 32767),
640  (Sint16) (gamepad.rightThumbstick.yAxis.value * -32767),
641  (Sint16) ((gamepad.rightTrigger.value * 65535) - 32768),
642  };
643 
644  /* Button order matches the XInput Windows mappings. */
645  Uint8 buttons[joystick->nbuttons];
646  int button_count = 0;
647 
648  /* These buttons are part of the original MFi spec */
649  buttons[button_count++] = gamepad.buttonA.isPressed;
650  buttons[button_count++] = gamepad.buttonB.isPressed;
651  buttons[button_count++] = gamepad.buttonX.isPressed;
652  buttons[button_count++] = gamepad.buttonY.isPressed;
653  buttons[button_count++] = gamepad.leftShoulder.isPressed;
654  buttons[button_count++] = gamepad.rightShoulder.isPressed;
655 
656  /* These buttons are available on some newer controllers */
657 #pragma clang diagnostic push
658 #pragma clang diagnostic ignored "-Wunguarded-availability-new"
659  if (joystick->hwdata->button_mask & (1 << SDL_CONTROLLER_BUTTON_LEFTSTICK)) {
660  buttons[button_count++] = gamepad.leftThumbstickButton.isPressed;
661  }
662  if (joystick->hwdata->button_mask & (1 << SDL_CONTROLLER_BUTTON_RIGHTSTICK)) {
663  buttons[button_count++] = gamepad.rightThumbstickButton.isPressed;
664  }
665  if (joystick->hwdata->button_mask & (1 << SDL_CONTROLLER_BUTTON_BACK)) {
666  buttons[button_count++] = gamepad.buttonOptions.isPressed;
667  }
668  /* This must be the last button, so we can optionally handle it with pause_button_index below */
669  if (joystick->hwdata->button_mask & (1 << SDL_CONTROLLER_BUTTON_START)) {
670  if (joystick->hwdata->uses_pause_handler) {
671  pause_button_index = button_count;
672  buttons[button_count++] = joystick->delayed_guide_button;
673  } else {
674  buttons[button_count++] = gamepad.buttonMenu.isPressed;
675  }
676  }
677 #pragma clang diagnostic pop
678 
679  hatstate = IOS_MFIJoystickHatStateForDPad(gamepad.dpad);
680 
681  for (i = 0; i < SDL_arraysize(axes); i++) {
682  SDL_PrivateJoystickAxis(joystick, i, axes[i]);
683  }
684 
685  for (i = 0; i < button_count; i++) {
686  SDL_PrivateJoystickButton(joystick, i, buttons[i]);
687  }
688  } else if (controller.gamepad) {
689  GCGamepad *gamepad = controller.gamepad;
690 
691  /* Button order matches the XInput Windows mappings. */
692  Uint8 buttons[joystick->nbuttons];
693  int button_count = 0;
694  buttons[button_count++] = gamepad.buttonA.isPressed;
695  buttons[button_count++] = gamepad.buttonB.isPressed;
696  buttons[button_count++] = gamepad.buttonX.isPressed;
697  buttons[button_count++] = gamepad.buttonY.isPressed;
698  buttons[button_count++] = gamepad.leftShoulder.isPressed;
699  buttons[button_count++] = gamepad.rightShoulder.isPressed;
700  pause_button_index = button_count;
701  buttons[button_count++] = joystick->delayed_guide_button;
702 
703  hatstate = IOS_MFIJoystickHatStateForDPad(gamepad.dpad);
704 
705  for (i = 0; i < button_count; i++) {
706  SDL_PrivateJoystickButton(joystick, i, buttons[i]);
707  }
708  }
709 #if TARGET_OS_TV
710  else if (controller.microGamepad) {
711  GCMicroGamepad *gamepad = controller.microGamepad;
712 
713  Sint16 axes[] = {
714  (Sint16) (gamepad.dpad.xAxis.value * 32767),
715  (Sint16) (gamepad.dpad.yAxis.value * -32767),
716  };
717 
718  for (i = 0; i < SDL_arraysize(axes); i++) {
719  SDL_PrivateJoystickAxis(joystick, i, axes[i]);
720  }
721 
722  Uint8 buttons[joystick->nbuttons];
723  int button_count = 0;
724  buttons[button_count++] = gamepad.buttonA.isPressed;
725  buttons[button_count++] = gamepad.buttonX.isPressed;
726 #pragma clang diagnostic push
727 #pragma clang diagnostic ignored "-Wunguarded-availability-new"
728  /* This must be the last button, so we can optionally handle it with pause_button_index below */
729  if (joystick->hwdata->button_mask & (1 << SDL_CONTROLLER_BUTTON_START)) {
730  if (joystick->hwdata->uses_pause_handler) {
731  pause_button_index = button_count;
732  buttons[button_count++] = joystick->delayed_guide_button;
733  } else {
734  buttons[button_count++] = gamepad.buttonMenu.isPressed;
735  }
736  }
737 #pragma clang diagnostic pop
738 
739  for (i = 0; i < button_count; i++) {
740  SDL_PrivateJoystickButton(joystick, i, buttons[i]);
741  }
742  }
743 #endif /* TARGET_OS_TV */
744 
745  if (joystick->nhats > 0) {
746  SDL_PrivateJoystickHat(joystick, 0, hatstate);
747  }
748 
749  if (joystick->hwdata->uses_pause_handler) {
750  for (i = 0; i < joystick->hwdata->num_pause_presses; i++) {
751  SDL_PrivateJoystickButton(joystick, pause_button_index, SDL_PRESSED);
752  SDL_PrivateJoystickButton(joystick, pause_button_index, SDL_RELEASED);
753  }
754  joystick->hwdata->num_pause_presses = 0;
755  }
756  }
757 #endif /* SDL_JOYSTICK_MFI */
758 }
759 
760 static int
761 IOS_JoystickRumble(SDL_Joystick * joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble)
762 {
763  return SDL_Unsupported();
764 }
765 
766 static void
767 IOS_JoystickUpdate(SDL_Joystick * joystick)
768 {
769  SDL_JoystickDeviceItem *device = joystick->hwdata;
770 
771  if (device == NULL) {
772  return;
773  }
774 
775  if (device->accelerometer) {
776  IOS_AccelerometerUpdate(joystick);
777  } else if (device->controller) {
778  IOS_MFIJoystickUpdate(joystick);
779  }
780 }
781 
782 static void
783 IOS_JoystickClose(SDL_Joystick * joystick)
784 {
785  SDL_JoystickDeviceItem *device = joystick->hwdata;
786 
787  if (device == NULL) {
788  return;
789  }
790 
791  device->joystick = NULL;
792 
793  @autoreleasepool {
794  if (device->accelerometer) {
795 #if !TARGET_OS_TV
796  [motionManager stopAccelerometerUpdates];
797 #endif /* !TARGET_OS_TV */
798  } else if (device->controller) {
799 #ifdef SDL_JOYSTICK_MFI
800  GCController *controller = device->controller;
801  controller.controllerPausedHandler = nil;
802  controller.playerIndex = -1;
803 #endif
804  }
805  }
806  if (device->remote) {
808  }
809 }
810 
811 static void
813 {
814  @autoreleasepool {
815 #ifdef SDL_JOYSTICK_MFI
816  NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
817 
818  if (connectObserver) {
819  [center removeObserver:connectObserver name:GCControllerDidConnectNotification object:nil];
820  connectObserver = nil;
821  }
822 
823  if (disconnectObserver) {
824  [center removeObserver:disconnectObserver name:GCControllerDidDisconnectNotification object:nil];
825  disconnectObserver = nil;
826  }
827 
828 #if TARGET_OS_TV
830  SDL_AppleTVRemoteRotationHintChanged, NULL);
831 #endif /* TARGET_OS_TV */
832 #endif /* SDL_JOYSTICK_MFI */
833 
834  while (deviceList != NULL) {
836  }
837 
838 #if !TARGET_OS_TV
839  motionManager = nil;
840 #endif /* !TARGET_OS_TV */
841  }
842 
843  numjoysticks = 0;
844 }
845 
846 SDL_JoystickDriver SDL_IOS_JoystickDriver =
847 {
861 };
862 
863 /* vi: set ts=4 sw=4 expandtab: */
SDL_zero
#define SDL_zero(x)
Definition: SDL_stdinc.h:418
IOS_JoystickGetDeviceInstanceID
static SDL_JoystickID IOS_JoystickGetDeviceInstanceID(int device_index)
Definition: SDL_sysjoystick.m:496
c
const GLubyte * c
Definition: SDL_opengl_glext.h:11096
SDL_events.h
IOS_AddMFIJoystickDevice
static void IOS_AddMFIJoystickDevice(SDL_JoystickDeviceItem *device, GCController *controller)
Definition: SDL_sysjoystick.m:104
GCExtendedGamepad(SDL)
Definition: SDL_sysjoystick.m:58
SDL_HINT_TV_REMOTE_AS_JOYSTICK
#define SDL_HINT_TV_REMOTE_AS_JOYSTICK
A variable controlling whether the Android / tvOS remotes should be listed as joystick devices,...
Definition: SDL_hints.h:451
in
GLuint in
Definition: SDL_opengl_glext.h:7943
numjoysticks
static int numjoysticks
Definition: SDL_sysjoystick.m:83
SDL_sysjoystick_c.h
SDL_HAT_CENTERED
#define SDL_HAT_CENTERED
Definition: SDL_joystick.h:339
NULL
#define NULL
Definition: begin_code.h:167
SDL_HAT_DOWN
#define SDL_HAT_DOWN
Definition: SDL_joystick.h:342
SDL_joystick.h
GetDeviceForIndex
static SDL_JoystickDeviceItem * GetDeviceForIndex(int device_index)
Definition: SDL_sysjoystick.m:87
IOS_JoystickGetDeviceGUID
static SDL_JoystickGUID IOS_JoystickGetDeviceGUID(int device_index)
Definition: SDL_sysjoystick.m:483
IOS_MFIJoystickUpdate
static void IOS_MFIJoystickUpdate(SDL_Joystick *joystick)
Definition: SDL_sysjoystick.m:622
IOS_JoystickSetDevicePlayerIndex
static void IOS_JoystickSetDevicePlayerIndex(int device_index, int player_index)
Definition: SDL_sysjoystick.m:474
IOS_AccelerometerUpdate
static void IOS_AccelerometerUpdate(SDL_Joystick *joystick)
Definition: SDL_sysjoystick.m:552
IOS_MFIJoystickHatStateForDPad
static Uint8 IOS_MFIJoystickHatStateForDPad(GCControllerDirectionPad *dpad)
Definition: SDL_sysjoystick.m:597
SDLCALL
#define SDLCALL
Definition: SDL_internal.h:49
GCMicroGamepad(SDL)
Definition: SDL_sysjoystick.m:68
SDL_PrivateJoystickAdded
void SDL_PrivateJoystickAdded(SDL_JoystickID device_instance)
Definition: SDL_joystick.c:921
IOS_JoystickDetect
static void IOS_JoystickDetect(void)
Definition: SDL_sysjoystick.m:455
SDL_CONTROLLER_BUTTON_RIGHTSTICK
@ SDL_CONTROLLER_BUTTON_RIGHTSTICK
Definition: SDL_gamecontroller.h:357
deviceList
static SDL_JoystickDeviceItem * deviceList
Definition: SDL_sysjoystick.m:81
SDL_HINT_APPLE_TV_REMOTE_ALLOW_ROTATION
#define SDL_HINT_APPLE_TV_REMOTE_ALLOW_ROTATION
A variable controlling whether the Apple TV remote's joystick axes will automatically match the rotat...
Definition: SDL_hints.h:420
SDL_GetNextJoystickInstanceID
SDL_JoystickID SDL_GetNextJoystickInstanceID()
Definition: SDL_joystick.c:250
GCExtendedGamepad(SDL)::rightThumbstickButton
GCControllerButtonInput * rightThumbstickButton
Definition: SDL_sysjoystick.m:61
IOS_JoystickClose
static void IOS_JoystickClose(SDL_Joystick *joystick)
Definition: SDL_sysjoystick.m:783
SDL_RELEASED
#define SDL_RELEASED
Definition: SDL_events.h:49
SDL_SwapLE16
#define SDL_SwapLE16(X)
Definition: SDL_endian.h:244
IOS_JoystickOpen
static int IOS_JoystickOpen(SDL_Joystick *joystick, int device_index)
Definition: SDL_sysjoystick.m:503
SDL_CONTROLLER_BUTTON_B
@ SDL_CONTROLLER_BUTTON_B
Definition: SDL_gamecontroller.h:350
GCExtendedGamepad(SDL)::buttonOptions
GCControllerButtonInput * buttonOptions
Definition: SDL_sysjoystick.m:65
SDL_PrivateJoystickRemoved
void SDL_PrivateJoystickRemoved(SDL_JoystickID device_instance)
Definition: SDL_joystick.c:987
SDL_PrivateJoystickAxis
int SDL_PrivateJoystickAxis(SDL_Joystick *joystick, Uint8 axis, Sint16 value)
Definition: SDL_joystick.c:1023
SDL_CONTROLLER_BUTTON_BACK
@ SDL_CONTROLLER_BUTTON_BACK
Definition: SDL_gamecontroller.h:353
SDL_CONTROLLER_BUTTON_LEFTSHOULDER
@ SDL_CONTROLLER_BUTTON_LEFTSHOULDER
Definition: SDL_gamecontroller.h:358
SDL_JoystickID
Sint32 SDL_JoystickID
Definition: SDL_joystick.h:81
SDL_PRESSED
#define SDL_PRESSED
Definition: SDL_events.h:50
Sint16
int16_t Sint16
Definition: SDL_stdinc.h:185
joystick_hwdata::next
struct joystick_hwdata * next
Definition: SDL_sysjoystick_c.h:51
SDL_HINT_ACCELEROMETER_AS_JOYSTICK
#define SDL_HINT_ACCELEROMETER_AS_JOYSTICK
A variable controlling whether the Android / iOS built-in accelerometer should be listed as a joystic...
Definition: SDL_hints.h:441
GCExtendedGamepad(SDL)::buttonMenu
GCControllerButtonInput * buttonMenu
Definition: SDL_sysjoystick.m:64
SDL_memcpy
#define SDL_memcpy
Definition: SDL_dynapi_overrides.h:387
SDL_GetHintBoolean
#define SDL_GetHintBoolean
Definition: SDL_dynapi_overrides.h:608
SDL_FALSE
@ SDL_FALSE
Definition: SDL_stdinc.h:163
disconnectObserver
static id disconnectObserver
Definition: SDL_sysjoystick.m:50
SDL_free
#define SDL_free
Definition: SDL_dynapi_overrides.h:377
IOS_JoystickGetDeviceName
static const char * IOS_JoystickGetDeviceName(int device_index)
Definition: SDL_sysjoystick.m:460
SDL_max
#define SDL_max(x, y)
Definition: SDL_stdinc.h:407
IOS_JoystickQuit
static void IOS_JoystickQuit(void)
Definition: SDL_sysjoystick.m:812
name
GLuint const GLchar * name
Definition: SDL_opengl_glext.h:663
SDL_IPHONE_MAX_GFORCE
#define SDL_IPHONE_MAX_GFORCE
Definition: SDL_config_iphoneos.h:201
SDL_PrivateJoystickButton
int SDL_PrivateJoystickButton(SDL_Joystick *joystick, Uint8 button, Uint8 state)
Definition: SDL_joystick.c:1162
Uint16
uint16_t Uint16
Definition: SDL_stdinc.h:191
SDL_HAT_LEFT
#define SDL_HAT_LEFT
Definition: SDL_joystick.h:343
SDL_assert.h
SDL_min
#define SDL_min(x, y)
Definition: SDL_stdinc.h:406
SDL_AppleTVRemoteOpenedAsJoystick
int SDL_AppleTVRemoteOpenedAsJoystick
Definition: SDL_sysjoystick.m:84
SDL_CONTROLLER_BUTTON_START
@ SDL_CONTROLLER_BUTTON_START
Definition: SDL_gamecontroller.h:355
IOS_JoystickInit
static int IOS_JoystickInit(void)
Definition: SDL_sysjoystick.m:393
SDL_PrivateJoystickHat
int SDL_PrivateJoystickHat(SDL_Joystick *joystick, Uint8 hat, Uint8 value)
Definition: SDL_joystick.c:1086
SDL_arraysize
#define SDL_arraysize(array)
Definition: SDL_stdinc.h:115
SDL_calloc
#define SDL_calloc
Definition: SDL_dynapi_overrides.h:375
SDL_CONTROLLER_BUTTON_RIGHTSHOULDER
@ SDL_CONTROLLER_BUTTON_RIGHTSHOULDER
Definition: SDL_gamecontroller.h:359
SDL_CONTROLLER_BUTTON_Y
@ SDL_CONTROLLER_BUTTON_Y
Definition: SDL_gamecontroller.h:352
sort_controllers.controllers
list controllers
Definition: sort_controllers.py:12
motionManager
static CMMotionManager * motionManager
Definition: SDL_sysjoystick.m:78
SDL_TRUE
@ SDL_TRUE
Definition: SDL_stdinc.h:164
IOS_JoystickGetCount
static int IOS_JoystickGetCount(void)
Definition: SDL_sysjoystick.m:449
SDL_AddHintCallback
#define SDL_AddHintCallback
Definition: SDL_dynapi_overrides.h:192
SDL_HAT_RIGHT
#define SDL_HAT_RIGHT
Definition: SDL_joystick.h:341
SDL_SetError
#define SDL_SetError
Definition: SDL_dynapi_overrides.h:30
IOS_JoystickGetDevicePlayerIndex
static int IOS_JoystickGetDevicePlayerIndex(int device_index)
Definition: SDL_sysjoystick.m:467
IOS_JoystickRumble
static int IOS_JoystickRumble(SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble)
Definition: SDL_sysjoystick.m:761
SDL_stdinc.h
SDL_hints.h
GCExtendedGamepad(SDL)::leftThumbstickButton
GCControllerButtonInput * leftThumbstickButton
Definition: SDL_sysjoystick.m:60
IOS_JoystickUpdate
static void IOS_JoystickUpdate(SDL_Joystick *joystick)
Definition: SDL_sysjoystick.m:767
SDL_CONTROLLER_BUTTON_LEFTSTICK
@ SDL_CONTROLLER_BUTTON_LEFTSTICK
Definition: SDL_gamecontroller.h:356
SDL_strdup
#define SDL_strdup
Definition: SDL_dynapi_overrides.h:397
SDL_strlen
#define SDL_strlen
Definition: SDL_dynapi_overrides.h:393
SDL_HAT_UP
#define SDL_HAT_UP
Definition: SDL_joystick.h:340
SDL_HARDWARE_BUS_BLUETOOTH
#define SDL_HARDWARE_BUS_BLUETOOTH
Definition: SDL_sysjoystick.h:83
IOS_RemoveJoystickDevice
static SDL_JoystickDeviceItem * IOS_RemoveJoystickDevice(SDL_JoystickDeviceItem *device)
Definition: SDL_sysjoystick.m:323
SDL_IOS_JoystickDriver
SDL_JoystickDriver SDL_IOS_JoystickDriver
Definition: SDL_sysjoystick.m:846
SDL_DelHintCallback
#define SDL_DelHintCallback
Definition: SDL_dynapi_overrides.h:193
SDL_Unsupported
#define SDL_Unsupported()
Definition: SDL_error.h:53
connectObserver
static id connectObserver
Definition: SDL_sysjoystick.m:49
SDL_CONTROLLER_BUTTON_X
@ SDL_CONTROLLER_BUTTON_X
Definition: SDL_gamecontroller.h:351
device
static SDL_AudioDeviceID device
Definition: loopwave.c:37
SDL_JoystickGUID
Definition: SDL_joystick.h:70
SDL_CONTROLLER_BUTTON_A
@ SDL_CONTROLLER_BUTTON_A
Definition: SDL_gamecontroller.h:349
accelerometerName
static const char * accelerometerName
Definition: SDL_sysjoystick.m:77
IOS_AddJoystickDevice
static void IOS_AddJoystickDevice(GCController *controller, SDL_bool accelerometer)
Definition: SDL_sysjoystick.m:262
i
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
FALSE
#define FALSE
Definition: edid-parse.c:34
joystick_hwdata
Definition: SDL_sysjoystick_c.h:46
SDL_bool
SDL_bool
Definition: SDL_stdinc.h:161
Uint8
uint8_t Uint8
Definition: SDL_stdinc.h:179