SDL_sysjoystick.m 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609
  1. /*
  2. Simple DirectMedia Layer
  3. Copyright (C) 1997-2015 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_c.h"
  21. /* needed for SDL_IPHONE_MAX_GFORCE macro */
  22. #include "SDL_config_iphoneos.h"
  23. #include "SDL_joystick.h"
  24. #include "SDL_hints.h"
  25. #include "SDL_stdinc.h"
  26. #include "../SDL_sysjoystick.h"
  27. #include "../SDL_joystick_c.h"
  28. #if !SDL_EVENTS_DISABLED
  29. #include "../../events/SDL_events_c.h"
  30. #endif
  31. #import <CoreMotion/CoreMotion.h>
  32. #ifdef SDL_JOYSTICK_MFI
  33. #import <GameController/GameController.h>
  34. static id connectObserver = nil;
  35. static id disconnectObserver = nil;
  36. #endif /* SDL_JOYSTICK_MFI */
  37. static const char *accelerometerName = "iOS Accelerometer";
  38. static CMMotionManager *motionManager = nil;
  39. static SDL_JoystickDeviceItem *deviceList = NULL;
  40. static int numjoysticks = 0;
  41. static SDL_JoystickID instancecounter = 0;
  42. static SDL_JoystickDeviceItem *
  43. GetDeviceForIndex(int device_index)
  44. {
  45. SDL_JoystickDeviceItem *device = deviceList;
  46. int i = 0;
  47. while (i < device_index) {
  48. if (device == NULL) {
  49. return NULL;
  50. }
  51. device = device->next;
  52. i++;
  53. }
  54. return device;
  55. }
  56. static void
  57. SDL_SYS_AddMFIJoystickDevice(SDL_JoystickDeviceItem *device, GCController *controller)
  58. {
  59. #ifdef SDL_JOYSTICK_MFI
  60. const char *name = NULL;
  61. /* Explicitly retain the controller because SDL_JoystickDeviceItem is a
  62. * struct, and ARC doesn't work with structs. */
  63. device->controller = (__bridge GCController *) CFBridgingRetain(controller);
  64. if (controller.vendorName) {
  65. name = controller.vendorName.UTF8String;
  66. }
  67. if (!name) {
  68. name = "MFi Gamepad";
  69. }
  70. device->name = SDL_strdup(name);
  71. device->guid.data[0] = 'M';
  72. device->guid.data[1] = 'F';
  73. device->guid.data[2] = 'i';
  74. device->guid.data[3] = 'G';
  75. device->guid.data[4] = 'a';
  76. device->guid.data[5] = 'm';
  77. device->guid.data[6] = 'e';
  78. device->guid.data[7] = 'p';
  79. device->guid.data[8] = 'a';
  80. device->guid.data[9] = 'd';
  81. if (controller.extendedGamepad) {
  82. device->guid.data[10] = 1;
  83. } else if (controller.gamepad) {
  84. device->guid.data[10] = 2;
  85. }
  86. if (controller.extendedGamepad) {
  87. device->naxes = 6; /* 2 thumbsticks and 2 triggers */
  88. device->nhats = 1; /* d-pad */
  89. device->nbuttons = 7; /* ABXY, shoulder buttons, pause button */
  90. } else if (controller.gamepad) {
  91. device->naxes = 0; /* no traditional analog inputs */
  92. device->nhats = 1; /* d-pad */
  93. device->nbuttons = 7; /* ABXY, shoulder buttons, pause button */
  94. }
  95. /* TODO: Handle micro profiles on tvOS. */
  96. #endif
  97. }
  98. static void
  99. SDL_SYS_AddJoystickDevice(GCController *controller, SDL_bool accelerometer)
  100. {
  101. SDL_JoystickDeviceItem *device = deviceList;
  102. #if !SDL_EVENTS_DISABLED
  103. SDL_Event event;
  104. #endif
  105. while (device != NULL) {
  106. if (device->controller == controller) {
  107. return;
  108. }
  109. device = device->next;
  110. }
  111. device = (SDL_JoystickDeviceItem *) SDL_malloc(sizeof(SDL_JoystickDeviceItem));
  112. if (device == NULL) {
  113. return;
  114. }
  115. SDL_zerop(device);
  116. device->accelerometer = accelerometer;
  117. device->instance_id = instancecounter++;
  118. if (accelerometer) {
  119. device->name = SDL_strdup(accelerometerName);
  120. device->naxes = 3; /* Device acceleration in the x, y, and z axes. */
  121. device->nhats = 0;
  122. device->nbuttons = 0;
  123. /* Use the accelerometer name as a GUID. */
  124. SDL_memcpy(&device->guid.data, device->name, SDL_min(sizeof(SDL_JoystickGUID), SDL_strlen(device->name)));
  125. } else if (controller) {
  126. SDL_SYS_AddMFIJoystickDevice(device, controller);
  127. }
  128. if (deviceList == NULL) {
  129. deviceList = device;
  130. } else {
  131. SDL_JoystickDeviceItem *lastdevice = deviceList;
  132. while (lastdevice->next != NULL) {
  133. lastdevice = lastdevice->next;
  134. }
  135. lastdevice->next = device;
  136. }
  137. ++numjoysticks;
  138. #if !SDL_EVENTS_DISABLED
  139. event.type = SDL_JOYDEVICEADDED;
  140. if (SDL_GetEventState(event.type) == SDL_ENABLE) {
  141. event.jdevice.which = numjoysticks - 1;
  142. if ((SDL_EventOK == NULL) ||
  143. (*SDL_EventOK)(SDL_EventOKParam, &event)) {
  144. SDL_PushEvent(&event);
  145. }
  146. }
  147. #endif /* !SDL_EVENTS_DISABLED */
  148. }
  149. static SDL_JoystickDeviceItem *
  150. SDL_SYS_RemoveJoystickDevice(SDL_JoystickDeviceItem *device)
  151. {
  152. SDL_JoystickDeviceItem *prev = NULL;
  153. SDL_JoystickDeviceItem *next = NULL;
  154. SDL_JoystickDeviceItem *item = deviceList;
  155. #if !SDL_EVENTS_DISABLED
  156. SDL_Event event;
  157. #endif
  158. if (device == NULL) {
  159. return NULL;
  160. }
  161. next = device->next;
  162. while (item != NULL) {
  163. if (item == device) {
  164. break;
  165. }
  166. prev = item;
  167. item = item->next;
  168. }
  169. /* Unlink the device item from the device list. */
  170. if (prev) {
  171. prev->next = device->next;
  172. } else if (device == deviceList) {
  173. deviceList = device->next;
  174. }
  175. if (device->joystick) {
  176. device->joystick->hwdata = NULL;
  177. }
  178. #ifdef SDL_JOYSTICK_MFI
  179. @autoreleasepool {
  180. if (device->controller) {
  181. /* The controller was explicitly retained in the struct, so it
  182. * should be explicitly released before freeing the struct. */
  183. GCController *controller = CFBridgingRelease((__bridge CFTypeRef)(device->controller));
  184. controller.controllerPausedHandler = nil;
  185. device->controller = nil;
  186. }
  187. }
  188. #endif /* SDL_JOYSTICK_MFI */
  189. --numjoysticks;
  190. #if !SDL_EVENTS_DISABLED
  191. event.type = SDL_JOYDEVICEREMOVED;
  192. if (SDL_GetEventState(event.type) == SDL_ENABLE) {
  193. event.jdevice.which = device->instance_id;
  194. if ((SDL_EventOK == NULL) ||
  195. (*SDL_EventOK)(SDL_EventOKParam, &event)) {
  196. SDL_PushEvent(&event);
  197. }
  198. }
  199. #endif /* !SDL_EVENTS_DISABLED */
  200. SDL_free(device->name);
  201. SDL_free(device);
  202. return next;
  203. }
  204. /* Function to scan the system for joysticks.
  205. * Joystick 0 should be the system default joystick.
  206. * It should return 0, or -1 on an unrecoverable fatal error.
  207. */
  208. int
  209. SDL_SYS_JoystickInit(void)
  210. {
  211. @autoreleasepool {
  212. NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
  213. const char *hint = SDL_GetHint(SDL_HINT_ACCELEROMETER_AS_JOYSTICK);
  214. if (!hint || SDL_atoi(hint)) {
  215. /* Default behavior, accelerometer as joystick */
  216. SDL_SYS_AddJoystickDevice(nil, SDL_TRUE);
  217. }
  218. #ifdef SDL_JOYSTICK_MFI
  219. /* GameController.framework was added in iOS 7. */
  220. if (![GCController class]) {
  221. return numjoysticks;
  222. }
  223. for (GCController *controller in [GCController controllers]) {
  224. SDL_SYS_AddJoystickDevice(controller, SDL_FALSE);
  225. }
  226. connectObserver = [center addObserverForName:GCControllerDidConnectNotification
  227. object:nil
  228. queue:nil
  229. usingBlock:^(NSNotification *note) {
  230. GCController *controller = note.object;
  231. SDL_SYS_AddJoystickDevice(controller, SDL_FALSE);
  232. }];
  233. disconnectObserver = [center addObserverForName:GCControllerDidDisconnectNotification
  234. object:nil
  235. queue:nil
  236. usingBlock:^(NSNotification *note) {
  237. GCController *controller = note.object;
  238. SDL_JoystickDeviceItem *device = deviceList;
  239. while (device != NULL) {
  240. if (device->controller == controller) {
  241. SDL_SYS_RemoveJoystickDevice(device);
  242. break;
  243. }
  244. device = device->next;
  245. }
  246. }];
  247. #endif /* SDL_JOYSTICK_MFI */
  248. }
  249. return numjoysticks;
  250. }
  251. int SDL_SYS_NumJoysticks()
  252. {
  253. return numjoysticks;
  254. }
  255. void SDL_SYS_JoystickDetect()
  256. {
  257. }
  258. /* Function to get the device-dependent name of a joystick */
  259. const char *
  260. SDL_SYS_JoystickNameForDeviceIndex(int device_index)
  261. {
  262. SDL_JoystickDeviceItem *device = GetDeviceForIndex(device_index);
  263. return device ? device->name : "Unknown";
  264. }
  265. /* Function to perform the mapping from device index to the instance id for this index */
  266. SDL_JoystickID SDL_SYS_GetInstanceIdOfDeviceIndex(int device_index)
  267. {
  268. SDL_JoystickDeviceItem *device = GetDeviceForIndex(device_index);
  269. return device ? device->instance_id : 0;
  270. }
  271. /* Function to open a joystick for use.
  272. The joystick to open is specified by the device index.
  273. This should fill the nbuttons and naxes fields of the joystick structure.
  274. It returns 0, or -1 if there is an error.
  275. */
  276. int
  277. SDL_SYS_JoystickOpen(SDL_Joystick * joystick, int device_index)
  278. {
  279. SDL_JoystickDeviceItem *device = GetDeviceForIndex(device_index);
  280. if (device == NULL) {
  281. return SDL_SetError("Could not open Joystick: no hardware device for the specified index");
  282. }
  283. joystick->hwdata = device;
  284. joystick->instance_id = device->instance_id;
  285. joystick->naxes = device->naxes;
  286. joystick->nhats = device->nhats;
  287. joystick->nbuttons = device->nbuttons;
  288. joystick->nballs = 0;
  289. device->joystick = joystick;
  290. @autoreleasepool {
  291. if (device->accelerometer) {
  292. if (motionManager == nil) {
  293. motionManager = [[CMMotionManager alloc] init];
  294. }
  295. /* Shorter times between updates can significantly increase CPU usage. */
  296. motionManager.accelerometerUpdateInterval = 0.1;
  297. [motionManager startAccelerometerUpdates];
  298. } else {
  299. #ifdef SDL_JOYSTICK_MFI
  300. GCController *controller = device->controller;
  301. controller.controllerPausedHandler = ^(GCController *controller) {
  302. if (joystick->hwdata) {
  303. ++joystick->hwdata->num_pause_presses;
  304. }
  305. };
  306. #endif /* SDL_JOYSTICK_MFI */
  307. }
  308. }
  309. return 0;
  310. }
  311. /* Function to determine if this joystick is attached to the system right now */
  312. SDL_bool
  313. SDL_SYS_JoystickAttached(SDL_Joystick *joystick)
  314. {
  315. return joystick->hwdata != NULL;
  316. }
  317. static void
  318. SDL_SYS_AccelerometerUpdate(SDL_Joystick * joystick)
  319. {
  320. const float maxgforce = SDL_IPHONE_MAX_GFORCE;
  321. const SInt16 maxsint16 = 0x7FFF;
  322. CMAcceleration accel;
  323. @autoreleasepool {
  324. if (!motionManager.isAccelerometerActive) {
  325. return;
  326. }
  327. accel = motionManager.accelerometerData.acceleration;
  328. }
  329. /*
  330. Convert accelerometer data from floating point to Sint16, which is what
  331. the joystick system expects.
  332. To do the conversion, the data is first clamped onto the interval
  333. [-SDL_IPHONE_MAX_G_FORCE, SDL_IPHONE_MAX_G_FORCE], then the data is multiplied
  334. by MAX_SINT16 so that it is mapped to the full range of an Sint16.
  335. You can customize the clamped range of this function by modifying the
  336. SDL_IPHONE_MAX_GFORCE macro in SDL_config_iphoneos.h.
  337. Once converted to Sint16, the accelerometer data no longer has coherent
  338. units. You can convert the data back to units of g-force by multiplying
  339. it in your application's code by SDL_IPHONE_MAX_GFORCE / 0x7FFF.
  340. */
  341. /* clamp the data */
  342. accel.x = SDL_min(SDL_max(accel.x, -maxgforce), maxgforce);
  343. accel.y = SDL_min(SDL_max(accel.y, -maxgforce), maxgforce);
  344. accel.z = SDL_min(SDL_max(accel.z, -maxgforce), maxgforce);
  345. /* pass in data mapped to range of SInt16 */
  346. SDL_PrivateJoystickAxis(joystick, 0, (accel.x / maxgforce) * maxsint16);
  347. SDL_PrivateJoystickAxis(joystick, 1, -(accel.y / maxgforce) * maxsint16);
  348. SDL_PrivateJoystickAxis(joystick, 2, (accel.z / maxgforce) * maxsint16);
  349. }
  350. #ifdef SDL_JOYSTICK_MFI
  351. static Uint8
  352. SDL_SYS_MFIJoystickHatStateForDPad(GCControllerDirectionPad *dpad)
  353. {
  354. Uint8 hat = 0;
  355. if (dpad.up.isPressed) {
  356. hat |= SDL_HAT_UP;
  357. } else if (dpad.down.isPressed) {
  358. hat |= SDL_HAT_DOWN;
  359. }
  360. if (dpad.left.isPressed) {
  361. hat |= SDL_HAT_LEFT;
  362. } else if (dpad.right.isPressed) {
  363. hat |= SDL_HAT_RIGHT;
  364. }
  365. if (hat == 0) {
  366. return SDL_HAT_CENTERED;
  367. }
  368. return hat;
  369. }
  370. #endif
  371. static void
  372. SDL_SYS_MFIJoystickUpdate(SDL_Joystick * joystick)
  373. {
  374. #ifdef SDL_JOYSTICK_MFI
  375. @autoreleasepool {
  376. GCController *controller = joystick->hwdata->controller;
  377. Uint8 hatstate = SDL_HAT_CENTERED;
  378. int i;
  379. if (controller.extendedGamepad) {
  380. GCExtendedGamepad *gamepad = controller.extendedGamepad;
  381. /* Axis order matches the XInput Windows mappings. */
  382. SDL_PrivateJoystickAxis(joystick, 0, (Sint16) (gamepad.leftThumbstick.xAxis.value * 32767));
  383. SDL_PrivateJoystickAxis(joystick, 1, (Sint16) (gamepad.leftThumbstick.yAxis.value * -32767));
  384. SDL_PrivateJoystickAxis(joystick, 2, (Sint16) (gamepad.leftTrigger.value * 32767));
  385. SDL_PrivateJoystickAxis(joystick, 3, (Sint16) (gamepad.rightThumbstick.xAxis.value * 32767));
  386. SDL_PrivateJoystickAxis(joystick, 4, (Sint16) (gamepad.rightThumbstick.yAxis.value * -32767));
  387. SDL_PrivateJoystickAxis(joystick, 5, (Sint16) (gamepad.rightTrigger.value * 32767));
  388. hatstate = SDL_SYS_MFIJoystickHatStateForDPad(gamepad.dpad);
  389. /* Button order matches the XInput Windows mappings. */
  390. SDL_PrivateJoystickButton(joystick, 0, gamepad.buttonA.isPressed);
  391. SDL_PrivateJoystickButton(joystick, 1, gamepad.buttonB.isPressed);
  392. SDL_PrivateJoystickButton(joystick, 2, gamepad.buttonX.isPressed);
  393. SDL_PrivateJoystickButton(joystick, 3, gamepad.buttonY.isPressed);
  394. SDL_PrivateJoystickButton(joystick, 4, gamepad.leftShoulder.isPressed);
  395. SDL_PrivateJoystickButton(joystick, 5, gamepad.rightShoulder.isPressed);
  396. } else if (controller.gamepad) {
  397. GCGamepad *gamepad = controller.gamepad;
  398. hatstate = SDL_SYS_MFIJoystickHatStateForDPad(gamepad.dpad);
  399. /* Button order matches the XInput Windows mappings. */
  400. SDL_PrivateJoystickButton(joystick, 0, gamepad.buttonA.isPressed);
  401. SDL_PrivateJoystickButton(joystick, 1, gamepad.buttonB.isPressed);
  402. SDL_PrivateJoystickButton(joystick, 2, gamepad.buttonX.isPressed);
  403. SDL_PrivateJoystickButton(joystick, 3, gamepad.buttonY.isPressed);
  404. SDL_PrivateJoystickButton(joystick, 4, gamepad.leftShoulder.isPressed);
  405. SDL_PrivateJoystickButton(joystick, 5, gamepad.rightShoulder.isPressed);
  406. }
  407. /* TODO: Handle micro profiles on tvOS. */
  408. SDL_PrivateJoystickHat(joystick, 0, hatstate);
  409. for (i = 0; i < joystick->hwdata->num_pause_presses; i++) {
  410. /* The pause button is always last. */
  411. Uint8 pausebutton = joystick->nbuttons - 1;
  412. SDL_PrivateJoystickButton(joystick, pausebutton, 1);
  413. SDL_PrivateJoystickButton(joystick, pausebutton, 0);
  414. }
  415. joystick->hwdata->num_pause_presses = 0;
  416. }
  417. #endif
  418. }
  419. /* Function to update the state of a joystick - called as a device poll.
  420. * This function shouldn't update the joystick structure directly,
  421. * but instead should call SDL_PrivateJoystick*() to deliver events
  422. * and update joystick device state.
  423. */
  424. void
  425. SDL_SYS_JoystickUpdate(SDL_Joystick * joystick)
  426. {
  427. SDL_JoystickDeviceItem *device = joystick->hwdata;
  428. if (device == NULL) {
  429. return;
  430. }
  431. if (device->accelerometer) {
  432. SDL_SYS_AccelerometerUpdate(joystick);
  433. } else if (device->controller) {
  434. SDL_SYS_MFIJoystickUpdate(joystick);
  435. }
  436. }
  437. /* Function to close a joystick after use */
  438. void
  439. SDL_SYS_JoystickClose(SDL_Joystick * joystick)
  440. {
  441. SDL_JoystickDeviceItem *device = joystick->hwdata;
  442. if (device == NULL) {
  443. return;
  444. }
  445. device->joystick = NULL;
  446. @autoreleasepool {
  447. if (device->accelerometer) {
  448. [motionManager stopAccelerometerUpdates];
  449. } else if (device->controller) {
  450. #ifdef SDL_JOYSTICK_MFI
  451. GCController *controller = device->controller;
  452. controller.controllerPausedHandler = nil;
  453. #endif
  454. }
  455. }
  456. }
  457. /* Function to perform any system-specific joystick related cleanup */
  458. void
  459. SDL_SYS_JoystickQuit(void)
  460. {
  461. @autoreleasepool {
  462. #ifdef SDL_JOYSTICK_MFI
  463. NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
  464. if (connectObserver) {
  465. [center removeObserver:connectObserver name:GCControllerDidConnectNotification object:nil];
  466. connectObserver = nil;
  467. }
  468. if (disconnectObserver) {
  469. [center removeObserver:disconnectObserver name:GCControllerDidDisconnectNotification object:nil];
  470. disconnectObserver = nil;
  471. }
  472. #endif /* SDL_JOYSTICK_MFI */
  473. while (deviceList != NULL) {
  474. SDL_SYS_RemoveJoystickDevice(deviceList);
  475. }
  476. motionManager = nil;
  477. }
  478. numjoysticks = 0;
  479. }
  480. SDL_JoystickGUID
  481. SDL_SYS_JoystickGetDeviceGUID( int device_index )
  482. {
  483. SDL_JoystickDeviceItem *device = GetDeviceForIndex(device_index);
  484. SDL_JoystickGUID guid;
  485. if (device) {
  486. guid = device->guid;
  487. } else {
  488. SDL_zero(guid);
  489. }
  490. return guid;
  491. }
  492. SDL_JoystickGUID
  493. SDL_SYS_JoystickGetGUID(SDL_Joystick * joystick)
  494. {
  495. SDL_JoystickGUID guid;
  496. if (joystick->hwdata) {
  497. guid = joystick->hwdata->guid;
  498. } else {
  499. SDL_zero(guid);
  500. }
  501. return guid;
  502. }
  503. /* vi: set ts=4 sw=4 expandtab: */