SDL  2.0
SDL_mfijoystick.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_events.h"
25 #include "SDL_joystick.h"
26 #include "SDL_hints.h"
27 #include "SDL_stdinc.h"
28 #include "../SDL_sysjoystick.h"
29 #include "../SDL_joystick_c.h"
30 #include "../usb_ids.h"
31 
32 #include "SDL_mfijoystick_c.h"
33 
34 #if !SDL_EVENTS_DISABLED
35 #include "../../events/SDL_events_c.h"
36 #endif
37 
38 #if TARGET_OS_IOS
39 #define SDL_JOYSTICK_iOS_ACCELEROMETER
40 #import <CoreMotion/CoreMotion.h>
41 #endif
42 
43 #if defined(__MACOSX__)
44 #include <IOKit/hid/IOHIDManager.h>
45 #include <AppKit/NSApplication.h>
46 #ifndef NSAppKitVersionNumber10_15
47 #define NSAppKitVersionNumber10_15 1894
48 #endif
49 #endif /* __MACOSX__ */
50 
51 #ifdef SDL_JOYSTICK_MFI
52 #import <GameController/GameController.h>
53 
54 static id connectObserver = nil;
55 static id disconnectObserver = nil;
56 
57 #include <Availability.h>
58 #include <objc/message.h>
59 
60 /* remove compilation warnings for strict builds by defining these selectors, even though
61  * they are only ever used indirectly through objc_msgSend
62  */
63 @interface GCController (SDL)
64 #if defined(__MACOSX__) && (__MAC_OS_X_VERSION_MAX_ALLOWED <= 101600)
65 + (BOOL)supportsHIDDevice:(IOHIDDeviceRef)device;
66 #endif
67 @end
68 @interface GCExtendedGamepad (SDL)
69 #if !((__IPHONE_OS_VERSION_MAX_ALLOWED >= 121000) || (__APPLETV_OS_VERSION_MAX_ALLOWED >= 121000) || (__MAC_OS_VERSION_MAX_ALLOWED >= 1401000))
70 @property (nonatomic, readonly, nullable) GCControllerButtonInput *leftThumbstickButton;
71 @property (nonatomic, readonly, nullable) GCControllerButtonInput *rightThumbstickButton;
72 #endif
73 #if !((__IPHONE_OS_VERSION_MAX_ALLOWED >= 130000) || (__APPLETV_OS_VERSION_MAX_ALLOWED >= 130000) || (__MAC_OS_VERSION_MAX_ALLOWED >= 1500000))
74 @property (nonatomic, readonly) GCControllerButtonInput *buttonMenu;
75 @property (nonatomic, readonly, nullable) GCControllerButtonInput *buttonOptions;
76 #endif
77 #if !((__IPHONE_OS_VERSION_MAX_ALLOWED >= 140000) || (__APPLETV_OS_VERSION_MAX_ALLOWED >= 140000) || (__MAC_OS_VERSION_MAX_ALLOWED > 1500000))
78 @property (nonatomic, readonly, nullable) GCControllerButtonInput *buttonHome;
79 #endif
80 @end
81 @interface GCMicroGamepad (SDL)
82 #if !((__IPHONE_OS_VERSION_MAX_ALLOWED >= 130000) || (__APPLETV_OS_VERSION_MAX_ALLOWED >= 130000) || (__MAC_OS_VERSION_MAX_ALLOWED >= 1500000))
83 @property (nonatomic, readonly) GCControllerButtonInput *buttonMenu;
84 #endif
85 @end
86 
87 #if (__IPHONE_OS_VERSION_MAX_ALLOWED >= 140000) || (__APPLETV_OS_VERSION_MAX_ALLOWED >= 140000) || (__MAC_OS_VERSION_MAX_ALLOWED > 1500000) || (__MAC_OS_X_VERSION_MAX_ALLOWED > 101600)
88 #define ENABLE_MFI_BATTERY
89 #define ENABLE_MFI_RUMBLE
90 #define ENABLE_MFI_LIGHT
91 #define ENABLE_MFI_SENSORS
92 #define ENABLE_PHYSICAL_INPUT_PROFILE
93 #endif
94 
95 #ifdef ENABLE_MFI_RUMBLE
96 #import <CoreHaptics/CoreHaptics.h>
97 #endif
98 
99 #endif /* SDL_JOYSTICK_MFI */
100 
101 #ifdef SDL_JOYSTICK_iOS_ACCELEROMETER
102 static const char *accelerometerName = "iOS Accelerometer";
103 static CMMotionManager *motionManager = nil;
104 #endif /* SDL_JOYSTICK_iOS_ACCELEROMETER */
105 
107 
108 static int numjoysticks = 0;
110 
111 static SDL_JoystickDeviceItem *
112 GetDeviceForIndex(int device_index)
113 {
115  int i = 0;
116 
117  while (i < device_index) {
118  if (device == NULL) {
119  return NULL;
120  }
121  device = device->next;
122  i++;
123  }
124 
125  return device;
126 }
127 
128 #ifdef SDL_JOYSTICK_MFI
129 static void
130 IOS_AddMFIJoystickDevice(SDL_JoystickDeviceItem *device, GCController *controller)
131 {
132  Uint16 *guid16 = (Uint16 *)device->guid.data;
133  Uint16 vendor = 0;
134  Uint16 product = 0;
135  Uint8 subtype = 0;
136 
137  const char *name = NULL;
138  /* Explicitly retain the controller because SDL_JoystickDeviceItem is a
139  * struct, and ARC doesn't work with structs. */
140  device->controller = (__bridge GCController *) CFBridgingRetain(controller);
141 
142  if (controller.vendorName) {
143  name = controller.vendorName.UTF8String;
144  }
145 
146  if (!name) {
147  name = "MFi Gamepad";
148  }
149 
150  device->name = SDL_CreateJoystickName(0, 0, NULL, name);
151 
152  if (controller.extendedGamepad) {
153  GCExtendedGamepad *gamepad = controller.extendedGamepad;
154  BOOL is_xbox = [controller.vendorName containsString: @"Xbox"];
155  BOOL is_ps4 = [controller.vendorName containsString: @"DUALSHOCK"];
156 #if TARGET_OS_TV
157  BOOL is_MFi = (!is_xbox && !is_ps4);
158 #endif
159  int nbuttons = 0;
160 
161  /* These buttons are part of the original MFi spec */
162  device->button_mask |= (1 << SDL_CONTROLLER_BUTTON_A);
163  device->button_mask |= (1 << SDL_CONTROLLER_BUTTON_B);
164  device->button_mask |= (1 << SDL_CONTROLLER_BUTTON_X);
165  device->button_mask |= (1 << SDL_CONTROLLER_BUTTON_Y);
166  device->button_mask |= (1 << SDL_CONTROLLER_BUTTON_LEFTSHOULDER);
167  device->button_mask |= (1 << SDL_CONTROLLER_BUTTON_RIGHTSHOULDER);
168  nbuttons += 6;
169 
170  /* These buttons are available on some newer controllers */
171 #pragma clang diagnostic push
172 #pragma clang diagnostic ignored "-Wunguarded-availability-new"
173  if ([gamepad respondsToSelector:@selector(leftThumbstickButton)] && gamepad.leftThumbstickButton) {
174  device->button_mask |= (1 << SDL_CONTROLLER_BUTTON_LEFTSTICK);
175  ++nbuttons;
176  }
177  if ([gamepad respondsToSelector:@selector(rightThumbstickButton)] && gamepad.rightThumbstickButton) {
178  device->button_mask |= (1 << SDL_CONTROLLER_BUTTON_RIGHTSTICK);
179  ++nbuttons;
180  }
181  if ([gamepad respondsToSelector:@selector(buttonOptions)] && gamepad.buttonOptions) {
182  device->button_mask |= (1 << SDL_CONTROLLER_BUTTON_BACK);
183  ++nbuttons;
184  }
185  if ([gamepad respondsToSelector:@selector(buttonHome)] && gamepad.buttonHome) {
186  device->button_mask |= (1 << SDL_CONTROLLER_BUTTON_GUIDE);
187  ++nbuttons;
188  }
189  BOOL has_direct_menu = [gamepad respondsToSelector:@selector(buttonMenu)] && gamepad.buttonMenu;
190 #if TARGET_OS_TV
191  /* On tvOS MFi controller menu button brings you to the home screen */
192  if (is_MFi) {
193  has_direct_menu = FALSE;
194  }
195 #endif
196  device->button_mask |= (1 << SDL_CONTROLLER_BUTTON_START);
197  ++nbuttons;
198  if (!has_direct_menu) {
199  device->uses_pause_handler = SDL_TRUE;
200  }
201 
202 #ifdef ENABLE_PHYSICAL_INPUT_PROFILE
203  if ([controller respondsToSelector:@selector(physicalInputProfile)]) {
204  if (controller.physicalInputProfile.buttons[GCInputDualShockTouchpadButton] != nil) {
205  device->has_dualshock_touchpad = SDL_TRUE;
206  device->button_mask |= (1 << SDL_CONTROLLER_BUTTON_MISC1);
207  ++nbuttons;
208  }
209  if (controller.physicalInputProfile.buttons[GCInputXboxPaddleOne] != nil) {
210  device->has_xbox_paddles = SDL_TRUE;
211  device->button_mask |= (1 << SDL_CONTROLLER_BUTTON_PADDLE1);
212  ++nbuttons;
213  }
214  if (controller.physicalInputProfile.buttons[GCInputXboxPaddleTwo] != nil) {
215  device->has_xbox_paddles = SDL_TRUE;
216  device->button_mask |= (1 << SDL_CONTROLLER_BUTTON_PADDLE2);
217  ++nbuttons;
218  }
219  if (controller.physicalInputProfile.buttons[GCInputXboxPaddleThree] != nil) {
220  device->has_xbox_paddles = SDL_TRUE;
221  device->button_mask |= (1 << SDL_CONTROLLER_BUTTON_PADDLE3);
222  ++nbuttons;
223  }
224  if (controller.physicalInputProfile.buttons[GCInputXboxPaddleFour] != nil) {
225  device->has_xbox_paddles = SDL_TRUE;
226  device->button_mask |= (1 << SDL_CONTROLLER_BUTTON_PADDLE4);
227  ++nbuttons;
228  }
229  }
230 #endif
231 #pragma clang diagnostic pop
232 
233  if (is_xbox) {
234  vendor = USB_VENDOR_MICROSOFT;
235  if (device->has_xbox_paddles) {
236  /* Assume Xbox One Elite Series 2 Controller unless/until GCController flows VID/PID */
238  subtype = 1;
239  } else {
240  /* Assume Xbox One S BLE Controller unless/until GCController flows VID/PID */
242  subtype = 0;
243  }
244  } else if (is_ps4) {
245  /* Assume DS4 Slim unless/until GCController flows VID/PID */
246  vendor = USB_VENDOR_SONY;
247  product = USB_PRODUCT_SONY_DS4_SLIM;
248  if (device->has_dualshock_touchpad) {
249  subtype = 1;
250  } else {
251  subtype = 0;
252  }
253  } else {
254  vendor = USB_VENDOR_APPLE;
255  product = 1;
256  subtype = 1;
257  }
258 
259  device->naxes = 6; /* 2 thumbsticks and 2 triggers */
260  device->nhats = 1; /* d-pad */
261  device->nbuttons = nbuttons;
262 
263  } else if (controller.gamepad) {
264  int nbuttons = 0;
265 
266  /* These buttons are part of the original MFi spec */
267  device->button_mask |= (1 << SDL_CONTROLLER_BUTTON_A);
268  device->button_mask |= (1 << SDL_CONTROLLER_BUTTON_B);
269  device->button_mask |= (1 << SDL_CONTROLLER_BUTTON_X);
270  device->button_mask |= (1 << SDL_CONTROLLER_BUTTON_Y);
271  device->button_mask |= (1 << SDL_CONTROLLER_BUTTON_LEFTSHOULDER);
272  device->button_mask |= (1 << SDL_CONTROLLER_BUTTON_RIGHTSHOULDER);
273  device->button_mask |= (1 << SDL_CONTROLLER_BUTTON_START);
274  nbuttons += 7;
275  device->uses_pause_handler = SDL_TRUE;
276 
277  vendor = USB_VENDOR_APPLE;
278  product = 2;
279  subtype = 2;
280  device->naxes = 0; /* no traditional analog inputs */
281  device->nhats = 1; /* d-pad */
282  device->nbuttons = nbuttons;
283  }
284 #if TARGET_OS_TV
285  else if (controller.microGamepad) {
286  int nbuttons = 0;
287 
288  device->button_mask |= (1 << SDL_CONTROLLER_BUTTON_A);
289  device->button_mask |= (1 << SDL_CONTROLLER_BUTTON_B); /* Button X on microGamepad */
290  nbuttons += 2;
291 
292  device->button_mask |= (1 << SDL_CONTROLLER_BUTTON_START);
293  ++nbuttons;
294  device->uses_pause_handler = SDL_TRUE;
295 
296  vendor = USB_VENDOR_APPLE;
297  product = 3;
298  subtype = 3;
299  device->naxes = 2; /* treat the touch surface as two axes */
300  device->nhats = 0; /* apparently the touch surface-as-dpad is buggy */
301  device->nbuttons = nbuttons;
302 
303  controller.microGamepad.allowsRotation = SDL_GetHintBoolean(SDL_HINT_APPLE_TV_REMOTE_ALLOW_ROTATION, SDL_FALSE);
304  }
305 #endif /* TARGET_OS_TV */
306 
307  /* We only need 16 bits for each of these; space them out to fill 128. */
308  /* Byteswap so devices get same GUID on little/big endian platforms. */
310  *guid16++ = 0;
311  *guid16++ = SDL_SwapLE16(vendor);
312  *guid16++ = 0;
313  *guid16++ = SDL_SwapLE16(product);
314  *guid16++ = 0;
315 
316  *guid16++ = SDL_SwapLE16(device->button_mask);
317 
318  if (vendor == USB_VENDOR_APPLE) {
319  /* Note that this is an MFI controller and what subtype it is */
320  device->guid.data[14] = 'm';
321  device->guid.data[15] = subtype;
322  } else {
323  device->guid.data[15] = subtype;
324  }
325 
326  /* This will be set when the first button press of the controller is
327  * detected. */
328  controller.playerIndex = -1;
329 }
330 #endif /* SDL_JOYSTICK_MFI */
331 
332 #if defined(SDL_JOYSTICK_iOS_ACCELEROMETER) || defined(SDL_JOYSTICK_MFI)
333 static void
334 IOS_AddJoystickDevice(GCController *controller, SDL_bool accelerometer)
335 {
337 
338 #if TARGET_OS_TV
340  /* Ignore devices that aren't actually controllers (e.g. remotes), they'll be handled as keyboard input */
341  if (controller && !controller.extendedGamepad && !controller.gamepad && controller.microGamepad) {
342  return;
343  }
344  }
345 #endif
346 
347  while (device != NULL) {
348  if (device->controller == controller) {
349  return;
350  }
351  device = device->next;
352  }
353 
355  if (device == NULL) {
356  return;
357  }
358 
359  device->accelerometer = accelerometer;
360  device->instance_id = SDL_GetNextJoystickInstanceID();
361 
362  if (accelerometer) {
363 #ifdef SDL_JOYSTICK_iOS_ACCELEROMETER
364  device->name = SDL_strdup(accelerometerName);
365  device->naxes = 3; /* Device acceleration in the x, y, and z axes. */
366  device->nhats = 0;
367  device->nbuttons = 0;
368 
369  /* Use the accelerometer name as a GUID. */
370  SDL_memcpy(&device->guid.data, device->name, SDL_min(sizeof(SDL_JoystickGUID), SDL_strlen(device->name)));
371 #else
372  SDL_free(device);
373  return;
374 #endif /* SDL_JOYSTICK_iOS_ACCELEROMETER */
375  } else if (controller) {
376 #ifdef SDL_JOYSTICK_MFI
377  IOS_AddMFIJoystickDevice(device, controller);
378 #else
379  SDL_free(device);
380  return;
381 #endif /* SDL_JOYSTICK_MFI */
382  }
383 
384  if (deviceList == NULL) {
385  deviceList = device;
386  } else {
387  SDL_JoystickDeviceItem *lastdevice = deviceList;
388  while (lastdevice->next != NULL) {
389  lastdevice = lastdevice->next;
390  }
391  lastdevice->next = device;
392  }
393 
394  ++numjoysticks;
395 
396  SDL_PrivateJoystickAdded(device->instance_id);
397 }
398 #endif /* SDL_JOYSTICK_iOS_ACCELEROMETER || SDL_JOYSTICK_MFI */
399 
400 static SDL_JoystickDeviceItem *
402 {
406 
407  if (device == NULL) {
408  return NULL;
409  }
410 
411  next = device->next;
412 
413  while (item != NULL) {
414  if (item == device) {
415  break;
416  }
417  prev = item;
418  item = item->next;
419  }
420 
421  /* Unlink the device item from the device list. */
422  if (prev) {
423  prev->next = device->next;
424  } else if (device == deviceList) {
425  deviceList = device->next;
426  }
427 
428  if (device->joystick) {
429  device->joystick->hwdata = NULL;
430  }
431 
432 #ifdef SDL_JOYSTICK_MFI
433  @autoreleasepool {
434  if (device->controller) {
435  /* The controller was explicitly retained in the struct, so it
436  * should be explicitly released before freeing the struct. */
437  GCController *controller = CFBridgingRelease((__bridge CFTypeRef)(device->controller));
438  controller.controllerPausedHandler = nil;
439  device->controller = nil;
440  }
441  }
442 #endif /* SDL_JOYSTICK_MFI */
443 
444  --numjoysticks;
445 
446  SDL_PrivateJoystickRemoved(device->instance_id);
447 
448  SDL_free(device->name);
449  SDL_free(device);
450 
451  return next;
452 }
453 
454 #if TARGET_OS_TV
455 static void SDLCALL
456 SDL_AppleTVRemoteRotationHintChanged(void *udata, const char *name, const char *oldValue, const char *newValue)
457 {
458  BOOL allowRotation = newValue != NULL && *newValue != '0';
459 
460  @autoreleasepool {
461  for (GCController *controller in [GCController controllers]) {
462  if (controller.microGamepad) {
463  controller.microGamepad.allowsRotation = allowRotation;
464  }
465  }
466  }
467 }
468 #endif /* TARGET_OS_TV */
469 
470 #if defined(__MACOSX__)
471 static int is_macos11(void)
472 {
473  return (floor(NSAppKitVersionNumber) > NSAppKitVersionNumber10_15);
474 }
475 #endif
476 
477 static int
479 {
480 #if defined(__MACOSX__)
481  if (!is_macos11()) {
482  return 0;
483  }
484 #endif
485 
486  @autoreleasepool {
487 #ifdef SDL_JOYSTICK_iOS_ACCELEROMETER
489  /* Default behavior, accelerometer as joystick */
490  IOS_AddJoystickDevice(nil, SDL_TRUE);
491  }
492 #endif
493 
494 #ifdef SDL_JOYSTICK_MFI
495  /* GameController.framework was added in iOS 7. */
496  if (![GCController class]) {
497  return 0;
498  }
499 
500  /* For whatever reason, this always returns an empty array on
501  macOS 11.0.1 */
502  for (GCController *controller in [GCController controllers]) {
503  IOS_AddJoystickDevice(controller, SDL_FALSE);
504  }
505 
506 #if TARGET_OS_TV
508  SDL_AppleTVRemoteRotationHintChanged, NULL);
509 #endif /* TARGET_OS_TV */
510 
511  NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
512 
513  connectObserver = [center addObserverForName:GCControllerDidConnectNotification
514  object:nil
515  queue:nil
516  usingBlock:^(NSNotification *note) {
517  GCController *controller = note.object;
518  IOS_AddJoystickDevice(controller, SDL_FALSE);
519  }];
520 
521  disconnectObserver = [center addObserverForName:GCControllerDidDisconnectNotification
522  object:nil
523  queue:nil
524  usingBlock:^(NSNotification *note) {
525  GCController *controller = note.object;
526  SDL_JoystickDeviceItem *device = deviceList;
527  while (device != NULL) {
528  if (device->controller == controller) {
529  IOS_RemoveJoystickDevice(device);
530  break;
531  }
532  device = device->next;
533  }
534  }];
535 #endif /* SDL_JOYSTICK_MFI */
536  }
537 
538  return 0;
539 }
540 
541 static int
543 {
544  return numjoysticks;
545 }
546 
547 static void
549 {
550 }
551 
552 static const char *
553 IOS_JoystickGetDeviceName(int device_index)
554 {
556  return device ? device->name : "Unknown";
557 }
558 
559 static int
561 {
562 #ifdef SDL_JOYSTICK_MFI
564  if (device && device->controller) {
565  return (int)device->controller.playerIndex;
566  }
567 #endif
568  return -1;
569 }
570 
571 static void
572 IOS_JoystickSetDevicePlayerIndex(int device_index, int player_index)
573 {
574 #ifdef SDL_JOYSTICK_MFI
576  if (device && device->controller) {
577  device->controller.playerIndex = player_index;
578  }
579 #endif
580 }
581 
582 static SDL_JoystickGUID
583 IOS_JoystickGetDeviceGUID( int device_index )
584 {
586  SDL_JoystickGUID guid;
587  if (device) {
588  guid = device->guid;
589  } else {
590  SDL_zero(guid);
591  }
592  return guid;
593 }
594 
595 static SDL_JoystickID
597 {
599  return device ? device->instance_id : -1;
600 }
601 
602 static int
603 IOS_JoystickOpen(SDL_Joystick *joystick, int device_index)
604 {
606  if (device == NULL) {
607  return SDL_SetError("Could not open Joystick: no hardware device for the specified index");
608  }
609 
610  joystick->hwdata = device;
611  joystick->instance_id = device->instance_id;
612 
613  joystick->naxes = device->naxes;
614  joystick->nhats = device->nhats;
615  joystick->nbuttons = device->nbuttons;
616  joystick->nballs = 0;
617 
618  if (device->has_dualshock_touchpad) {
620  }
621 
622  device->joystick = joystick;
623 
624  @autoreleasepool {
625  if (device->accelerometer) {
626 #ifdef SDL_JOYSTICK_iOS_ACCELEROMETER
627  if (motionManager == nil) {
628  motionManager = [[CMMotionManager alloc] init];
629  }
630 
631  /* Shorter times between updates can significantly increase CPU usage. */
632  motionManager.accelerometerUpdateInterval = 0.1;
633  [motionManager startAccelerometerUpdates];
634 #endif
635  } else {
636 #ifdef SDL_JOYSTICK_MFI
637  if (device->uses_pause_handler) {
638  GCController *controller = device->controller;
639  controller.controllerPausedHandler = ^(GCController *c) {
640  if (joystick->hwdata) {
641  ++joystick->hwdata->num_pause_presses;
642  }
643  };
644  }
645 
646 #ifdef ENABLE_MFI_SENSORS
647  if (@available(macOS 11.0, iOS 14.0, tvOS 14.0, *)) {
648  GCController *controller = joystick->hwdata->controller;
649  GCMotion *motion = controller.motion;
650  if (motion && motion.hasRotationRate) {
652  }
653  if (motion && motion.hasGravityAndUserAcceleration) {
655  }
656  }
657 #endif /* ENABLE_MFI_SENSORS */
658 
659 #endif /* SDL_JOYSTICK_MFI */
660  }
661  }
662  if (device->remote) {
664  }
665 
666  return 0;
667 }
668 
669 static void
671 {
672 #ifdef SDL_JOYSTICK_iOS_ACCELEROMETER
673  const float maxgforce = SDL_IPHONE_MAX_GFORCE;
674  const SInt16 maxsint16 = 0x7FFF;
675  CMAcceleration accel;
676 
677  @autoreleasepool {
678  if (!motionManager.isAccelerometerActive) {
679  return;
680  }
681 
682  accel = motionManager.accelerometerData.acceleration;
683  }
684 
685  /*
686  Convert accelerometer data from floating point to Sint16, which is what
687  the joystick system expects.
688 
689  To do the conversion, the data is first clamped onto the interval
690  [-SDL_IPHONE_MAX_G_FORCE, SDL_IPHONE_MAX_G_FORCE], then the data is multiplied
691  by MAX_SINT16 so that it is mapped to the full range of an Sint16.
692 
693  You can customize the clamped range of this function by modifying the
694  SDL_IPHONE_MAX_GFORCE macro in SDL_config_iphoneos.h.
695 
696  Once converted to Sint16, the accelerometer data no longer has coherent
697  units. You can convert the data back to units of g-force by multiplying
698  it in your application's code by SDL_IPHONE_MAX_GFORCE / 0x7FFF.
699  */
700 
701  /* clamp the data */
702  accel.x = SDL_min(SDL_max(accel.x, -maxgforce), maxgforce);
703  accel.y = SDL_min(SDL_max(accel.y, -maxgforce), maxgforce);
704  accel.z = SDL_min(SDL_max(accel.z, -maxgforce), maxgforce);
705 
706  /* pass in data mapped to range of SInt16 */
707  SDL_PrivateJoystickAxis(joystick, 0, (accel.x / maxgforce) * maxsint16);
708  SDL_PrivateJoystickAxis(joystick, 1, -(accel.y / maxgforce) * maxsint16);
709  SDL_PrivateJoystickAxis(joystick, 2, (accel.z / maxgforce) * maxsint16);
710 #endif /* SDL_JOYSTICK_iOS_ACCELEROMETER */
711 }
712 
713 #ifdef SDL_JOYSTICK_MFI
714 static Uint8
715 IOS_MFIJoystickHatStateForDPad(GCControllerDirectionPad *dpad)
716 {
717  Uint8 hat = 0;
718 
719  if (dpad.up.isPressed) {
720  hat |= SDL_HAT_UP;
721  } else if (dpad.down.isPressed) {
722  hat |= SDL_HAT_DOWN;
723  }
724 
725  if (dpad.left.isPressed) {
726  hat |= SDL_HAT_LEFT;
727  } else if (dpad.right.isPressed) {
728  hat |= SDL_HAT_RIGHT;
729  }
730 
731  if (hat == 0) {
732  return SDL_HAT_CENTERED;
733  }
734 
735  return hat;
736 }
737 #endif
738 
739 static void
741 {
742 #if SDL_JOYSTICK_MFI
743  @autoreleasepool {
744  GCController *controller = joystick->hwdata->controller;
745  Uint8 hatstate = SDL_HAT_CENTERED;
746  int i;
747  int pause_button_index = 0;
748 
749  if (controller.extendedGamepad) {
750  GCExtendedGamepad *gamepad = controller.extendedGamepad;
751 
752  /* Axis order matches the XInput Windows mappings. */
753  Sint16 axes[] = {
754  (Sint16) (gamepad.leftThumbstick.xAxis.value * 32767),
755  (Sint16) (gamepad.leftThumbstick.yAxis.value * -32767),
756  (Sint16) ((gamepad.leftTrigger.value * 65535) - 32768),
757  (Sint16) (gamepad.rightThumbstick.xAxis.value * 32767),
758  (Sint16) (gamepad.rightThumbstick.yAxis.value * -32767),
759  (Sint16) ((gamepad.rightTrigger.value * 65535) - 32768),
760  };
761 
762  /* Button order matches the XInput Windows mappings. */
763  Uint8 buttons[joystick->nbuttons];
764  int button_count = 0;
765 
766  /* These buttons are part of the original MFi spec */
767  buttons[button_count++] = gamepad.buttonA.isPressed;
768  buttons[button_count++] = gamepad.buttonB.isPressed;
769  buttons[button_count++] = gamepad.buttonX.isPressed;
770  buttons[button_count++] = gamepad.buttonY.isPressed;
771  buttons[button_count++] = gamepad.leftShoulder.isPressed;
772  buttons[button_count++] = gamepad.rightShoulder.isPressed;
773 
774  /* These buttons are available on some newer controllers */
775 #pragma clang diagnostic push
776 #pragma clang diagnostic ignored "-Wunguarded-availability-new"
777  if (joystick->hwdata->button_mask & (1 << SDL_CONTROLLER_BUTTON_LEFTSTICK)) {
778  buttons[button_count++] = gamepad.leftThumbstickButton.isPressed;
779  }
780  if (joystick->hwdata->button_mask & (1 << SDL_CONTROLLER_BUTTON_RIGHTSTICK)) {
781  buttons[button_count++] = gamepad.rightThumbstickButton.isPressed;
782  }
783  if (joystick->hwdata->button_mask & (1 << SDL_CONTROLLER_BUTTON_BACK)) {
784  buttons[button_count++] = gamepad.buttonOptions.isPressed;
785  }
786  if (joystick->hwdata->button_mask & (1 << SDL_CONTROLLER_BUTTON_GUIDE)) {
787  buttons[button_count++] = gamepad.buttonHome.isPressed;
788  }
789  /* This must be the last button, so we can optionally handle it with pause_button_index below */
790  if (joystick->hwdata->button_mask & (1 << SDL_CONTROLLER_BUTTON_START)) {
791  if (joystick->hwdata->uses_pause_handler) {
792  pause_button_index = button_count;
793  buttons[button_count++] = joystick->delayed_guide_button;
794  } else {
795  buttons[button_count++] = gamepad.buttonMenu.isPressed;
796  }
797  }
798 
799 #ifdef ENABLE_PHYSICAL_INPUT_PROFILE
800  if (joystick->hwdata->has_dualshock_touchpad) {
801  buttons[button_count++] = controller.physicalInputProfile.buttons[GCInputDualShockTouchpadButton].isPressed;
802 
803  GCControllerDirectionPad *dpad;
804 
805  dpad = controller.physicalInputProfile.dpads[GCInputDualShockTouchpadOne];
806  if (dpad.xAxis.value || dpad.yAxis.value) {
807  SDL_PrivateJoystickTouchpad(joystick, 0, 0, SDL_PRESSED, (1.0f + dpad.xAxis.value) * 0.5f, 1.0f - (1.0f + dpad.yAxis.value) * 0.5f, 1.0f);
808  } else {
810  }
811 
812  dpad = controller.physicalInputProfile.dpads[GCInputDualShockTouchpadTwo];
813  if (dpad.xAxis.value || dpad.yAxis.value) {
814  SDL_PrivateJoystickTouchpad(joystick, 0, 1, SDL_PRESSED, (1.0f + dpad.xAxis.value) * 0.5f, 1.0f - (1.0f + dpad.yAxis.value) * 0.5f, 1.0f);
815  } else {
817  }
818  }
819 
820  if (joystick->hwdata->has_xbox_paddles) {
821  if (joystick->hwdata->button_mask & (1 << SDL_CONTROLLER_BUTTON_PADDLE1)) {
822  buttons[button_count++] = controller.physicalInputProfile.buttons[GCInputXboxPaddleOne].isPressed;
823  }
824  if (joystick->hwdata->button_mask & (1 << SDL_CONTROLLER_BUTTON_PADDLE2)) {
825  buttons[button_count++] = controller.physicalInputProfile.buttons[GCInputXboxPaddleTwo].isPressed;
826  }
827  if (joystick->hwdata->button_mask & (1 << SDL_CONTROLLER_BUTTON_PADDLE3)) {
828  buttons[button_count++] = controller.physicalInputProfile.buttons[GCInputXboxPaddleThree].isPressed;
829  }
830  if (joystick->hwdata->button_mask & (1 << SDL_CONTROLLER_BUTTON_PADDLE4)) {
831  buttons[button_count++] = controller.physicalInputProfile.buttons[GCInputXboxPaddleFour].isPressed;
832  }
833 
834  /*
835  SDL_Log("Paddles: [%d,%d,%d,%d]",
836  controller.physicalInputProfile.buttons[GCInputXboxPaddleOne].isPressed,
837  controller.physicalInputProfile.buttons[GCInputXboxPaddleTwo].isPressed,
838  controller.physicalInputProfile.buttons[GCInputXboxPaddleThree].isPressed,
839  controller.physicalInputProfile.buttons[GCInputXboxPaddleFour].isPressed);
840  */
841  }
842 #endif
843 #pragma clang diagnostic pop
844 
845  hatstate = IOS_MFIJoystickHatStateForDPad(gamepad.dpad);
846 
847  for (i = 0; i < SDL_arraysize(axes); i++) {
849  }
850 
851  for (i = 0; i < button_count; i++) {
853  }
854 
855 #ifdef ENABLE_MFI_SENSORS
856  if (@available(macOS 11.0, iOS 14.0, tvOS 14.0, *)) {
857  GCMotion *motion = controller.motion;
858  if (motion && motion.sensorsActive) {
859  float data[3];
860 
861  if (motion.hasRotationRate) {
862  GCRotationRate rate = motion.rotationRate;
863  data[0] = rate.x;
864  data[1] = rate.z;
865  data[2] = -rate.y;
867  }
868  if (motion.hasGravityAndUserAcceleration) {
869  GCAcceleration accel = motion.acceleration;
870  data[0] = -accel.x * SDL_STANDARD_GRAVITY;
871  data[1] = -accel.y * SDL_STANDARD_GRAVITY;
872  data[2] = -accel.z * SDL_STANDARD_GRAVITY;
874  }
875  }
876  }
877 #endif /* ENABLE_MFI_SENSORS */
878 
879  } else if (controller.gamepad) {
880  GCGamepad *gamepad = controller.gamepad;
881 
882  /* Button order matches the XInput Windows mappings. */
883  Uint8 buttons[joystick->nbuttons];
884  int button_count = 0;
885  buttons[button_count++] = gamepad.buttonA.isPressed;
886  buttons[button_count++] = gamepad.buttonB.isPressed;
887  buttons[button_count++] = gamepad.buttonX.isPressed;
888  buttons[button_count++] = gamepad.buttonY.isPressed;
889  buttons[button_count++] = gamepad.leftShoulder.isPressed;
890  buttons[button_count++] = gamepad.rightShoulder.isPressed;
891  pause_button_index = button_count;
892  buttons[button_count++] = joystick->delayed_guide_button;
893 
894  hatstate = IOS_MFIJoystickHatStateForDPad(gamepad.dpad);
895 
896  for (i = 0; i < button_count; i++) {
898  }
899  }
900 #if TARGET_OS_TV
901  else if (controller.microGamepad) {
902  GCMicroGamepad *gamepad = controller.microGamepad;
903 
904  Sint16 axes[] = {
905  (Sint16) (gamepad.dpad.xAxis.value * 32767),
906  (Sint16) (gamepad.dpad.yAxis.value * -32767),
907  };
908 
909  for (i = 0; i < SDL_arraysize(axes); i++) {
911  }
912 
913  Uint8 buttons[joystick->nbuttons];
914  int button_count = 0;
915  buttons[button_count++] = gamepad.buttonA.isPressed;
916  buttons[button_count++] = gamepad.buttonX.isPressed;
917 #pragma clang diagnostic push
918 #pragma clang diagnostic ignored "-Wunguarded-availability-new"
919  /* This must be the last button, so we can optionally handle it with pause_button_index below */
920  if (joystick->hwdata->button_mask & (1 << SDL_CONTROLLER_BUTTON_START)) {
921  if (joystick->hwdata->uses_pause_handler) {
922  pause_button_index = button_count;
923  buttons[button_count++] = joystick->delayed_guide_button;
924  } else {
925  buttons[button_count++] = gamepad.buttonMenu.isPressed;
926  }
927  }
928 #pragma clang diagnostic pop
929 
930  for (i = 0; i < button_count; i++) {
932  }
933  }
934 #endif /* TARGET_OS_TV */
935 
936  if (joystick->nhats > 0) {
937  SDL_PrivateJoystickHat(joystick, 0, hatstate);
938  }
939 
940  if (joystick->hwdata->uses_pause_handler) {
941  for (i = 0; i < joystick->hwdata->num_pause_presses; i++) {
942  SDL_PrivateJoystickButton(joystick, pause_button_index, SDL_PRESSED);
943  SDL_PrivateJoystickButton(joystick, pause_button_index, SDL_RELEASED);
944  }
945  joystick->hwdata->num_pause_presses = 0;
946  }
947 
948 #ifdef ENABLE_MFI_BATTERY
949  if (@available(macos 11.0, iOS 14.0, tvOS 14.0, *)) {
950  GCDeviceBattery *battery = controller.battery;
951  if (battery) {
953 
954  switch (battery.batteryState) {
955  case GCDeviceBatteryStateDischarging:
956  {
957  float power_level = battery.batteryLevel;
958  if (power_level <= 0.05f) {
959  ePowerLevel = SDL_JOYSTICK_POWER_EMPTY;
960  } else if (power_level <= 0.20f) {
961  ePowerLevel = SDL_JOYSTICK_POWER_LOW;
962  } else if (power_level <= 0.70f) {
963  ePowerLevel = SDL_JOYSTICK_POWER_MEDIUM;
964  } else {
965  ePowerLevel = SDL_JOYSTICK_POWER_FULL;
966  }
967  }
968  break;
969  case GCDeviceBatteryStateCharging:
970  ePowerLevel = SDL_JOYSTICK_POWER_WIRED;
971  break;
972  case GCDeviceBatteryStateFull:
973  ePowerLevel = SDL_JOYSTICK_POWER_FULL;
974  break;
975  default:
976  break;
977  }
978 
980  }
981  }
982 #endif /* ENABLE_MFI_BATTERY */
983  }
984 #endif /* SDL_JOYSTICK_MFI */
985 }
986 
987 #ifdef ENABLE_MFI_RUMBLE
988 
989 @interface SDL_RumbleMotor : NSObject
990 @end
991 
992 @implementation SDL_RumbleMotor {
993  CHHapticEngine *engine API_AVAILABLE(macos(11.0), ios(13.0), tvos(14.0));
994  id<CHHapticPatternPlayer> player API_AVAILABLE(macos(11.0), ios(13.0), tvos(14.0));
995  bool active;
996 }
997 
998 -(void)cleanup
999 {
1000  if (self->player != nil) {
1001  [self->player cancelAndReturnError:nil];
1002  self->player = nil;
1003  }
1004  if (self->engine != nil) {
1005  [self->engine stopWithCompletionHandler:nil];
1006  self->engine = nil;
1007  }
1008 }
1009 
1010 -(int)setIntensity:(float)intensity
1011 {
1012  @autoreleasepool {
1013  if (@available(macos 11.0, iOS 14.0, tvOS 14.0, *)) {
1014  NSError *error;
1015 
1016  if (self->engine == nil) {
1017  return SDL_SetError("Haptics engine was stopped");
1018  }
1019 
1020  if (intensity == 0.0f) {
1021  if (self->player && self->active) {
1022  [self->player stopAtTime:0 error:&error];
1023  }
1024  self->active = false;
1025  return 0;
1026  }
1027 
1028  if (self->player == nil) {
1029  CHHapticEventParameter *param = [[CHHapticEventParameter alloc] initWithParameterID:CHHapticEventParameterIDHapticIntensity value:1.0f];
1030  CHHapticEvent *event = [[CHHapticEvent alloc] initWithEventType:CHHapticEventTypeHapticContinuous parameters:[NSArray arrayWithObjects:param, nil] relativeTime:0 duration:GCHapticDurationInfinite];
1031  CHHapticPattern *pattern = [[CHHapticPattern alloc] initWithEvents:[NSArray arrayWithObject:event] parameters:[[NSArray alloc] init] error:&error];
1032  if (error != nil) {
1033  return SDL_SetError("Couldn't create haptic pattern: %s", [error.localizedDescription UTF8String]);
1034  }
1035 
1036  self->player = [self->engine createPlayerWithPattern:pattern error:&error];
1037  if (error != nil) {
1038  return SDL_SetError("Couldn't create haptic player: %s", [error.localizedDescription UTF8String]);
1039  }
1040  self->active = false;
1041  }
1042 
1043  CHHapticDynamicParameter *param = [[CHHapticDynamicParameter alloc] initWithParameterID:CHHapticDynamicParameterIDHapticIntensityControl value:intensity relativeTime:0];
1044  [self->player sendParameters:[NSArray arrayWithObject:param] atTime:0 error:&error];
1045  if (error != nil) {
1046  return SDL_SetError("Couldn't update haptic player: %s", [error.localizedDescription UTF8String]);
1047  }
1048 
1049  if (!self->active) {
1050  [self->player startAtTime:0 error:&error];
1051  self->active = true;
1052  }
1053  }
1054 
1055  return 0;
1056  }
1057 }
1058 
1059 -(id) initWithController:(GCController*)controller locality:(GCHapticsLocality)locality API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0))
1060 {
1061  @autoreleasepool {
1062  self = [super init];
1063  NSError *error;
1064 
1065  self->engine = [controller.haptics createEngineWithLocality:locality];
1066  if (self->engine == nil) {
1067  SDL_SetError("Couldn't create haptics engine");
1068  return nil;
1069  }
1070 
1071  [self->engine startAndReturnError:&error];
1072  if (error != nil) {
1073  SDL_SetError("Couldn't start haptics engine");
1074  return nil;
1075  }
1076 
1077  __weak typeof(self) weakSelf = self;
1078  self->engine.stoppedHandler = ^(CHHapticEngineStoppedReason stoppedReason) {
1079  SDL_RumbleMotor *_this = weakSelf;
1080  if (_this == nil) {
1081  return;
1082  }
1083 
1084  _this->player = nil;
1085  _this->engine = nil;
1086  };
1087  self->engine.resetHandler = ^{
1088  SDL_RumbleMotor *_this = weakSelf;
1089  if (_this == nil) {
1090  return;
1091  }
1092 
1093  _this->player = nil;
1094  [_this->engine startAndReturnError:nil];
1095  };
1096 
1097  return self;
1098  }
1099 }
1100 
1101 @end
1102 
1103 @interface SDL_RumbleContext : NSObject
1104 @end
1105 
1106 @implementation SDL_RumbleContext {
1107  SDL_RumbleMotor *m_low_frequency_motor;
1108  SDL_RumbleMotor *m_high_frequency_motor;
1109  SDL_RumbleMotor *m_left_trigger_motor;
1110  SDL_RumbleMotor *m_right_trigger_motor;
1111 }
1112 
1113 -(id) initWithLowFrequencyMotor:(SDL_RumbleMotor*)low_frequency_motor
1114  HighFrequencyMotor:(SDL_RumbleMotor*)high_frequency_motor
1115  LeftTriggerMotor:(SDL_RumbleMotor*)left_trigger_motor
1116  RightTriggerMotor:(SDL_RumbleMotor*)right_trigger_motor
1117 {
1118  self = [super init];
1119  self->m_low_frequency_motor = low_frequency_motor;
1120  self->m_high_frequency_motor = high_frequency_motor;
1121  self->m_left_trigger_motor = left_trigger_motor;
1122  self->m_right_trigger_motor = right_trigger_motor;
1123  return self;
1124 }
1125 
1126 -(int) rumbleWithLowFrequency:(Uint16)low_frequency_rumble andHighFrequency:(Uint16)high_frequency_rumble
1127 {
1128  int result = 0;
1129 
1130  result += [self->m_low_frequency_motor setIntensity:((float)low_frequency_rumble / 65535.0f)];
1131  result += [self->m_high_frequency_motor setIntensity:((float)high_frequency_rumble / 65535.0f)];
1132  return ((result < 0) ? -1 : 0);
1133 }
1134 
1135 -(int) rumbleLeftTrigger:(Uint16)left_rumble andRightTrigger:(Uint16)right_rumble
1136 {
1137  int result = 0;
1138 
1139  if (self->m_left_trigger_motor && self->m_right_trigger_motor) {
1140  result += [self->m_left_trigger_motor setIntensity:((float)left_rumble / 65535.0f)];
1141  result += [self->m_right_trigger_motor setIntensity:((float)right_rumble / 65535.0f)];
1142  } else {
1143  result = SDL_Unsupported();
1144  }
1145  return ((result < 0) ? -1 : 0);
1146 }
1147 
1148 -(void)cleanup
1149 {
1150  [self->m_low_frequency_motor cleanup];
1151  [self->m_high_frequency_motor cleanup];
1152 }
1153 
1154 @end
1155 
1156 static SDL_RumbleContext *IOS_JoystickInitRumble(GCController *controller)
1157 {
1158  @autoreleasepool {
1159  if (@available(macOS 11.0, iOS 14.0, tvOS 14.0, *)) {
1160  SDL_RumbleMotor *low_frequency_motor = [[SDL_RumbleMotor alloc] initWithController:controller locality:GCHapticsLocalityLeftHandle];
1161  SDL_RumbleMotor *high_frequency_motor = [[SDL_RumbleMotor alloc] initWithController:controller locality:GCHapticsLocalityRightHandle];
1162  SDL_RumbleMotor *left_trigger_motor = [[SDL_RumbleMotor alloc] initWithController:controller locality:GCHapticsLocalityLeftTrigger];
1163  SDL_RumbleMotor *right_trigger_motor = [[SDL_RumbleMotor alloc] initWithController:controller locality:GCHapticsLocalityRightTrigger];
1164  if (low_frequency_motor && high_frequency_motor) {
1165  return [[SDL_RumbleContext alloc] initWithLowFrequencyMotor:low_frequency_motor
1166  HighFrequencyMotor:high_frequency_motor
1167  LeftTriggerMotor:left_trigger_motor
1168  RightTriggerMotor:right_trigger_motor];
1169  }
1170  }
1171  }
1172  return nil;
1173 }
1174 
1175 #endif /* ENABLE_MFI_RUMBLE */
1176 
1177 static int
1178 IOS_JoystickRumble(SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble)
1179 {
1180 #ifdef ENABLE_MFI_RUMBLE
1182 
1183  if (@available(macOS 11.0, iOS 14.0, tvOS 14.0, *)) {
1184  if (!device->rumble && device->controller && device->controller.haptics) {
1185  SDL_RumbleContext *rumble = IOS_JoystickInitRumble(device->controller);
1186  if (rumble) {
1187  device->rumble = (void *)CFBridgingRetain(rumble);
1188  }
1189  }
1190  }
1191 
1192  if (device->rumble) {
1193  SDL_RumbleContext *rumble = (__bridge SDL_RumbleContext *)device->rumble;
1194  return [rumble rumbleWithLowFrequency:low_frequency_rumble andHighFrequency:high_frequency_rumble];
1195  } else {
1196  return SDL_Unsupported();
1197  }
1198 #else
1199  return SDL_Unsupported();
1200 #endif
1201 }
1202 
1203 static int
1204 IOS_JoystickRumbleTriggers(SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble)
1205 {
1206 #ifdef ENABLE_MFI_RUMBLE
1208 
1209  if (@available(macOS 11.0, iOS 14.0, tvOS 14.0, *)) {
1210  if (!device->rumble && device->controller && device->controller.haptics) {
1211  SDL_RumbleContext *rumble = IOS_JoystickInitRumble(device->controller);
1212  if (rumble) {
1213  device->rumble = (void *)CFBridgingRetain(rumble);
1214  }
1215  }
1216  }
1217 
1218  if (device->rumble) {
1219  SDL_RumbleContext *rumble = (__bridge SDL_RumbleContext *)device->rumble;
1220  return [rumble rumbleLeftTrigger:left_rumble andRightTrigger:right_rumble];
1221  } else {
1222  return SDL_Unsupported();
1223  }
1224 #else
1225  return SDL_Unsupported();
1226 #endif
1227 }
1228 
1229 static SDL_bool
1231 {
1232 #ifdef ENABLE_MFI_LIGHT
1233  @autoreleasepool {
1234  if (@available(macos 11.0, iOS 14.0, tvOS 14.0, *)) {
1235  GCController *controller = joystick->hwdata->controller;
1236  GCDeviceLight *light = controller.light;
1237  if (light) {
1238  return SDL_TRUE;
1239  }
1240  }
1241  }
1242 #endif /* ENABLE_MFI_LIGHT */
1243 
1244  return SDL_FALSE;
1245 }
1246 
1247 static int
1249 {
1250 #ifdef ENABLE_MFI_LIGHT
1251  @autoreleasepool {
1252  if (@available(macos 11.0, iOS 14.0, tvOS 14.0, *)) {
1253  GCController *controller = joystick->hwdata->controller;
1254  GCDeviceLight *light = controller.light;
1255  if (light) {
1256  light.color = [[GCColor alloc] initWithRed:(float)red / 255.0f
1257  green:(float)green / 255.0f
1258  blue:(float)blue / 255.0f];
1259  return 0;
1260  }
1261  }
1262  }
1263 #endif /* ENABLE_MFI_LIGHT */
1264 
1265  return SDL_Unsupported();
1266 }
1267 
1268 static int
1270 {
1271 #ifdef ENABLE_MFI_SENSORS
1272  @autoreleasepool {
1273  if (@available(macOS 11.0, iOS 14.0, tvOS 14.0, *)) {
1274  GCController *controller = joystick->hwdata->controller;
1275  GCMotion *motion = controller.motion;
1276  if (motion) {
1277  motion.sensorsActive = enabled ? YES : NO;
1278  return 0;
1279  }
1280  }
1281  }
1282 #endif /* ENABLE_MFI_SENSORS */
1283 
1284  return SDL_Unsupported();
1285 }
1286 
1287 static void
1289 {
1291 
1292  if (device == NULL) {
1293  return;
1294  }
1295 
1296  if (device->accelerometer) {
1298  } else if (device->controller) {
1300  }
1301 }
1302 
1303 static void
1305 {
1307 
1308  if (device == NULL) {
1309  return;
1310  }
1311 
1312  device->joystick = NULL;
1313 
1314  @autoreleasepool {
1315 #ifdef ENABLE_MFI_RUMBLE
1316  if (device->rumble) {
1317  SDL_RumbleContext *rumble = (__bridge SDL_RumbleContext *)device->rumble;
1318 
1319  [rumble cleanup];
1320  CFRelease(device->rumble);
1321  device->rumble = NULL;
1322  }
1323 #endif /* ENABLE_MFI_RUMBLE */
1324 
1325  if (device->accelerometer) {
1326 #ifdef SDL_JOYSTICK_iOS_ACCELEROMETER
1327  [motionManager stopAccelerometerUpdates];
1328 #endif
1329  } else if (device->controller) {
1330 #ifdef SDL_JOYSTICK_MFI
1331  GCController *controller = device->controller;
1332  controller.controllerPausedHandler = nil;
1333  controller.playerIndex = -1;
1334 #endif
1335  }
1336  }
1337  if (device->remote) {
1339  }
1340 }
1341 
1342 static void
1344 {
1345  @autoreleasepool {
1346 #ifdef SDL_JOYSTICK_MFI
1347  NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
1348 
1349  if (connectObserver) {
1350  [center removeObserver:connectObserver name:GCControllerDidConnectNotification object:nil];
1351  connectObserver = nil;
1352  }
1353 
1354  if (disconnectObserver) {
1355  [center removeObserver:disconnectObserver name:GCControllerDidDisconnectNotification object:nil];
1356  disconnectObserver = nil;
1357  }
1358 
1359 #if TARGET_OS_TV
1361  SDL_AppleTVRemoteRotationHintChanged, NULL);
1362 #endif /* TARGET_OS_TV */
1363 #endif /* SDL_JOYSTICK_MFI */
1364 
1365  while (deviceList != NULL) {
1367  }
1368 
1369 #ifdef SDL_JOYSTICK_iOS_ACCELEROMETER
1370  motionManager = nil;
1371 #endif
1372  }
1373 
1374  numjoysticks = 0;
1375 }
1376 
1377 static SDL_bool
1379 {
1380  return SDL_FALSE;
1381 }
1382 
1383 #if defined(SDL_JOYSTICK_MFI) && defined(__MACOSX__)
1384 SDL_bool IOS_SupportedHIDDevice(IOHIDDeviceRef device)
1385 {
1386  if (is_macos11()) {
1387  return [GCController supportsHIDDevice:device] ? SDL_TRUE : SDL_FALSE;
1388  }
1389  return SDL_FALSE;
1390 }
1391 #endif
1392 
1394 {
1413 };
1414 
1415 /* vi: set ts=4 sw=4 expandtab: */
#define SDL_SetError
#define SDL_DelHintCallback
#define SDL_strlen
#define SDL_free
#define SDL_strdup
#define SDL_GetHintBoolean
#define SDL_memcpy
#define SDL_AddHintCallback
#define SDL_calloc
SDL_PRINTF_FORMAT_STRING const char int SDL_PRINTF_FORMAT_STRING const char int SDL_PRINTF_FORMAT_STRING const char int SDL_PRINTF_FORMAT_STRING const char const char SDL_SCANF_FORMAT_STRING const char return SDL_ThreadFunction const char void return Uint32 return Uint32 void
#define SDL_SwapLE16(X)
Definition: SDL_endian.h:235
#define SDL_Unsupported()
Definition: SDL_error.h:89
SDL_atomic_t active
Definition: SDL_events.c:84
#define SDL_RELEASED
Definition: SDL_events.h:49
#define SDL_PRESSED
Definition: SDL_events.h:50
@ SDL_CONTROLLER_BUTTON_B
@ SDL_CONTROLLER_BUTTON_BACK
@ SDL_CONTROLLER_BUTTON_LEFTSTICK
@ SDL_CONTROLLER_BUTTON_START
@ SDL_CONTROLLER_BUTTON_PADDLE2
@ SDL_CONTROLLER_BUTTON_PADDLE1
@ SDL_CONTROLLER_BUTTON_RIGHTSHOULDER
@ SDL_CONTROLLER_BUTTON_LEFTSHOULDER
@ SDL_CONTROLLER_BUTTON_GUIDE
@ SDL_CONTROLLER_BUTTON_MISC1
@ SDL_CONTROLLER_BUTTON_X
@ SDL_CONTROLLER_BUTTON_RIGHTSTICK
@ SDL_CONTROLLER_BUTTON_PADDLE3
@ SDL_CONTROLLER_BUTTON_Y
@ SDL_CONTROLLER_BUTTON_A
@ SDL_CONTROLLER_BUTTON_PADDLE4
const GLubyte GLuint red
Definition: SDL_glfuncs.h:80
const GLubyte GLuint GLuint GLuint GLuint alpha GLboolean GLboolean GLboolean GLboolean alpha GLint GLint GLsizei GLsizei GLenum type GLenum GLint GLenum GLint GLint GLsizei GLsizei GLint border GLenum GLint GLint GLint GLint GLint GLsizei GLsizei height GLsizei GLsizei GLenum GLenum const GLvoid *pixels GLenum GLint GLint GLint GLint j2 GLdouble GLdouble GLdouble GLdouble GLdouble GLdouble zFar GLenum light
Definition: SDL_glfuncs.h:167
#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:454
#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:464
#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:433
#define SDLCALL
Definition: SDL_internal.h:49
int SDL_PrivateJoystickTouchpad(SDL_Joystick *joystick, int touchpad, int finger, Uint8 state, float x, float y, float pressure)
void SDL_PrivateJoystickBatteryLevel(SDL_Joystick *joystick, SDL_JoystickPowerLevel ePowerLevel)
int SDL_PrivateJoystickHat(SDL_Joystick *joystick, Uint8 hat, Uint8 value)
void SDL_PrivateJoystickRemoved(SDL_JoystickID device_instance)
int SDL_PrivateJoystickAxis(SDL_Joystick *joystick, Uint8 axis, Sint16 value)
void SDL_PrivateJoystickAddTouchpad(SDL_Joystick *joystick, int nfingers)
void SDL_PrivateJoystickAdded(SDL_JoystickID device_instance)
int SDL_PrivateJoystickSensor(SDL_Joystick *joystick, SDL_SensorType type, const float *data, int num_values)
void SDL_PrivateJoystickAddSensor(SDL_Joystick *joystick, SDL_SensorType type)
SDL_JoystickID SDL_GetNextJoystickInstanceID()
Definition: SDL_joystick.c:262
int SDL_PrivateJoystickButton(SDL_Joystick *joystick, Uint8 button, Uint8 state)
char * SDL_CreateJoystickName(Uint16 vendor, Uint16 product, const char *vendor_name, const char *product_name)
#define SDL_IPHONE_MAX_GFORCE
Definition: SDL_joystick.h:111
SDL_JoystickPowerLevel
Definition: SDL_joystick.h:98
@ SDL_JOYSTICK_POWER_FULL
Definition: SDL_joystick.h:103
@ SDL_JOYSTICK_POWER_MEDIUM
Definition: SDL_joystick.h:102
@ SDL_JOYSTICK_POWER_EMPTY
Definition: SDL_joystick.h:100
@ SDL_JOYSTICK_POWER_UNKNOWN
Definition: SDL_joystick.h:99
@ SDL_JOYSTICK_POWER_WIRED
Definition: SDL_joystick.h:104
@ SDL_JOYSTICK_POWER_LOW
Definition: SDL_joystick.h:101
#define SDL_HAT_LEFT
Definition: SDL_joystick.h:390
#define SDL_HAT_RIGHT
Definition: SDL_joystick.h:388
Sint32 SDL_JoystickID
Definition: SDL_joystick.h:81
#define SDL_HAT_DOWN
Definition: SDL_joystick.h:389
#define SDL_HAT_UP
Definition: SDL_joystick.h:387
#define SDL_HAT_CENTERED
Definition: SDL_joystick.h:386
static int IOS_JoystickGetDevicePlayerIndex(int device_index)
static void IOS_JoystickDetect(void)
static int IOS_JoystickSetLED(SDL_Joystick *joystick, Uint8 red, Uint8 green, Uint8 blue)
static SDL_JoystickDeviceItem * deviceList
static void IOS_JoystickClose(SDL_Joystick *joystick)
static void IOS_AccelerometerUpdate(SDL_Joystick *joystick)
SDL_JoystickDriver SDL_IOS_JoystickDriver
static SDL_JoystickGUID IOS_JoystickGetDeviceGUID(int device_index)
static void IOS_JoystickSetDevicePlayerIndex(int device_index, int player_index)
static void IOS_JoystickUpdate(SDL_Joystick *joystick)
static int IOS_JoystickOpen(SDL_Joystick *joystick, int device_index)
static int IOS_JoystickRumbleTriggers(SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble)
static int IOS_JoystickSetSensorsEnabled(SDL_Joystick *joystick, SDL_bool enabled)
static void IOS_MFIJoystickUpdate(SDL_Joystick *joystick)
static SDL_JoystickDeviceItem * IOS_RemoveJoystickDevice(SDL_JoystickDeviceItem *device)
static int IOS_JoystickGetCount(void)
static int IOS_JoystickRumble(SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble)
static const char * IOS_JoystickGetDeviceName(int device_index)
static void IOS_JoystickQuit(void)
static int IOS_JoystickInit(void)
static SDL_bool IOS_JoystickGetGamepadMapping(int device_index, SDL_GamepadMapping *out)
static SDL_bool IOS_JoystickHasLED(SDL_Joystick *joystick)
static SDL_JoystickDeviceItem * GetDeviceForIndex(int device_index)
static SDL_JoystickID IOS_JoystickGetDeviceInstanceID(int device_index)
int SDL_AppleTVRemoteOpenedAsJoystick
static int numjoysticks
GLint GLenum GLsizei GLsizei GLsizei GLint GLsizei const GLvoid * data
Definition: SDL_opengl.h:1974
GLenum GLenum GLsizei const GLuint GLboolean enabled
GLfloat f
GLbyte GLbyte blue
GLuint64EXT * result
GLuint id
GLfloat param
const GLubyte * c
GLuint in
GLuint const GLchar * name
GLbyte green
GLubyte * pattern
#define SDL_STANDARD_GRAVITY
Definition: SDL_sensor.h:99
@ SDL_SENSOR_GYRO
Definition: SDL_sensor.h:74
@ SDL_SENSOR_ACCEL
Definition: SDL_sensor.h:73
uint16_t Uint16
Definition: SDL_stdinc.h:197
int16_t Sint16
Definition: SDL_stdinc.h:191
#define SDL_zero(x)
Definition: SDL_stdinc.h:426
SDL_bool
Definition: SDL_stdinc.h:168
@ SDL_TRUE
Definition: SDL_stdinc.h:170
@ SDL_FALSE
Definition: SDL_stdinc.h:169
#define SDL_arraysize(array)
Definition: SDL_stdinc.h:121
uint8_t Uint8
Definition: SDL_stdinc.h:185
#define SDL_min(x, y)
Definition: SDL_stdinc.h:412
#define SDL_max(x, y)
Definition: SDL_stdinc.h:413
#define SDL_HARDWARE_BUS_BLUETOOTH
static SDL_VideoDevice * _this
Definition: SDL_video.c:126
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
#define NULL
Definition: begin_code.h:163
#define FALSE
Definition: edid-parse.c:34
static SDL_AudioDeviceID device
Definition: loopwave.c:37
set set set set set set set macro pixldst1 abits if abits op else op endif endm macro pixldst2 abits if abits op else op endif endm macro pixldst4 abits if abits op else op endif endm macro pixldst0 abits op endm macro pixldst3 mem_operand op endm macro pixldst30 mem_operand op endm macro pixldst abits if abits elseif abits elseif abits elseif abits elseif abits pixldst0 abits else pixldst0 abits pixldst0 abits pixldst0 abits pixldst0 abits endif elseif abits else pixldst0 abits pixldst0 abits endif elseif abits else error unsupported bpp *numpix else pixst endif endm macro pixld1_s mem_operand if asr adds SRC_WIDTH_FIXED bpl add asl mov asr adds SRC_WIDTH_FIXED bpl add asl mov asr adds SRC_WIDTH_FIXED bpl add asl mov asr adds SRC_WIDTH_FIXED bpl add asl elseif asr adds SRC_WIDTH_FIXED bpl add asl mov asr adds SRC_WIDTH_FIXED bpl add asl else error unsupported endif endm macro pixld2_s mem_operand if mov asr add asl add asl mov asr sub UNIT_X add asl mov asr add asl add asl mov asr add UNIT_X add asl else pixld1_s mem_operand pixld1_s mem_operand endif endm macro pixld0_s mem_operand if asr adds SRC_WIDTH_FIXED bpl add asl elseif asr adds SRC_WIDTH_FIXED bpl add asl endif endm macro pixld_s_internal mem_operand if mem_operand pixld2_s mem_operand pixdeinterleave basereg elseif mem_operand elseif mem_operand elseif mem_operand elseif mem_operand pixld0_s mem_operand else pixld0_s mem_operand pixld0_s mem_operand pixld0_s mem_operand pixld0_s mem_operand endif elseif mem_operand else pixld0_s mem_operand pixld0_s mem_operand endif elseif mem_operand else error unsupported mem_operand if bpp mem_operand endif endm macro vuzp8 reg2 vuzp d d &reg2 endm macro vzip8 reg2 vzip d d &reg2 endm macro pixdeinterleave basereg basereg basereg basereg basereg endif endm macro pixinterleave basereg basereg basereg basereg basereg endif endm macro PF boost_increment endif if endif PF tst PF addne PF subne PF cmp ORIG_W if endif if endif if endif PF subge ORIG_W PF subges if endif if endif if endif endif endm macro cache_preload_simple endif if dst_r_bpp pld[DST_R, #(PREFETCH_DISTANCE_SIMPLE *dst_r_bpp/8)] endif if mask_bpp pld cleanup[MASK, #(PREFETCH_DISTANCE_SIMPLE *mask_bpp/8)] endif endif endm macro fetch_mask_pixblock pixld mask_basereg pixblock_size MASK endm macro ensure_destination_ptr_alignment process_pixblock_tail_head if beq irp skip1 beq endif SRC MASK if dst_r_bpp DST_R else add endif PF add sub src_basereg pixdeinterleave mask_basereg pixdeinterleave dst_r_basereg process_pixblock_head pixblock_size cache_preload_simple process_pixblock_tail pixinterleave dst_w_basereg irp beq endif process_pixblock_tail_head tst beq irp if pixblock_size chunk_size tst beq pixld_src SRC pixld MASK if DST_R else pixld DST_R endif if src_basereg pixdeinterleave mask_basereg pixdeinterleave dst_r_basereg process_pixblock_head if pixblock_size cache_preload_simple endif process_pixblock_tail pixinterleave dst_w_basereg irp if pixblock_size chunk_size tst beq if DST_W else pixst DST_W else mov ORIG_W endif add lsl if lsl endif if lsl endif lsl endif lsl endif lsl endif subs mov DST_W if regs_shortage str endif bge start_of_loop_label endm macro generate_composite_function
double floor(double x)
Definition: s_floor.c:33
struct joystick_hwdata * next
static SDL_Joystick * joystick
Definition: testjoystick.c:37
#define USB_PRODUCT_XBOX_ONE_S_REV1_BLUETOOTH
Definition: usb_ids.h:51
#define USB_VENDOR_MICROSOFT
Definition: usb_ids.h:29
#define USB_PRODUCT_XBOX_ONE_ELITE_SERIES_2_BLUETOOTH
Definition: usb_ids.h:49
#define USB_VENDOR_APPLE
Definition: usb_ids.h:27
#define USB_PRODUCT_SONY_DS4_SLIM
Definition: usb_ids.h:45
#define USB_VENDOR_SONY
Definition: usb_ids.h:34
typedef int(__stdcall *FARPROC)()