SDL_mfijoystick.m 68 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881
  1. /*
  2. Simple DirectMedia Layer
  3. Copyright (C) 1997-2022 Sam Lantinga <slouken@libsdl.org>
  4. This software is provided 'as-is', without any express or implied
  5. warranty. In no event will the authors be held liable for any damages
  6. arising from the use of this software.
  7. Permission is granted to anyone to use this software for any purpose,
  8. including commercial applications, and to alter it and redistribute it
  9. freely, subject to the following restrictions:
  10. 1. The origin of this software must not be misrepresented; you must not
  11. claim that you wrote the original software. If you use this software
  12. in a product, an acknowledgment in the product documentation would be
  13. appreciated but is not required.
  14. 2. Altered source versions must be plainly marked as such, and must not be
  15. misrepresented as being the original software.
  16. 3. This notice may not be removed or altered from any source distribution.
  17. */
  18. #include "SDL_internal.h"
  19. /* This is the iOS implementation of the SDL joystick API */
  20. #include "../SDL_sysjoystick.h"
  21. #include "../SDL_joystick_c.h"
  22. #include "../hidapi/SDL_hidapijoystick_c.h"
  23. #include "../usb_ids.h"
  24. #include "SDL_mfijoystick_c.h"
  25. #if !SDL_EVENTS_DISABLED
  26. #include "../../events/SDL_events_c.h"
  27. #endif
  28. #if TARGET_OS_IOS
  29. #define SDL_JOYSTICK_iOS_ACCELEROMETER
  30. #import <CoreMotion/CoreMotion.h>
  31. #endif
  32. #if defined(__MACOS__)
  33. #include <IOKit/hid/IOHIDManager.h>
  34. #include <AppKit/NSApplication.h>
  35. #ifndef NSAppKitVersionNumber10_15
  36. #define NSAppKitVersionNumber10_15 1894
  37. #endif
  38. #endif /* __MACOS__ */
  39. #ifdef SDL_JOYSTICK_MFI
  40. #import <GameController/GameController.h>
  41. static id connectObserver = nil;
  42. static id disconnectObserver = nil;
  43. static NSString *GCInputXboxShareButton = @"Button Share";
  44. #include <Availability.h>
  45. #include <objc/message.h>
  46. /* remove compilation warnings for strict builds by defining these selectors, even though
  47. * they are only ever used indirectly through objc_msgSend
  48. */
  49. @interface GCController (SDL)
  50. #if defined(__MACOS__) && (__MAC_OS_X_VERSION_MAX_ALLOWED <= 101600)
  51. + (BOOL)supportsHIDDevice:(IOHIDDeviceRef)device;
  52. #endif
  53. #if !((__IPHONE_OS_VERSION_MAX_ALLOWED >= 130000) || (__APPLETV_OS_VERSION_MAX_ALLOWED >= 130000) || (__MAC_OS_VERSION_MAX_ALLOWED >= 1500000))
  54. @property(nonatomic, readonly) NSString *productCategory;
  55. #endif
  56. #if !((__IPHONE_OS_VERSION_MAX_ALLOWED >= 140500) || (__APPLETV_OS_VERSION_MAX_ALLOWED >= 140500) || (__MAC_OS_X_VERSION_MAX_ALLOWED >= 110300))
  57. @property(class, nonatomic, readwrite) BOOL shouldMonitorBackgroundEvents;
  58. #endif
  59. @end
  60. @interface GCExtendedGamepad (SDL)
  61. #if !((__IPHONE_OS_VERSION_MAX_ALLOWED >= 121000) || (__APPLETV_OS_VERSION_MAX_ALLOWED >= 121000) || (__MAC_OS_VERSION_MAX_ALLOWED >= 1401000))
  62. @property(nonatomic, readonly, nullable) GCControllerButtonInput *leftThumbstickButton;
  63. @property(nonatomic, readonly, nullable) GCControllerButtonInput *rightThumbstickButton;
  64. #endif
  65. #if !((__IPHONE_OS_VERSION_MAX_ALLOWED >= 130000) || (__APPLETV_OS_VERSION_MAX_ALLOWED >= 130000) || (__MAC_OS_VERSION_MAX_ALLOWED >= 1500000))
  66. @property(nonatomic, readonly) GCControllerButtonInput *buttonMenu;
  67. @property(nonatomic, readonly, nullable) GCControllerButtonInput *buttonOptions;
  68. #endif
  69. #if !((__IPHONE_OS_VERSION_MAX_ALLOWED >= 140000) || (__APPLETV_OS_VERSION_MAX_ALLOWED >= 140000) || (__MAC_OS_VERSION_MAX_ALLOWED > 1500000))
  70. @property(nonatomic, readonly, nullable) GCControllerButtonInput *buttonHome;
  71. #endif
  72. @end
  73. @interface GCMicroGamepad (SDL)
  74. #if !((__IPHONE_OS_VERSION_MAX_ALLOWED >= 130000) || (__APPLETV_OS_VERSION_MAX_ALLOWED >= 130000) || (__MAC_OS_VERSION_MAX_ALLOWED >= 1500000))
  75. @property(nonatomic, readonly) GCControllerButtonInput *buttonMenu;
  76. #endif
  77. @end
  78. #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)
  79. #define ENABLE_MFI_BATTERY
  80. #define ENABLE_MFI_RUMBLE
  81. #define ENABLE_MFI_LIGHT
  82. #define ENABLE_MFI_SENSORS
  83. #define ENABLE_MFI_SYSTEM_GESTURE_STATE
  84. #define ENABLE_PHYSICAL_INPUT_PROFILE
  85. #endif
  86. #ifdef ENABLE_MFI_RUMBLE
  87. #import <CoreHaptics/CoreHaptics.h>
  88. #endif
  89. #endif /* SDL_JOYSTICK_MFI */
  90. #ifdef SDL_JOYSTICK_iOS_ACCELEROMETER
  91. static const char *accelerometerName = "iOS Accelerometer";
  92. static CMMotionManager *motionManager = nil;
  93. #endif /* SDL_JOYSTICK_iOS_ACCELEROMETER */
  94. static SDL_JoystickDeviceItem *deviceList = NULL;
  95. static int numjoysticks = 0;
  96. int SDL_AppleTVRemoteOpenedAsJoystick = 0;
  97. static SDL_JoystickDeviceItem *GetDeviceForIndex(int device_index)
  98. {
  99. SDL_JoystickDeviceItem *device = deviceList;
  100. int i = 0;
  101. while (i < device_index) {
  102. if (device == NULL) {
  103. return NULL;
  104. }
  105. device = device->next;
  106. i++;
  107. }
  108. return device;
  109. }
  110. #ifdef SDL_JOYSTICK_MFI
  111. static BOOL IsControllerPS4(GCController *controller)
  112. {
  113. if (@available(macOS 10.15, iOS 13.0, tvOS 13.0, *)) {
  114. if ([controller.productCategory isEqualToString:@"DualShock 4"]) {
  115. return TRUE;
  116. }
  117. } else {
  118. if ([controller.vendorName containsString:@"DUALSHOCK"]) {
  119. return TRUE;
  120. }
  121. }
  122. return FALSE;
  123. }
  124. static BOOL IsControllerPS5(GCController *controller)
  125. {
  126. if (@available(macOS 10.15, iOS 13.0, tvOS 13.0, *)) {
  127. if ([controller.productCategory isEqualToString:@"DualSense"]) {
  128. return TRUE;
  129. }
  130. } else {
  131. if ([controller.vendorName containsString:@"DualSense"]) {
  132. return TRUE;
  133. }
  134. }
  135. return FALSE;
  136. }
  137. static BOOL IsControllerXbox(GCController *controller)
  138. {
  139. if (@available(macOS 10.15, iOS 13.0, tvOS 13.0, *)) {
  140. if ([controller.productCategory isEqualToString:@"Xbox One"]) {
  141. return TRUE;
  142. }
  143. } else {
  144. if ([controller.vendorName containsString:@"Xbox"]) {
  145. return TRUE;
  146. }
  147. }
  148. return FALSE;
  149. }
  150. static BOOL IsControllerSwitchPro(GCController *controller)
  151. {
  152. if (@available(macOS 10.15, iOS 13.0, tvOS 13.0, *)) {
  153. if ([controller.productCategory isEqualToString:@"Switch Pro Controller"]) {
  154. return TRUE;
  155. }
  156. }
  157. return FALSE;
  158. }
  159. static BOOL IsControllerSwitchJoyConL(GCController *controller)
  160. {
  161. if (@available(macOS 10.15, iOS 13.0, tvOS 13.0, *)) {
  162. if ([controller.productCategory isEqualToString:@"Nintendo Switch Joy-Con (L)"]) {
  163. return TRUE;
  164. }
  165. }
  166. return FALSE;
  167. }
  168. static BOOL IsControllerSwitchJoyConR(GCController *controller)
  169. {
  170. if (@available(macOS 10.15, iOS 13.0, tvOS 13.0, *)) {
  171. if ([controller.productCategory isEqualToString:@"Nintendo Switch Joy-Con (R)"]) {
  172. return TRUE;
  173. }
  174. }
  175. return FALSE;
  176. }
  177. static BOOL IsControllerSwitchJoyConPair(GCController *controller)
  178. {
  179. if (@available(macOS 10.15, iOS 13.0, tvOS 13.0, *)) {
  180. if ([controller.productCategory isEqualToString:@"Nintendo Switch Joy-Con (L/R)"]) {
  181. return TRUE;
  182. }
  183. }
  184. return FALSE;
  185. }
  186. static BOOL IOS_AddMFIJoystickDevice(SDL_JoystickDeviceItem *device, GCController *controller)
  187. {
  188. Uint16 vendor = 0;
  189. Uint16 product = 0;
  190. Uint8 subtype = 0;
  191. const char *name = NULL;
  192. if (@available(macOS 11.3, iOS 14.5, tvOS 14.5, *)) {
  193. if (!GCController.shouldMonitorBackgroundEvents) {
  194. GCController.shouldMonitorBackgroundEvents = YES;
  195. }
  196. }
  197. /* Explicitly retain the controller because SDL_JoystickDeviceItem is a
  198. * struct, and ARC doesn't work with structs. */
  199. device->controller = (__bridge GCController *)CFBridgingRetain(controller);
  200. if (controller.vendorName) {
  201. name = controller.vendorName.UTF8String;
  202. }
  203. if (!name) {
  204. name = "MFi Gamepad";
  205. }
  206. device->name = SDL_CreateJoystickName(0, 0, NULL, name);
  207. #ifdef DEBUG_CONTROLLER_PROFILE
  208. if (@available(macOS 10.16, iOS 14.0, tvOS 14.0, *)) {
  209. if (controller.physicalInputProfile) {
  210. for (id key in controller.physicalInputProfile.buttons) {
  211. NSLog(@"Button %@ available\n", key);
  212. }
  213. for (id key in controller.physicalInputProfile.axes) {
  214. NSLog(@"Axis %@ available\n", key);
  215. }
  216. }
  217. }
  218. #endif
  219. if (controller.extendedGamepad) {
  220. GCExtendedGamepad *gamepad = controller.extendedGamepad;
  221. BOOL is_xbox = IsControllerXbox(controller);
  222. BOOL is_ps4 = IsControllerPS4(controller);
  223. BOOL is_ps5 = IsControllerPS5(controller);
  224. BOOL is_switch_pro = IsControllerSwitchPro(controller);
  225. BOOL is_switch_joycon_pair = IsControllerSwitchJoyConPair(controller);
  226. int nbuttons = 0;
  227. BOOL has_direct_menu;
  228. #ifdef SDL_JOYSTICK_HIDAPI
  229. if ((is_xbox && HIDAPI_IsDeviceTypePresent(SDL_GAMEPAD_TYPE_XBOXONE)) ||
  230. (is_ps4 && HIDAPI_IsDeviceTypePresent(SDL_GAMEPAD_TYPE_PS4)) ||
  231. (is_ps5 && HIDAPI_IsDeviceTypePresent(SDL_GAMEPAD_TYPE_PS5)) ||
  232. (is_switch_pro && HIDAPI_IsDeviceTypePresent(SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_PRO))) {
  233. /* The HIDAPI driver is taking care of this device */
  234. return FALSE;
  235. }
  236. #endif
  237. /* These buttons are part of the original MFi spec */
  238. device->button_mask |= (1 << SDL_GAMEPAD_BUTTON_A);
  239. device->button_mask |= (1 << SDL_GAMEPAD_BUTTON_B);
  240. device->button_mask |= (1 << SDL_GAMEPAD_BUTTON_X);
  241. device->button_mask |= (1 << SDL_GAMEPAD_BUTTON_Y);
  242. device->button_mask |= (1 << SDL_GAMEPAD_BUTTON_LEFT_SHOULDER);
  243. device->button_mask |= (1 << SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER);
  244. nbuttons += 6;
  245. /* These buttons are available on some newer controllers */
  246. #pragma clang diagnostic push
  247. #pragma clang diagnostic ignored "-Wunguarded-availability-new"
  248. if ([gamepad respondsToSelector:@selector(leftThumbstickButton)] && gamepad.leftThumbstickButton) {
  249. device->button_mask |= (1 << SDL_GAMEPAD_BUTTON_LEFT_STICK);
  250. ++nbuttons;
  251. }
  252. if ([gamepad respondsToSelector:@selector(rightThumbstickButton)] && gamepad.rightThumbstickButton) {
  253. device->button_mask |= (1 << SDL_GAMEPAD_BUTTON_RIGHT_STICK);
  254. ++nbuttons;
  255. }
  256. if ([gamepad respondsToSelector:@selector(buttonOptions)] && gamepad.buttonOptions) {
  257. device->button_mask |= (1 << SDL_GAMEPAD_BUTTON_BACK);
  258. ++nbuttons;
  259. }
  260. /* The Nintendo Switch JoyCon home button doesn't ever show as being held down */
  261. if ([gamepad respondsToSelector:@selector(buttonHome)] && gamepad.buttonHome && !is_switch_joycon_pair) {
  262. device->button_mask |= (1 << SDL_GAMEPAD_BUTTON_GUIDE);
  263. ++nbuttons;
  264. }
  265. device->button_mask |= (1 << SDL_GAMEPAD_BUTTON_START);
  266. ++nbuttons;
  267. has_direct_menu = [gamepad respondsToSelector:@selector(buttonMenu)] && gamepad.buttonMenu;
  268. if (!has_direct_menu) {
  269. device->uses_pause_handler = SDL_TRUE;
  270. }
  271. #if TARGET_OS_TV
  272. /* The single menu button isn't very reliable, at least as of tvOS 16.1 */
  273. if ((device->button_mask & (1 << SDL_GAMEPAD_BUTTON_BACK)) == 0) {
  274. device->uses_pause_handler = SDL_TRUE;
  275. }
  276. #endif
  277. #ifdef ENABLE_PHYSICAL_INPUT_PROFILE
  278. if ([controller respondsToSelector:@selector(physicalInputProfile)]) {
  279. if (controller.physicalInputProfile.buttons[GCInputDualShockTouchpadButton] != nil) {
  280. device->has_dualshock_touchpad = SDL_TRUE;
  281. device->button_mask |= (1 << SDL_GAMEPAD_BUTTON_MISC1);
  282. ++nbuttons;
  283. }
  284. if (controller.physicalInputProfile.buttons[GCInputXboxPaddleOne] != nil) {
  285. device->has_xbox_paddles = SDL_TRUE;
  286. device->button_mask |= (1 << SDL_GAMEPAD_BUTTON_PADDLE1);
  287. ++nbuttons;
  288. }
  289. if (controller.physicalInputProfile.buttons[GCInputXboxPaddleTwo] != nil) {
  290. device->has_xbox_paddles = SDL_TRUE;
  291. device->button_mask |= (1 << SDL_GAMEPAD_BUTTON_PADDLE2);
  292. ++nbuttons;
  293. }
  294. if (controller.physicalInputProfile.buttons[GCInputXboxPaddleThree] != nil) {
  295. device->has_xbox_paddles = SDL_TRUE;
  296. device->button_mask |= (1 << SDL_GAMEPAD_BUTTON_PADDLE3);
  297. ++nbuttons;
  298. }
  299. if (controller.physicalInputProfile.buttons[GCInputXboxPaddleFour] != nil) {
  300. device->has_xbox_paddles = SDL_TRUE;
  301. device->button_mask |= (1 << SDL_GAMEPAD_BUTTON_PADDLE4);
  302. ++nbuttons;
  303. }
  304. if (controller.physicalInputProfile.buttons[GCInputXboxShareButton] != nil) {
  305. device->has_xbox_share_button = SDL_TRUE;
  306. device->button_mask |= (1 << SDL_GAMEPAD_BUTTON_MISC1);
  307. ++nbuttons;
  308. }
  309. }
  310. #endif
  311. #pragma clang diagnostic pop
  312. if (is_xbox) {
  313. vendor = USB_VENDOR_MICROSOFT;
  314. if (device->has_xbox_paddles) {
  315. /* Assume Xbox One Elite Series 2 Controller unless/until GCController flows VID/PID */
  316. product = USB_PRODUCT_XBOX_ONE_ELITE_SERIES_2_BLUETOOTH;
  317. subtype = 1;
  318. } else if (device->has_xbox_share_button) {
  319. /* Assume Xbox Series X Controller unless/until GCController flows VID/PID */
  320. product = USB_PRODUCT_XBOX_SERIES_X_BLE;
  321. subtype = 1;
  322. } else {
  323. /* Assume Xbox One S Bluetooth Controller unless/until GCController flows VID/PID */
  324. product = USB_PRODUCT_XBOX_ONE_S_REV1_BLUETOOTH;
  325. subtype = 0;
  326. }
  327. } else if (is_ps4) {
  328. /* Assume DS4 Slim unless/until GCController flows VID/PID */
  329. vendor = USB_VENDOR_SONY;
  330. product = USB_PRODUCT_SONY_DS4_SLIM;
  331. if (device->has_dualshock_touchpad) {
  332. subtype = 1;
  333. } else {
  334. subtype = 0;
  335. }
  336. } else if (is_ps5) {
  337. vendor = USB_VENDOR_SONY;
  338. product = USB_PRODUCT_SONY_DS5;
  339. subtype = 0;
  340. } else if (is_switch_pro) {
  341. vendor = USB_VENDOR_NINTENDO;
  342. product = USB_PRODUCT_NINTENDO_SWITCH_PRO;
  343. subtype = 0;
  344. } else if (is_switch_joycon_pair) {
  345. vendor = USB_VENDOR_NINTENDO;
  346. product = USB_PRODUCT_NINTENDO_SWITCH_JOYCON_PAIR;
  347. subtype = 0;
  348. } else {
  349. vendor = USB_VENDOR_APPLE;
  350. product = 1;
  351. subtype = 1;
  352. }
  353. if (SDL_strcmp(name, "Backbone One") == 0) {
  354. /* The Backbone app uses share button */
  355. if ((device->button_mask & (1 << SDL_GAMEPAD_BUTTON_MISC1)) != 0) {
  356. device->button_mask &= ~(1 << SDL_GAMEPAD_BUTTON_MISC1);
  357. --nbuttons;
  358. device->has_xbox_share_button = SDL_FALSE;
  359. }
  360. }
  361. device->naxes = 6; /* 2 thumbsticks and 2 triggers */
  362. device->nhats = 1; /* d-pad */
  363. device->nbuttons = nbuttons;
  364. } else if (controller.gamepad) {
  365. BOOL is_switch_joyconL = IsControllerSwitchJoyConL(controller);
  366. BOOL is_switch_joyconR = IsControllerSwitchJoyConR(controller);
  367. int nbuttons = 0;
  368. if (is_switch_joyconL) {
  369. vendor = USB_VENDOR_NINTENDO;
  370. product = USB_PRODUCT_NINTENDO_SWITCH_JOYCON_LEFT;
  371. subtype = 0;
  372. } else if (is_switch_joyconR) {
  373. vendor = USB_VENDOR_NINTENDO;
  374. product = USB_PRODUCT_NINTENDO_SWITCH_JOYCON_RIGHT;
  375. subtype = 0;
  376. } else {
  377. vendor = USB_VENDOR_APPLE;
  378. product = 2;
  379. subtype = 2;
  380. }
  381. /* These buttons are part of the original MFi spec */
  382. device->button_mask |= (1 << SDL_GAMEPAD_BUTTON_A);
  383. device->button_mask |= (1 << SDL_GAMEPAD_BUTTON_B);
  384. device->button_mask |= (1 << SDL_GAMEPAD_BUTTON_X);
  385. device->button_mask |= (1 << SDL_GAMEPAD_BUTTON_Y);
  386. device->button_mask |= (1 << SDL_GAMEPAD_BUTTON_LEFT_SHOULDER);
  387. device->button_mask |= (1 << SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER);
  388. #if TARGET_OS_TV
  389. /* The menu button is used by the OS and not available to applications */
  390. nbuttons += 6;
  391. #else
  392. device->button_mask |= (1 << SDL_GAMEPAD_BUTTON_START);
  393. nbuttons += 7;
  394. device->uses_pause_handler = SDL_TRUE;
  395. #endif
  396. device->naxes = 0; /* no traditional analog inputs */
  397. device->nhats = 1; /* d-pad */
  398. device->nbuttons = nbuttons;
  399. }
  400. #if TARGET_OS_TV
  401. else if (controller.microGamepad) {
  402. int nbuttons = 0;
  403. device->button_mask |= (1 << SDL_GAMEPAD_BUTTON_A);
  404. device->button_mask |= (1 << SDL_GAMEPAD_BUTTON_B); /* Button X on microGamepad */
  405. nbuttons += 2;
  406. device->button_mask |= (1 << SDL_GAMEPAD_BUTTON_START);
  407. ++nbuttons;
  408. device->uses_pause_handler = SDL_TRUE;
  409. vendor = USB_VENDOR_APPLE;
  410. product = 3;
  411. subtype = 3;
  412. device->naxes = 2; /* treat the touch surface as two axes */
  413. device->nhats = 0; /* apparently the touch surface-as-dpad is buggy */
  414. device->nbuttons = nbuttons;
  415. controller.microGamepad.allowsRotation = SDL_GetHintBoolean(SDL_HINT_APPLE_TV_REMOTE_ALLOW_ROTATION, SDL_FALSE);
  416. }
  417. #endif /* TARGET_OS_TV */
  418. if (vendor == USB_VENDOR_APPLE) {
  419. /* Note that this is an MFI controller and what subtype it is */
  420. device->guid = SDL_CreateJoystickGUID(SDL_HARDWARE_BUS_BLUETOOTH, vendor, product, 0, name, 'm', subtype);
  421. } else {
  422. device->guid = SDL_CreateJoystickGUID(SDL_HARDWARE_BUS_BLUETOOTH, vendor, product, 0, name, 0, subtype);
  423. }
  424. /* Update the GUID with capability bits */
  425. {
  426. Uint16 *guid16 = (Uint16 *)device->guid.data;
  427. guid16[6] = SDL_SwapLE16(device->button_mask);
  428. }
  429. /* This will be set when the first button press of the controller is
  430. * detected. */
  431. controller.playerIndex = -1;
  432. return TRUE;
  433. }
  434. #endif /* SDL_JOYSTICK_MFI */
  435. #if defined(SDL_JOYSTICK_iOS_ACCELEROMETER) || defined(SDL_JOYSTICK_MFI)
  436. static void IOS_AddJoystickDevice(GCController *controller, SDL_bool accelerometer)
  437. {
  438. SDL_JoystickDeviceItem *device = deviceList;
  439. #if TARGET_OS_TV
  440. if (!SDL_GetHintBoolean(SDL_HINT_TV_REMOTE_AS_JOYSTICK, SDL_TRUE)) {
  441. /* Ignore devices that aren't actually controllers (e.g. remotes), they'll be handled as keyboard input */
  442. if (controller && !controller.extendedGamepad && !controller.gamepad && controller.microGamepad) {
  443. return;
  444. }
  445. }
  446. #endif
  447. while (device != NULL) {
  448. if (device->controller == controller) {
  449. return;
  450. }
  451. device = device->next;
  452. }
  453. device = (SDL_JoystickDeviceItem *)SDL_calloc(1, sizeof(SDL_JoystickDeviceItem));
  454. if (device == NULL) {
  455. return;
  456. }
  457. device->accelerometer = accelerometer;
  458. device->instance_id = SDL_GetNextJoystickInstanceID();
  459. if (accelerometer) {
  460. #ifdef SDL_JOYSTICK_iOS_ACCELEROMETER
  461. device->name = SDL_strdup(accelerometerName);
  462. device->naxes = 3; /* Device acceleration in the x, y, and z axes. */
  463. device->nhats = 0;
  464. device->nbuttons = 0;
  465. /* Use the accelerometer name as a GUID. */
  466. SDL_memcpy(&device->guid.data, device->name, SDL_min(sizeof(SDL_JoystickGUID), SDL_strlen(device->name)));
  467. #else
  468. SDL_free(device);
  469. return;
  470. #endif /* SDL_JOYSTICK_iOS_ACCELEROMETER */
  471. } else if (controller) {
  472. #ifdef SDL_JOYSTICK_MFI
  473. if (!IOS_AddMFIJoystickDevice(device, controller)) {
  474. SDL_free(device);
  475. return;
  476. }
  477. #else
  478. SDL_free(device);
  479. return;
  480. #endif /* SDL_JOYSTICK_MFI */
  481. }
  482. if (deviceList == NULL) {
  483. deviceList = device;
  484. } else {
  485. SDL_JoystickDeviceItem *lastdevice = deviceList;
  486. while (lastdevice->next != NULL) {
  487. lastdevice = lastdevice->next;
  488. }
  489. lastdevice->next = device;
  490. }
  491. ++numjoysticks;
  492. SDL_PrivateJoystickAdded(device->instance_id);
  493. }
  494. #endif /* SDL_JOYSTICK_iOS_ACCELEROMETER || SDL_JOYSTICK_MFI */
  495. static SDL_JoystickDeviceItem *IOS_RemoveJoystickDevice(SDL_JoystickDeviceItem *device)
  496. {
  497. SDL_JoystickDeviceItem *prev = NULL;
  498. SDL_JoystickDeviceItem *next = NULL;
  499. SDL_JoystickDeviceItem *item = deviceList;
  500. if (device == NULL) {
  501. return NULL;
  502. }
  503. next = device->next;
  504. while (item != NULL) {
  505. if (item == device) {
  506. break;
  507. }
  508. prev = item;
  509. item = item->next;
  510. }
  511. /* Unlink the device item from the device list. */
  512. if (prev) {
  513. prev->next = device->next;
  514. } else if (device == deviceList) {
  515. deviceList = device->next;
  516. }
  517. if (device->joystick) {
  518. device->joystick->hwdata = NULL;
  519. }
  520. #ifdef SDL_JOYSTICK_MFI
  521. @autoreleasepool {
  522. if (device->controller) {
  523. /* The controller was explicitly retained in the struct, so it
  524. * should be explicitly released before freeing the struct. */
  525. GCController *controller = CFBridgingRelease((__bridge CFTypeRef)(device->controller));
  526. controller.controllerPausedHandler = nil;
  527. device->controller = nil;
  528. }
  529. }
  530. #endif /* SDL_JOYSTICK_MFI */
  531. --numjoysticks;
  532. SDL_PrivateJoystickRemoved(device->instance_id);
  533. SDL_free(device->name);
  534. SDL_free(device);
  535. return next;
  536. }
  537. #if TARGET_OS_TV
  538. static void SDLCALL SDL_AppleTVRemoteRotationHintChanged(void *udata, const char *name, const char *oldValue, const char *newValue)
  539. {
  540. BOOL allowRotation = newValue != NULL && *newValue != '0';
  541. @autoreleasepool {
  542. for (GCController *controller in [GCController controllers]) {
  543. if (controller.microGamepad) {
  544. controller.microGamepad.allowsRotation = allowRotation;
  545. }
  546. }
  547. }
  548. }
  549. #endif /* TARGET_OS_TV */
  550. static int IOS_JoystickInit(void)
  551. {
  552. #if defined(__MACOS__)
  553. #if SDL_HAS_BUILTIN(__builtin_available)
  554. if (@available(macOS 10.16, *)) {
  555. /* Continue with initialization on macOS 11+ */
  556. } else {
  557. return 0;
  558. }
  559. #else
  560. /* No @available, must be an older macOS version */
  561. return 0;
  562. #endif
  563. #endif
  564. @autoreleasepool {
  565. #ifdef SDL_JOYSTICK_MFI
  566. NSNotificationCenter *center;
  567. #endif
  568. #ifdef SDL_JOYSTICK_iOS_ACCELEROMETER
  569. if (SDL_GetHintBoolean(SDL_HINT_ACCELEROMETER_AS_JOYSTICK, SDL_TRUE)) {
  570. /* Default behavior, accelerometer as joystick */
  571. IOS_AddJoystickDevice(nil, SDL_TRUE);
  572. }
  573. #endif
  574. #ifdef SDL_JOYSTICK_MFI
  575. /* GameController.framework was added in iOS 7. */
  576. if (![GCController class]) {
  577. return 0;
  578. }
  579. /* For whatever reason, this always returns an empty array on
  580. macOS 11.0.1 */
  581. for (GCController *controller in [GCController controllers]) {
  582. IOS_AddJoystickDevice(controller, SDL_FALSE);
  583. }
  584. #if TARGET_OS_TV
  585. SDL_AddHintCallback(SDL_HINT_APPLE_TV_REMOTE_ALLOW_ROTATION,
  586. SDL_AppleTVRemoteRotationHintChanged, NULL);
  587. #endif /* TARGET_OS_TV */
  588. center = [NSNotificationCenter defaultCenter];
  589. connectObserver = [center addObserverForName:GCControllerDidConnectNotification
  590. object:nil
  591. queue:nil
  592. usingBlock:^(NSNotification *note) {
  593. GCController *controller = note.object;
  594. SDL_LockJoysticks();
  595. IOS_AddJoystickDevice(controller, SDL_FALSE);
  596. SDL_UnlockJoysticks();
  597. }];
  598. disconnectObserver = [center addObserverForName:GCControllerDidDisconnectNotification
  599. object:nil
  600. queue:nil
  601. usingBlock:^(NSNotification *note) {
  602. GCController *controller = note.object;
  603. SDL_JoystickDeviceItem *device;
  604. SDL_LockJoysticks();
  605. for (device = deviceList; device != NULL; device = device->next) {
  606. if (device->controller == controller) {
  607. IOS_RemoveJoystickDevice(device);
  608. break;
  609. }
  610. }
  611. SDL_UnlockJoysticks();
  612. }];
  613. #endif /* SDL_JOYSTICK_MFI */
  614. }
  615. return 0;
  616. }
  617. static int IOS_JoystickGetCount(void)
  618. {
  619. return numjoysticks;
  620. }
  621. static void IOS_JoystickDetect(void)
  622. {
  623. }
  624. static const char *IOS_JoystickGetDeviceName(int device_index)
  625. {
  626. SDL_JoystickDeviceItem *device = GetDeviceForIndex(device_index);
  627. return device ? device->name : "Unknown";
  628. }
  629. static const char *IOS_JoystickGetDevicePath(int device_index)
  630. {
  631. return NULL;
  632. }
  633. static int IOS_JoystickGetDevicePlayerIndex(int device_index)
  634. {
  635. #ifdef SDL_JOYSTICK_MFI
  636. SDL_JoystickDeviceItem *device = GetDeviceForIndex(device_index);
  637. if (device && device->controller) {
  638. return (int)device->controller.playerIndex;
  639. }
  640. #endif
  641. return -1;
  642. }
  643. static void IOS_JoystickSetDevicePlayerIndex(int device_index, int player_index)
  644. {
  645. #ifdef SDL_JOYSTICK_MFI
  646. SDL_JoystickDeviceItem *device = GetDeviceForIndex(device_index);
  647. if (device && device->controller) {
  648. device->controller.playerIndex = player_index;
  649. }
  650. #endif
  651. }
  652. static SDL_JoystickGUID IOS_JoystickGetDeviceGUID(int device_index)
  653. {
  654. SDL_JoystickDeviceItem *device = GetDeviceForIndex(device_index);
  655. SDL_JoystickGUID guid;
  656. if (device) {
  657. guid = device->guid;
  658. } else {
  659. SDL_zero(guid);
  660. }
  661. return guid;
  662. }
  663. static SDL_JoystickID IOS_JoystickGetDeviceInstanceID(int device_index)
  664. {
  665. SDL_JoystickDeviceItem *device = GetDeviceForIndex(device_index);
  666. return device ? device->instance_id : -1;
  667. }
  668. static int IOS_JoystickOpen(SDL_Joystick *joystick, int device_index)
  669. {
  670. SDL_JoystickDeviceItem *device = GetDeviceForIndex(device_index);
  671. if (device == NULL) {
  672. return SDL_SetError("Could not open Joystick: no hardware device for the specified index");
  673. }
  674. joystick->hwdata = device;
  675. joystick->instance_id = device->instance_id;
  676. joystick->naxes = device->naxes;
  677. joystick->nhats = device->nhats;
  678. joystick->nbuttons = device->nbuttons;
  679. if (device->has_dualshock_touchpad) {
  680. SDL_PrivateJoystickAddTouchpad(joystick, 2);
  681. }
  682. device->joystick = joystick;
  683. @autoreleasepool {
  684. if (device->accelerometer) {
  685. #ifdef SDL_JOYSTICK_iOS_ACCELEROMETER
  686. if (motionManager == nil) {
  687. motionManager = [[CMMotionManager alloc] init];
  688. }
  689. /* Shorter times between updates can significantly increase CPU usage. */
  690. motionManager.accelerometerUpdateInterval = 0.1;
  691. [motionManager startAccelerometerUpdates];
  692. #endif
  693. } else {
  694. #ifdef SDL_JOYSTICK_MFI
  695. if (device->uses_pause_handler) {
  696. GCController *controller = device->controller;
  697. controller.controllerPausedHandler = ^(GCController *c) {
  698. if (joystick->hwdata) {
  699. ++joystick->hwdata->num_pause_presses;
  700. }
  701. };
  702. }
  703. #ifdef ENABLE_MFI_SENSORS
  704. if (@available(macOS 10.16, iOS 14.0, tvOS 14.0, *)) {
  705. GCController *controller = joystick->hwdata->controller;
  706. GCMotion *motion = controller.motion;
  707. if (motion && motion.hasRotationRate) {
  708. SDL_PrivateJoystickAddSensor(joystick, SDL_SENSOR_GYRO, 0.0f);
  709. }
  710. if (motion && motion.hasGravityAndUserAcceleration) {
  711. SDL_PrivateJoystickAddSensor(joystick, SDL_SENSOR_ACCEL, 0.0f);
  712. }
  713. }
  714. #endif /* ENABLE_MFI_SENSORS */
  715. #ifdef ENABLE_MFI_SYSTEM_GESTURE_STATE
  716. if (@available(macOS 10.16, iOS 14.0, tvOS 14.0, *)) {
  717. GCController *controller = joystick->hwdata->controller;
  718. for (id key in controller.physicalInputProfile.buttons) {
  719. GCControllerButtonInput *button = controller.physicalInputProfile.buttons[key];
  720. if ([button isBoundToSystemGesture]) {
  721. button.preferredSystemGestureState = GCSystemGestureStateDisabled;
  722. }
  723. }
  724. }
  725. #endif /* ENABLE_MFI_SYSTEM_GESTURE_STATE */
  726. #endif /* SDL_JOYSTICK_MFI */
  727. }
  728. }
  729. if (device->remote) {
  730. ++SDL_AppleTVRemoteOpenedAsJoystick;
  731. }
  732. return 0;
  733. }
  734. static void IOS_AccelerometerUpdate(SDL_Joystick *joystick)
  735. {
  736. #ifdef SDL_JOYSTICK_iOS_ACCELEROMETER
  737. const float maxgforce = SDL_IPHONE_MAX_GFORCE;
  738. const SInt16 maxsint16 = 0x7FFF;
  739. CMAcceleration accel;
  740. Uint64 timestamp = SDL_GetTicksNS();
  741. @autoreleasepool {
  742. if (!motionManager.isAccelerometerActive) {
  743. return;
  744. }
  745. accel = motionManager.accelerometerData.acceleration;
  746. }
  747. /*
  748. Convert accelerometer data from floating point to Sint16, which is what
  749. the joystick system expects.
  750. To do the conversion, the data is first clamped onto the interval
  751. [-SDL_IPHONE_MAX_G_FORCE, SDL_IPHONE_MAX_G_FORCE], then the data is multiplied
  752. by MAX_SINT16 so that it is mapped to the full range of an Sint16.
  753. You can customize the clamped range of this function by modifying the
  754. SDL_IPHONE_MAX_GFORCE macro in SDL_config_ios.h.
  755. Once converted to Sint16, the accelerometer data no longer has coherent
  756. units. You can convert the data back to units of g-force by multiplying
  757. it in your application's code by SDL_IPHONE_MAX_GFORCE / 0x7FFF.
  758. */
  759. /* clamp the data */
  760. accel.x = SDL_clamp(accel.x, -maxgforce, maxgforce);
  761. accel.y = SDL_clamp(accel.y, -maxgforce, maxgforce);
  762. accel.z = SDL_clamp(accel.z, -maxgforce, maxgforce);
  763. /* pass in data mapped to range of SInt16 */
  764. SDL_SendJoystickAxis(timestamp, joystick, 0, (accel.x / maxgforce) * maxsint16);
  765. SDL_SendJoystickAxis(timestamp, joystick, 1, -(accel.y / maxgforce) * maxsint16);
  766. SDL_SendJoystickAxis(timestamp, joystick, 2, (accel.z / maxgforce) * maxsint16);
  767. #endif /* SDL_JOYSTICK_iOS_ACCELEROMETER */
  768. }
  769. #ifdef SDL_JOYSTICK_MFI
  770. static Uint8 IOS_MFIJoystickHatStateForDPad(GCControllerDirectionPad *dpad)
  771. {
  772. Uint8 hat = 0;
  773. if (dpad.up.isPressed) {
  774. hat |= SDL_HAT_UP;
  775. } else if (dpad.down.isPressed) {
  776. hat |= SDL_HAT_DOWN;
  777. }
  778. if (dpad.left.isPressed) {
  779. hat |= SDL_HAT_LEFT;
  780. } else if (dpad.right.isPressed) {
  781. hat |= SDL_HAT_RIGHT;
  782. }
  783. if (hat == 0) {
  784. return SDL_HAT_CENTERED;
  785. }
  786. return hat;
  787. }
  788. #endif
  789. static void IOS_MFIJoystickUpdate(SDL_Joystick *joystick)
  790. {
  791. #if SDL_JOYSTICK_MFI
  792. @autoreleasepool {
  793. GCController *controller = joystick->hwdata->controller;
  794. Uint8 hatstate = SDL_HAT_CENTERED;
  795. int i;
  796. int pause_button_index = 0;
  797. Uint64 timestamp = SDL_GetTicksNS();
  798. #ifdef DEBUG_CONTROLLER_STATE
  799. if (@available(macOS 10.16, iOS 14.0, tvOS 14.0, *)) {
  800. if (controller.physicalInputProfile) {
  801. for (id key in controller.physicalInputProfile.buttons) {
  802. GCControllerButtonInput *button = controller.physicalInputProfile.buttons[key];
  803. if (button.isPressed)
  804. NSLog(@"Button %@ = %s\n", key, button.isPressed ? "pressed" : "released");
  805. }
  806. for (id key in controller.physicalInputProfile.axes) {
  807. GCControllerAxisInput *axis = controller.physicalInputProfile.axes[key];
  808. if (axis.value != 0.0f)
  809. NSLog(@"Axis %@ = %g\n", key, axis.value);
  810. }
  811. }
  812. }
  813. #endif
  814. if (controller.extendedGamepad) {
  815. SDL_bool isstack;
  816. GCExtendedGamepad *gamepad = controller.extendedGamepad;
  817. /* Axis order matches the XInput Windows mappings. */
  818. Sint16 axes[] = {
  819. (Sint16)(gamepad.leftThumbstick.xAxis.value * 32767),
  820. (Sint16)(gamepad.leftThumbstick.yAxis.value * -32767),
  821. (Sint16)((gamepad.leftTrigger.value * 65535) - 32768),
  822. (Sint16)(gamepad.rightThumbstick.xAxis.value * 32767),
  823. (Sint16)(gamepad.rightThumbstick.yAxis.value * -32767),
  824. (Sint16)((gamepad.rightTrigger.value * 65535) - 32768),
  825. };
  826. /* Button order matches the XInput Windows mappings. */
  827. Uint8 *buttons = SDL_small_alloc(Uint8, joystick->nbuttons, &isstack);
  828. int button_count = 0;
  829. if (buttons == NULL) {
  830. SDL_OutOfMemory();
  831. return;
  832. }
  833. /* These buttons are part of the original MFi spec */
  834. buttons[button_count++] = gamepad.buttonA.isPressed;
  835. buttons[button_count++] = gamepad.buttonB.isPressed;
  836. buttons[button_count++] = gamepad.buttonX.isPressed;
  837. buttons[button_count++] = gamepad.buttonY.isPressed;
  838. buttons[button_count++] = gamepad.leftShoulder.isPressed;
  839. buttons[button_count++] = gamepad.rightShoulder.isPressed;
  840. /* These buttons are available on some newer controllers */
  841. #pragma clang diagnostic push
  842. #pragma clang diagnostic ignored "-Wunguarded-availability-new"
  843. if (joystick->hwdata->button_mask & (1 << SDL_GAMEPAD_BUTTON_LEFT_STICK)) {
  844. buttons[button_count++] = gamepad.leftThumbstickButton.isPressed;
  845. }
  846. if (joystick->hwdata->button_mask & (1 << SDL_GAMEPAD_BUTTON_RIGHT_STICK)) {
  847. buttons[button_count++] = gamepad.rightThumbstickButton.isPressed;
  848. }
  849. if (joystick->hwdata->button_mask & (1 << SDL_GAMEPAD_BUTTON_BACK)) {
  850. buttons[button_count++] = gamepad.buttonOptions.isPressed;
  851. }
  852. if (joystick->hwdata->button_mask & (1 << SDL_GAMEPAD_BUTTON_GUIDE)) {
  853. buttons[button_count++] = gamepad.buttonHome.isPressed;
  854. }
  855. /* This must be the last button, so we can optionally handle it with pause_button_index below */
  856. if (joystick->hwdata->button_mask & (1 << SDL_GAMEPAD_BUTTON_START)) {
  857. if (joystick->hwdata->uses_pause_handler) {
  858. pause_button_index = button_count;
  859. buttons[button_count++] = joystick->delayed_guide_button;
  860. } else {
  861. buttons[button_count++] = gamepad.buttonMenu.isPressed;
  862. }
  863. }
  864. #ifdef ENABLE_PHYSICAL_INPUT_PROFILE
  865. if (joystick->hwdata->has_dualshock_touchpad) {
  866. GCControllerDirectionPad *dpad;
  867. buttons[button_count++] = controller.physicalInputProfile.buttons[GCInputDualShockTouchpadButton].isPressed;
  868. dpad = controller.physicalInputProfile.dpads[GCInputDualShockTouchpadOne];
  869. if (dpad.xAxis.value || dpad.yAxis.value) {
  870. SDL_SendJoystickTouchpad(timestamp, joystick, 0, 0, SDL_PRESSED, (1.0f + dpad.xAxis.value) * 0.5f, 1.0f - (1.0f + dpad.yAxis.value) * 0.5f, 1.0f);
  871. } else {
  872. SDL_SendJoystickTouchpad(timestamp, joystick, 0, 0, SDL_RELEASED, 0.0f, 0.0f, 1.0f);
  873. }
  874. dpad = controller.physicalInputProfile.dpads[GCInputDualShockTouchpadTwo];
  875. if (dpad.xAxis.value || dpad.yAxis.value) {
  876. SDL_SendJoystickTouchpad(timestamp, joystick, 0, 1, SDL_PRESSED, (1.0f + dpad.xAxis.value) * 0.5f, 1.0f - (1.0f + dpad.yAxis.value) * 0.5f, 1.0f);
  877. } else {
  878. SDL_SendJoystickTouchpad(timestamp, joystick, 0, 1, SDL_RELEASED, 0.0f, 0.0f, 1.0f);
  879. }
  880. }
  881. if (joystick->hwdata->has_xbox_paddles) {
  882. if (joystick->hwdata->button_mask & (1 << SDL_GAMEPAD_BUTTON_PADDLE1)) {
  883. buttons[button_count++] = controller.physicalInputProfile.buttons[GCInputXboxPaddleOne].isPressed;
  884. }
  885. if (joystick->hwdata->button_mask & (1 << SDL_GAMEPAD_BUTTON_PADDLE2)) {
  886. buttons[button_count++] = controller.physicalInputProfile.buttons[GCInputXboxPaddleTwo].isPressed;
  887. }
  888. if (joystick->hwdata->button_mask & (1 << SDL_GAMEPAD_BUTTON_PADDLE3)) {
  889. buttons[button_count++] = controller.physicalInputProfile.buttons[GCInputXboxPaddleThree].isPressed;
  890. }
  891. if (joystick->hwdata->button_mask & (1 << SDL_GAMEPAD_BUTTON_PADDLE4)) {
  892. buttons[button_count++] = controller.physicalInputProfile.buttons[GCInputXboxPaddleFour].isPressed;
  893. }
  894. /*
  895. SDL_Log("Paddles: [%d,%d,%d,%d]",
  896. controller.physicalInputProfile.buttons[GCInputXboxPaddleOne].isPressed,
  897. controller.physicalInputProfile.buttons[GCInputXboxPaddleTwo].isPressed,
  898. controller.physicalInputProfile.buttons[GCInputXboxPaddleThree].isPressed,
  899. controller.physicalInputProfile.buttons[GCInputXboxPaddleFour].isPressed);
  900. */
  901. }
  902. if (joystick->hwdata->has_xbox_share_button) {
  903. buttons[button_count++] = controller.physicalInputProfile.buttons[GCInputXboxShareButton].isPressed;
  904. }
  905. #endif
  906. #pragma clang diagnostic pop
  907. hatstate = IOS_MFIJoystickHatStateForDPad(gamepad.dpad);
  908. for (i = 0; i < SDL_arraysize(axes); i++) {
  909. SDL_SendJoystickAxis(timestamp, joystick, i, axes[i]);
  910. }
  911. for (i = 0; i < button_count; i++) {
  912. SDL_SendJoystickButton(timestamp, joystick, i, buttons[i]);
  913. }
  914. #ifdef ENABLE_MFI_SENSORS
  915. if (@available(macOS 10.16, iOS 14.0, tvOS 14.0, *)) {
  916. GCMotion *motion = controller.motion;
  917. if (motion && motion.sensorsActive) {
  918. float data[3];
  919. if (motion.hasRotationRate) {
  920. GCRotationRate rate = motion.rotationRate;
  921. data[0] = rate.x;
  922. data[1] = rate.z;
  923. data[2] = -rate.y;
  924. SDL_SendJoystickSensor(timestamp, joystick, SDL_SENSOR_GYRO, timestamp, data, 3);
  925. }
  926. if (motion.hasGravityAndUserAcceleration) {
  927. GCAcceleration accel = motion.acceleration;
  928. data[0] = -accel.x * SDL_STANDARD_GRAVITY;
  929. data[1] = -accel.y * SDL_STANDARD_GRAVITY;
  930. data[2] = -accel.z * SDL_STANDARD_GRAVITY;
  931. SDL_SendJoystickSensor(timestamp, joystick, SDL_SENSOR_ACCEL, timestamp, data, 3);
  932. }
  933. }
  934. }
  935. #endif /* ENABLE_MFI_SENSORS */
  936. SDL_small_free(buttons, isstack);
  937. } else if (controller.gamepad) {
  938. SDL_bool isstack;
  939. GCGamepad *gamepad = controller.gamepad;
  940. /* Button order matches the XInput Windows mappings. */
  941. Uint8 *buttons = SDL_small_alloc(Uint8, joystick->nbuttons, &isstack);
  942. int button_count = 0;
  943. if (buttons == NULL) {
  944. SDL_OutOfMemory();
  945. return;
  946. }
  947. buttons[button_count++] = gamepad.buttonA.isPressed;
  948. buttons[button_count++] = gamepad.buttonB.isPressed;
  949. buttons[button_count++] = gamepad.buttonX.isPressed;
  950. buttons[button_count++] = gamepad.buttonY.isPressed;
  951. buttons[button_count++] = gamepad.leftShoulder.isPressed;
  952. buttons[button_count++] = gamepad.rightShoulder.isPressed;
  953. pause_button_index = button_count;
  954. buttons[button_count++] = joystick->delayed_guide_button;
  955. hatstate = IOS_MFIJoystickHatStateForDPad(gamepad.dpad);
  956. for (i = 0; i < button_count; i++) {
  957. SDL_SendJoystickButton(timestamp, joystick, i, buttons[i]);
  958. }
  959. SDL_small_free(buttons, isstack);
  960. }
  961. #if TARGET_OS_TV
  962. else if (controller.microGamepad) {
  963. GCMicroGamepad *gamepad = controller.microGamepad;
  964. Sint16 axes[] = {
  965. (Sint16)(gamepad.dpad.xAxis.value * 32767),
  966. (Sint16)(gamepad.dpad.yAxis.value * -32767),
  967. };
  968. for (i = 0; i < SDL_arraysize(axes); i++) {
  969. SDL_SendJoystickAxis(timestamp, joystick, i, axes[i]);
  970. }
  971. Uint8 buttons[joystick->nbuttons];
  972. int button_count = 0;
  973. buttons[button_count++] = gamepad.buttonA.isPressed;
  974. buttons[button_count++] = gamepad.buttonX.isPressed;
  975. #pragma clang diagnostic push
  976. #pragma clang diagnostic ignored "-Wunguarded-availability-new"
  977. /* This must be the last button, so we can optionally handle it with pause_button_index below */
  978. if (joystick->hwdata->button_mask & (1 << SDL_GAMEPAD_BUTTON_START)) {
  979. if (joystick->hwdata->uses_pause_handler) {
  980. pause_button_index = button_count;
  981. buttons[button_count++] = joystick->delayed_guide_button;
  982. } else {
  983. buttons[button_count++] = gamepad.buttonMenu.isPressed;
  984. }
  985. }
  986. #pragma clang diagnostic pop
  987. for (i = 0; i < button_count; i++) {
  988. SDL_SendJoystickButton(timestamp, joystick, i, buttons[i]);
  989. }
  990. }
  991. #endif /* TARGET_OS_TV */
  992. if (joystick->nhats > 0) {
  993. SDL_SendJoystickHat(timestamp, joystick, 0, hatstate);
  994. }
  995. if (joystick->hwdata->uses_pause_handler) {
  996. for (i = 0; i < joystick->hwdata->num_pause_presses; i++) {
  997. SDL_SendJoystickButton(timestamp, joystick, pause_button_index, SDL_PRESSED);
  998. SDL_SendJoystickButton(timestamp, joystick, pause_button_index, SDL_RELEASED);
  999. }
  1000. joystick->hwdata->num_pause_presses = 0;
  1001. }
  1002. #ifdef ENABLE_MFI_BATTERY
  1003. if (@available(macOS 10.16, iOS 14.0, tvOS 14.0, *)) {
  1004. GCDeviceBattery *battery = controller.battery;
  1005. if (battery) {
  1006. SDL_JoystickPowerLevel ePowerLevel = SDL_JOYSTICK_POWER_UNKNOWN;
  1007. switch (battery.batteryState) {
  1008. case GCDeviceBatteryStateDischarging:
  1009. {
  1010. float power_level = battery.batteryLevel;
  1011. if (power_level <= 0.05f) {
  1012. ePowerLevel = SDL_JOYSTICK_POWER_EMPTY;
  1013. } else if (power_level <= 0.20f) {
  1014. ePowerLevel = SDL_JOYSTICK_POWER_LOW;
  1015. } else if (power_level <= 0.70f) {
  1016. ePowerLevel = SDL_JOYSTICK_POWER_MEDIUM;
  1017. } else {
  1018. ePowerLevel = SDL_JOYSTICK_POWER_FULL;
  1019. }
  1020. } break;
  1021. case GCDeviceBatteryStateCharging:
  1022. ePowerLevel = SDL_JOYSTICK_POWER_WIRED;
  1023. break;
  1024. case GCDeviceBatteryStateFull:
  1025. ePowerLevel = SDL_JOYSTICK_POWER_FULL;
  1026. break;
  1027. default:
  1028. break;
  1029. }
  1030. SDL_SendJoystickBatteryLevel(joystick, ePowerLevel);
  1031. }
  1032. }
  1033. #endif /* ENABLE_MFI_BATTERY */
  1034. }
  1035. #endif /* SDL_JOYSTICK_MFI */
  1036. }
  1037. #ifdef ENABLE_MFI_RUMBLE
  1038. @interface SDL_RumbleMotor : NSObject
  1039. @property(nonatomic, strong) CHHapticEngine *engine API_AVAILABLE(macos(10.16), ios(13.0), tvos(14.0));
  1040. @property(nonatomic, strong) id<CHHapticPatternPlayer> player API_AVAILABLE(macos(10.16), ios(13.0), tvos(14.0));
  1041. @property bool active;
  1042. @end
  1043. @implementation SDL_RumbleMotor
  1044. {
  1045. }
  1046. - (void)cleanup
  1047. {
  1048. @autoreleasepool {
  1049. if (@available(macOS 10.16, iOS 14.0, tvOS 14.0, *)) {
  1050. if (self.player != nil) {
  1051. [self.player cancelAndReturnError:nil];
  1052. self.player = nil;
  1053. }
  1054. if (self.engine != nil) {
  1055. [self.engine stopWithCompletionHandler:nil];
  1056. self.engine = nil;
  1057. }
  1058. }
  1059. }
  1060. }
  1061. - (int)setIntensity:(float)intensity
  1062. {
  1063. @autoreleasepool {
  1064. if (@available(macOS 10.16, iOS 14.0, tvOS 14.0, *)) {
  1065. NSError *error = nil;
  1066. CHHapticDynamicParameter *param;
  1067. if (self.engine == nil) {
  1068. return SDL_SetError("Haptics engine was stopped");
  1069. }
  1070. if (intensity == 0.0f) {
  1071. if (self.player && self.active) {
  1072. [self.player stopAtTime:0 error:&error];
  1073. }
  1074. self.active = false;
  1075. return 0;
  1076. }
  1077. if (self.player == nil) {
  1078. CHHapticEventParameter *event_param = [[CHHapticEventParameter alloc] initWithParameterID:CHHapticEventParameterIDHapticIntensity value:1.0f];
  1079. CHHapticEvent *event = [[CHHapticEvent alloc] initWithEventType:CHHapticEventTypeHapticContinuous parameters:[NSArray arrayWithObjects:event_param, nil] relativeTime:0 duration:GCHapticDurationInfinite];
  1080. CHHapticPattern *pattern = [[CHHapticPattern alloc] initWithEvents:[NSArray arrayWithObject:event] parameters:[[NSArray alloc] init] error:&error];
  1081. if (error != nil) {
  1082. return SDL_SetError("Couldn't create haptic pattern: %s", [error.localizedDescription UTF8String]);
  1083. }
  1084. self.player = [self.engine createPlayerWithPattern:pattern error:&error];
  1085. if (error != nil) {
  1086. return SDL_SetError("Couldn't create haptic player: %s", [error.localizedDescription UTF8String]);
  1087. }
  1088. self.active = false;
  1089. }
  1090. param = [[CHHapticDynamicParameter alloc] initWithParameterID:CHHapticDynamicParameterIDHapticIntensityControl value:intensity relativeTime:0];
  1091. [self.player sendParameters:[NSArray arrayWithObject:param] atTime:0 error:&error];
  1092. if (error != nil) {
  1093. return SDL_SetError("Couldn't update haptic player: %s", [error.localizedDescription UTF8String]);
  1094. }
  1095. if (!self.active) {
  1096. [self.player startAtTime:0 error:&error];
  1097. self.active = true;
  1098. }
  1099. }
  1100. return 0;
  1101. }
  1102. }
  1103. - (id)initWithController:(GCController *)controller locality:(GCHapticsLocality)locality API_AVAILABLE(macos(10.16), ios(14.0), tvos(14.0))
  1104. {
  1105. @autoreleasepool {
  1106. NSError *error;
  1107. __weak __typeof(self) weakSelf;
  1108. self = [super init];
  1109. weakSelf = self;
  1110. self.engine = [controller.haptics createEngineWithLocality:locality];
  1111. if (self.engine == nil) {
  1112. SDL_SetError("Couldn't create haptics engine");
  1113. return nil;
  1114. }
  1115. [self.engine startAndReturnError:&error];
  1116. if (error != nil) {
  1117. SDL_SetError("Couldn't start haptics engine");
  1118. return nil;
  1119. }
  1120. self.engine.stoppedHandler = ^(CHHapticEngineStoppedReason stoppedReason) {
  1121. SDL_RumbleMotor *_this = weakSelf;
  1122. if (_this == nil) {
  1123. return;
  1124. }
  1125. _this.player = nil;
  1126. _this.engine = nil;
  1127. };
  1128. self.engine.resetHandler = ^{
  1129. SDL_RumbleMotor *_this = weakSelf;
  1130. if (_this == nil) {
  1131. return;
  1132. }
  1133. _this.player = nil;
  1134. [_this.engine startAndReturnError:nil];
  1135. };
  1136. return self;
  1137. }
  1138. }
  1139. @end
  1140. @interface SDL_RumbleContext : NSObject
  1141. @property(nonatomic, strong) SDL_RumbleMotor *lowFrequencyMotor;
  1142. @property(nonatomic, strong) SDL_RumbleMotor *highFrequencyMotor;
  1143. @property(nonatomic, strong) SDL_RumbleMotor *leftTriggerMotor;
  1144. @property(nonatomic, strong) SDL_RumbleMotor *rightTriggerMotor;
  1145. @end
  1146. @implementation SDL_RumbleContext
  1147. {
  1148. }
  1149. - (id)initWithLowFrequencyMotor:(SDL_RumbleMotor *)low_frequency_motor
  1150. HighFrequencyMotor:(SDL_RumbleMotor *)high_frequency_motor
  1151. LeftTriggerMotor:(SDL_RumbleMotor *)left_trigger_motor
  1152. RightTriggerMotor:(SDL_RumbleMotor *)right_trigger_motor
  1153. {
  1154. self = [super init];
  1155. self.lowFrequencyMotor = low_frequency_motor;
  1156. self.highFrequencyMotor = high_frequency_motor;
  1157. self.leftTriggerMotor = left_trigger_motor;
  1158. self.rightTriggerMotor = right_trigger_motor;
  1159. return self;
  1160. }
  1161. - (int)rumbleWithLowFrequency:(Uint16)low_frequency_rumble andHighFrequency:(Uint16)high_frequency_rumble
  1162. {
  1163. int result = 0;
  1164. result += [self.lowFrequencyMotor setIntensity:((float)low_frequency_rumble / 65535.0f)];
  1165. result += [self.highFrequencyMotor setIntensity:((float)high_frequency_rumble / 65535.0f)];
  1166. return ((result < 0) ? -1 : 0);
  1167. }
  1168. - (int)rumbleLeftTrigger:(Uint16)left_rumble andRightTrigger:(Uint16)right_rumble
  1169. {
  1170. int result = 0;
  1171. if (self.leftTriggerMotor && self.rightTriggerMotor) {
  1172. result += [self.leftTriggerMotor setIntensity:((float)left_rumble / 65535.0f)];
  1173. result += [self.rightTriggerMotor setIntensity:((float)right_rumble / 65535.0f)];
  1174. } else {
  1175. result = SDL_Unsupported();
  1176. }
  1177. return ((result < 0) ? -1 : 0);
  1178. }
  1179. - (void)cleanup
  1180. {
  1181. [self.lowFrequencyMotor cleanup];
  1182. [self.highFrequencyMotor cleanup];
  1183. }
  1184. @end
  1185. static SDL_RumbleContext *IOS_JoystickInitRumble(GCController *controller)
  1186. {
  1187. @autoreleasepool {
  1188. if (@available(macOS 10.16, iOS 14.0, tvOS 14.0, *)) {
  1189. SDL_RumbleMotor *low_frequency_motor = [[SDL_RumbleMotor alloc] initWithController:controller locality:GCHapticsLocalityLeftHandle];
  1190. SDL_RumbleMotor *high_frequency_motor = [[SDL_RumbleMotor alloc] initWithController:controller locality:GCHapticsLocalityRightHandle];
  1191. SDL_RumbleMotor *left_trigger_motor = [[SDL_RumbleMotor alloc] initWithController:controller locality:GCHapticsLocalityLeftTrigger];
  1192. SDL_RumbleMotor *right_trigger_motor = [[SDL_RumbleMotor alloc] initWithController:controller locality:GCHapticsLocalityRightTrigger];
  1193. if (low_frequency_motor && high_frequency_motor) {
  1194. return [[SDL_RumbleContext alloc] initWithLowFrequencyMotor:low_frequency_motor
  1195. HighFrequencyMotor:high_frequency_motor
  1196. LeftTriggerMotor:left_trigger_motor
  1197. RightTriggerMotor:right_trigger_motor];
  1198. }
  1199. }
  1200. }
  1201. return nil;
  1202. }
  1203. #endif /* ENABLE_MFI_RUMBLE */
  1204. static int IOS_JoystickRumble(SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble)
  1205. {
  1206. #ifdef ENABLE_MFI_RUMBLE
  1207. SDL_JoystickDeviceItem *device = joystick->hwdata;
  1208. if (device == NULL) {
  1209. return SDL_SetError("Controller is no longer connected");
  1210. }
  1211. if (@available(macOS 10.16, iOS 14.0, tvOS 14.0, *)) {
  1212. if (!device->rumble && device->controller && device->controller.haptics) {
  1213. SDL_RumbleContext *rumble = IOS_JoystickInitRumble(device->controller);
  1214. if (rumble) {
  1215. device->rumble = (void *)CFBridgingRetain(rumble);
  1216. }
  1217. }
  1218. }
  1219. if (device->rumble) {
  1220. SDL_RumbleContext *rumble = (__bridge SDL_RumbleContext *)device->rumble;
  1221. return [rumble rumbleWithLowFrequency:low_frequency_rumble andHighFrequency:high_frequency_rumble];
  1222. } else {
  1223. return SDL_Unsupported();
  1224. }
  1225. #else
  1226. return SDL_Unsupported();
  1227. #endif
  1228. }
  1229. static int IOS_JoystickRumbleTriggers(SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble)
  1230. {
  1231. #ifdef ENABLE_MFI_RUMBLE
  1232. SDL_JoystickDeviceItem *device = joystick->hwdata;
  1233. if (device == NULL) {
  1234. return SDL_SetError("Controller is no longer connected");
  1235. }
  1236. if (@available(macOS 10.16, iOS 14.0, tvOS 14.0, *)) {
  1237. if (!device->rumble && device->controller && device->controller.haptics) {
  1238. SDL_RumbleContext *rumble = IOS_JoystickInitRumble(device->controller);
  1239. if (rumble) {
  1240. device->rumble = (void *)CFBridgingRetain(rumble);
  1241. }
  1242. }
  1243. }
  1244. if (device->rumble) {
  1245. SDL_RumbleContext *rumble = (__bridge SDL_RumbleContext *)device->rumble;
  1246. return [rumble rumbleLeftTrigger:left_rumble andRightTrigger:right_rumble];
  1247. } else {
  1248. return SDL_Unsupported();
  1249. }
  1250. #else
  1251. return SDL_Unsupported();
  1252. #endif
  1253. }
  1254. static Uint32 IOS_JoystickGetCapabilities(SDL_Joystick *joystick)
  1255. {
  1256. Uint32 result = 0;
  1257. #if defined(ENABLE_MFI_LIGHT) || defined(ENABLE_MFI_RUMBLE)
  1258. @autoreleasepool {
  1259. SDL_JoystickDeviceItem *device = joystick->hwdata;
  1260. if (device == NULL) {
  1261. return 0;
  1262. }
  1263. if (@available(macOS 10.16, iOS 14.0, tvOS 14.0, *)) {
  1264. GCController *controller = device->controller;
  1265. #ifdef ENABLE_MFI_LIGHT
  1266. if (controller.light) {
  1267. result |= SDL_JOYCAP_LED;
  1268. }
  1269. #endif
  1270. #ifdef ENABLE_MFI_RUMBLE
  1271. if (controller.haptics) {
  1272. for (GCHapticsLocality locality in controller.haptics.supportedLocalities) {
  1273. if ([locality isEqualToString:GCHapticsLocalityHandles]) {
  1274. result |= SDL_JOYCAP_RUMBLE;
  1275. } else if ([locality isEqualToString:GCHapticsLocalityTriggers]) {
  1276. result |= SDL_JOYCAP_RUMBLE_TRIGGERS;
  1277. }
  1278. }
  1279. }
  1280. #endif
  1281. }
  1282. }
  1283. #endif /* ENABLE_MFI_LIGHT || ENABLE_MFI_RUMBLE */
  1284. return result;
  1285. }
  1286. static int IOS_JoystickSetLED(SDL_Joystick *joystick, Uint8 red, Uint8 green, Uint8 blue)
  1287. {
  1288. #ifdef ENABLE_MFI_LIGHT
  1289. @autoreleasepool {
  1290. SDL_JoystickDeviceItem *device = joystick->hwdata;
  1291. if (device == NULL) {
  1292. return SDL_SetError("Controller is no longer connected");
  1293. }
  1294. if (@available(macOS 10.16, iOS 14.0, tvOS 14.0, *)) {
  1295. GCController *controller = device->controller;
  1296. GCDeviceLight *light = controller.light;
  1297. if (light) {
  1298. light.color = [[GCColor alloc] initWithRed:(float)red / 255.0f
  1299. green:(float)green / 255.0f
  1300. blue:(float)blue / 255.0f];
  1301. return 0;
  1302. }
  1303. }
  1304. }
  1305. #endif /* ENABLE_MFI_LIGHT */
  1306. return SDL_Unsupported();
  1307. }
  1308. static int IOS_JoystickSendEffect(SDL_Joystick *joystick, const void *data, int size)
  1309. {
  1310. return SDL_Unsupported();
  1311. }
  1312. static int IOS_JoystickSetSensorsEnabled(SDL_Joystick *joystick, SDL_bool enabled)
  1313. {
  1314. #ifdef ENABLE_MFI_SENSORS
  1315. @autoreleasepool {
  1316. SDL_JoystickDeviceItem *device = joystick->hwdata;
  1317. if (device == NULL) {
  1318. return SDL_SetError("Controller is no longer connected");
  1319. }
  1320. if (@available(macOS 10.16, iOS 14.0, tvOS 14.0, *)) {
  1321. GCController *controller = device->controller;
  1322. GCMotion *motion = controller.motion;
  1323. if (motion) {
  1324. motion.sensorsActive = enabled ? YES : NO;
  1325. return 0;
  1326. }
  1327. }
  1328. }
  1329. #endif /* ENABLE_MFI_SENSORS */
  1330. return SDL_Unsupported();
  1331. }
  1332. static void IOS_JoystickUpdate(SDL_Joystick *joystick)
  1333. {
  1334. SDL_JoystickDeviceItem *device = joystick->hwdata;
  1335. if (device == NULL) {
  1336. return;
  1337. }
  1338. if (device->accelerometer) {
  1339. IOS_AccelerometerUpdate(joystick);
  1340. } else if (device->controller) {
  1341. IOS_MFIJoystickUpdate(joystick);
  1342. }
  1343. }
  1344. static void IOS_JoystickClose(SDL_Joystick *joystick)
  1345. {
  1346. SDL_JoystickDeviceItem *device = joystick->hwdata;
  1347. if (device == NULL) {
  1348. return;
  1349. }
  1350. device->joystick = NULL;
  1351. @autoreleasepool {
  1352. #ifdef ENABLE_MFI_RUMBLE
  1353. if (device->rumble) {
  1354. SDL_RumbleContext *rumble = (__bridge SDL_RumbleContext *)device->rumble;
  1355. [rumble cleanup];
  1356. CFRelease(device->rumble);
  1357. device->rumble = NULL;
  1358. }
  1359. #endif /* ENABLE_MFI_RUMBLE */
  1360. if (device->accelerometer) {
  1361. #ifdef SDL_JOYSTICK_iOS_ACCELEROMETER
  1362. [motionManager stopAccelerometerUpdates];
  1363. #endif
  1364. } else if (device->controller) {
  1365. #ifdef SDL_JOYSTICK_MFI
  1366. GCController *controller = device->controller;
  1367. controller.controllerPausedHandler = nil;
  1368. controller.playerIndex = -1;
  1369. #ifdef ENABLE_MFI_SYSTEM_GESTURE_STATE
  1370. if (@available(macOS 10.16, iOS 14.0, tvOS 14.0, *)) {
  1371. for (id key in controller.physicalInputProfile.buttons) {
  1372. GCControllerButtonInput *button = controller.physicalInputProfile.buttons[key];
  1373. if ([button isBoundToSystemGesture]) {
  1374. button.preferredSystemGestureState = GCSystemGestureStateEnabled;
  1375. }
  1376. }
  1377. }
  1378. #endif /* ENABLE_MFI_SYSTEM_GESTURE_STATE */
  1379. #endif /* SDL_JOYSTICK_MFI */
  1380. }
  1381. }
  1382. if (device->remote) {
  1383. --SDL_AppleTVRemoteOpenedAsJoystick;
  1384. }
  1385. }
  1386. static void IOS_JoystickQuit(void)
  1387. {
  1388. @autoreleasepool {
  1389. #ifdef SDL_JOYSTICK_MFI
  1390. NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
  1391. if (connectObserver) {
  1392. [center removeObserver:connectObserver name:GCControllerDidConnectNotification object:nil];
  1393. connectObserver = nil;
  1394. }
  1395. if (disconnectObserver) {
  1396. [center removeObserver:disconnectObserver name:GCControllerDidDisconnectNotification object:nil];
  1397. disconnectObserver = nil;
  1398. }
  1399. #if TARGET_OS_TV
  1400. SDL_DelHintCallback(SDL_HINT_APPLE_TV_REMOTE_ALLOW_ROTATION,
  1401. SDL_AppleTVRemoteRotationHintChanged, NULL);
  1402. #endif /* TARGET_OS_TV */
  1403. #endif /* SDL_JOYSTICK_MFI */
  1404. while (deviceList != NULL) {
  1405. IOS_RemoveJoystickDevice(deviceList);
  1406. }
  1407. #ifdef SDL_JOYSTICK_iOS_ACCELEROMETER
  1408. motionManager = nil;
  1409. #endif
  1410. }
  1411. numjoysticks = 0;
  1412. }
  1413. static SDL_bool IOS_JoystickGetGamepadMapping(int device_index, SDL_GamepadMapping *out)
  1414. {
  1415. return SDL_FALSE;
  1416. }
  1417. #if defined(SDL_JOYSTICK_MFI) && defined(__MACOS__)
  1418. SDL_bool IOS_SupportedHIDDevice(IOHIDDeviceRef device)
  1419. {
  1420. if (@available(macOS 10.16, *)) {
  1421. if ([GCController supportsHIDDevice:device]) {
  1422. return SDL_TRUE;
  1423. }
  1424. /* GCController supportsHIDDevice may return false if the device hasn't been
  1425. * seen by the framework yet, so check a few controllers we know are supported.
  1426. */
  1427. {
  1428. Sint32 vendor = 0;
  1429. Sint32 product = 0;
  1430. CFTypeRef refCF = NULL;
  1431. refCF = IOHIDDeviceGetProperty(device, CFSTR(kIOHIDVendorIDKey));
  1432. if (refCF) {
  1433. CFNumberGetValue(refCF, kCFNumberSInt32Type, &vendor);
  1434. }
  1435. refCF = IOHIDDeviceGetProperty(device, CFSTR(kIOHIDProductIDKey));
  1436. if (refCF) {
  1437. CFNumberGetValue(refCF, kCFNumberSInt32Type, &product);
  1438. }
  1439. if (vendor == USB_VENDOR_MICROSOFT && SDL_IsJoystickXboxSeriesX(vendor, product)) {
  1440. return SDL_TRUE;
  1441. }
  1442. }
  1443. }
  1444. return SDL_FALSE;
  1445. }
  1446. #endif
  1447. #if defined(SDL_JOYSTICK_MFI) && defined(ENABLE_PHYSICAL_INPUT_PROFILE)
  1448. /* NOLINTNEXTLINE(readability-non-const-parameter): getCString takes a non-const char* */
  1449. static void GetAppleSFSymbolsNameForElement(GCControllerElement *element, char *name)
  1450. {
  1451. if (@available(macOS 10.16, iOS 14.0, tvOS 14.0, *)) {
  1452. if (element) {
  1453. [element.sfSymbolsName getCString:name maxLength:255 encoding:NSASCIIStringEncoding];
  1454. }
  1455. }
  1456. }
  1457. static GCControllerDirectionPad *GetDirectionalPadForController(GCController *controller)
  1458. {
  1459. if (controller.extendedGamepad) {
  1460. return controller.extendedGamepad.dpad;
  1461. }
  1462. if (controller.gamepad) {
  1463. return controller.gamepad.dpad;
  1464. }
  1465. if (controller.microGamepad) {
  1466. return controller.microGamepad.dpad;
  1467. }
  1468. return nil;
  1469. }
  1470. #endif /* SDL_JOYSTICK_MFI && ENABLE_PHYSICAL_INPUT_PROFILE */
  1471. static char elementName[256];
  1472. const char *
  1473. IOS_GetAppleSFSymbolsNameForButton(SDL_Gamepad *gamepad, SDL_GamepadButton button)
  1474. {
  1475. elementName[0] = '\0';
  1476. #if defined(SDL_JOYSTICK_MFI) && defined(ENABLE_PHYSICAL_INPUT_PROFILE)
  1477. if (gamepad && SDL_GetGamepadJoystick(gamepad)->driver == &SDL_IOS_JoystickDriver) {
  1478. if (@available(macOS 10.16, iOS 14.0, tvOS 14.0, *)) {
  1479. GCController *controller = SDL_GetGamepadJoystick(gamepad)->hwdata->controller;
  1480. if ([controller respondsToSelector:@selector(physicalInputProfile)]) {
  1481. NSDictionary<NSString *, GCControllerElement *> *elements = controller.physicalInputProfile.elements;
  1482. switch (button) {
  1483. case SDL_GAMEPAD_BUTTON_A:
  1484. GetAppleSFSymbolsNameForElement(elements[GCInputButtonA], elementName);
  1485. break;
  1486. case SDL_GAMEPAD_BUTTON_B:
  1487. GetAppleSFSymbolsNameForElement(elements[GCInputButtonB], elementName);
  1488. break;
  1489. case SDL_GAMEPAD_BUTTON_X:
  1490. GetAppleSFSymbolsNameForElement(elements[GCInputButtonX], elementName);
  1491. break;
  1492. case SDL_GAMEPAD_BUTTON_Y:
  1493. GetAppleSFSymbolsNameForElement(elements[GCInputButtonY], elementName);
  1494. break;
  1495. case SDL_GAMEPAD_BUTTON_BACK:
  1496. GetAppleSFSymbolsNameForElement(elements[GCInputButtonOptions], elementName);
  1497. break;
  1498. case SDL_GAMEPAD_BUTTON_GUIDE:
  1499. GetAppleSFSymbolsNameForElement(elements[GCInputButtonHome], elementName);
  1500. break;
  1501. case SDL_GAMEPAD_BUTTON_START:
  1502. GetAppleSFSymbolsNameForElement(elements[GCInputButtonMenu], elementName);
  1503. break;
  1504. case SDL_GAMEPAD_BUTTON_LEFT_STICK:
  1505. GetAppleSFSymbolsNameForElement(elements[GCInputLeftThumbstickButton], elementName);
  1506. break;
  1507. case SDL_GAMEPAD_BUTTON_RIGHT_STICK:
  1508. GetAppleSFSymbolsNameForElement(elements[GCInputRightThumbstickButton], elementName);
  1509. break;
  1510. case SDL_GAMEPAD_BUTTON_LEFT_SHOULDER:
  1511. GetAppleSFSymbolsNameForElement(elements[GCInputLeftShoulder], elementName);
  1512. break;
  1513. case SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER:
  1514. GetAppleSFSymbolsNameForElement(elements[GCInputRightShoulder], elementName);
  1515. break;
  1516. case SDL_GAMEPAD_BUTTON_DPAD_UP:
  1517. {
  1518. GCControllerDirectionPad *dpad = GetDirectionalPadForController(controller);
  1519. if (dpad) {
  1520. GetAppleSFSymbolsNameForElement(dpad.up, elementName);
  1521. if (SDL_strlen(elementName) == 0) {
  1522. SDL_strlcpy(elementName, "dpad.up.fill", sizeof(elementName));
  1523. }
  1524. }
  1525. break;
  1526. }
  1527. case SDL_GAMEPAD_BUTTON_DPAD_DOWN:
  1528. {
  1529. GCControllerDirectionPad *dpad = GetDirectionalPadForController(controller);
  1530. if (dpad) {
  1531. GetAppleSFSymbolsNameForElement(dpad.down, elementName);
  1532. if (SDL_strlen(elementName) == 0) {
  1533. SDL_strlcpy(elementName, "dpad.down.fill", sizeof(elementName));
  1534. }
  1535. }
  1536. break;
  1537. }
  1538. case SDL_GAMEPAD_BUTTON_DPAD_LEFT:
  1539. {
  1540. GCControllerDirectionPad *dpad = GetDirectionalPadForController(controller);
  1541. if (dpad) {
  1542. GetAppleSFSymbolsNameForElement(dpad.left, elementName);
  1543. if (SDL_strlen(elementName) == 0) {
  1544. SDL_strlcpy(elementName, "dpad.left.fill", sizeof(elementName));
  1545. }
  1546. }
  1547. break;
  1548. }
  1549. case SDL_GAMEPAD_BUTTON_DPAD_RIGHT:
  1550. {
  1551. GCControllerDirectionPad *dpad = GetDirectionalPadForController(controller);
  1552. if (dpad) {
  1553. GetAppleSFSymbolsNameForElement(dpad.right, elementName);
  1554. if (SDL_strlen(elementName) == 0) {
  1555. SDL_strlcpy(elementName, "dpad.right.fill", sizeof(elementName));
  1556. }
  1557. }
  1558. break;
  1559. }
  1560. case SDL_GAMEPAD_BUTTON_MISC1:
  1561. GetAppleSFSymbolsNameForElement(elements[GCInputDualShockTouchpadButton], elementName);
  1562. break;
  1563. case SDL_GAMEPAD_BUTTON_PADDLE1:
  1564. GetAppleSFSymbolsNameForElement(elements[GCInputXboxPaddleOne], elementName);
  1565. break;
  1566. case SDL_GAMEPAD_BUTTON_PADDLE2:
  1567. GetAppleSFSymbolsNameForElement(elements[GCInputXboxPaddleTwo], elementName);
  1568. break;
  1569. case SDL_GAMEPAD_BUTTON_PADDLE3:
  1570. GetAppleSFSymbolsNameForElement(elements[GCInputXboxPaddleThree], elementName);
  1571. break;
  1572. case SDL_GAMEPAD_BUTTON_PADDLE4:
  1573. GetAppleSFSymbolsNameForElement(elements[GCInputXboxPaddleFour], elementName);
  1574. break;
  1575. case SDL_GAMEPAD_BUTTON_TOUCHPAD:
  1576. GetAppleSFSymbolsNameForElement(elements[GCInputDualShockTouchpadButton], elementName);
  1577. break;
  1578. default:
  1579. break;
  1580. }
  1581. }
  1582. }
  1583. }
  1584. #endif
  1585. return elementName;
  1586. }
  1587. const char *
  1588. IOS_GetAppleSFSymbolsNameForAxis(SDL_Gamepad *gamepad, SDL_GamepadAxis axis)
  1589. {
  1590. elementName[0] = '\0';
  1591. #if defined(SDL_JOYSTICK_MFI) && defined(ENABLE_PHYSICAL_INPUT_PROFILE)
  1592. if (gamepad && SDL_GetGamepadJoystick(gamepad)->driver == &SDL_IOS_JoystickDriver) {
  1593. if (@available(macOS 10.16, iOS 14.0, tvOS 14.0, *)) {
  1594. GCController *controller = SDL_GetGamepadJoystick(gamepad)->hwdata->controller;
  1595. if ([controller respondsToSelector:@selector(physicalInputProfile)]) {
  1596. NSDictionary<NSString *, GCControllerElement *> *elements = controller.physicalInputProfile.elements;
  1597. switch (axis) {
  1598. case SDL_GAMEPAD_AXIS_LEFTX:
  1599. GetAppleSFSymbolsNameForElement(elements[GCInputLeftThumbstick], elementName);
  1600. break;
  1601. case SDL_GAMEPAD_AXIS_LEFTY:
  1602. GetAppleSFSymbolsNameForElement(elements[GCInputLeftThumbstick], elementName);
  1603. break;
  1604. case SDL_GAMEPAD_AXIS_RIGHTX:
  1605. GetAppleSFSymbolsNameForElement(elements[GCInputRightThumbstick], elementName);
  1606. break;
  1607. case SDL_GAMEPAD_AXIS_RIGHTY:
  1608. GetAppleSFSymbolsNameForElement(elements[GCInputRightThumbstick], elementName);
  1609. break;
  1610. case SDL_GAMEPAD_AXIS_LEFT_TRIGGER:
  1611. GetAppleSFSymbolsNameForElement(elements[GCInputLeftTrigger], elementName);
  1612. break;
  1613. case SDL_GAMEPAD_AXIS_RIGHT_TRIGGER:
  1614. GetAppleSFSymbolsNameForElement(elements[GCInputRightTrigger], elementName);
  1615. break;
  1616. default:
  1617. break;
  1618. }
  1619. }
  1620. }
  1621. }
  1622. #endif
  1623. return *elementName ? elementName : NULL;
  1624. }
  1625. SDL_JoystickDriver SDL_IOS_JoystickDriver = {
  1626. IOS_JoystickInit,
  1627. IOS_JoystickGetCount,
  1628. IOS_JoystickDetect,
  1629. IOS_JoystickGetDeviceName,
  1630. IOS_JoystickGetDevicePath,
  1631. IOS_JoystickGetDevicePlayerIndex,
  1632. IOS_JoystickSetDevicePlayerIndex,
  1633. IOS_JoystickGetDeviceGUID,
  1634. IOS_JoystickGetDeviceInstanceID,
  1635. IOS_JoystickOpen,
  1636. IOS_JoystickRumble,
  1637. IOS_JoystickRumbleTriggers,
  1638. IOS_JoystickGetCapabilities,
  1639. IOS_JoystickSetLED,
  1640. IOS_JoystickSendEffect,
  1641. IOS_JoystickSetSensorsEnabled,
  1642. IOS_JoystickUpdate,
  1643. IOS_JoystickClose,
  1644. IOS_JoystickQuit,
  1645. IOS_JoystickGetGamepadMapping
  1646. };