joypad_apple.mm 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428
  1. /**************************************************************************/
  2. /* joypad_apple.mm */
  3. /**************************************************************************/
  4. /* This file is part of: */
  5. /* GODOT ENGINE */
  6. /* https://godotengine.org */
  7. /**************************************************************************/
  8. /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
  9. /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
  10. /* */
  11. /* Permission is hereby granted, free of charge, to any person obtaining */
  12. /* a copy of this software and associated documentation files (the */
  13. /* "Software"), to deal in the Software without restriction, including */
  14. /* without limitation the rights to use, copy, modify, merge, publish, */
  15. /* distribute, sublicense, and/or sell copies of the Software, and to */
  16. /* permit persons to whom the Software is furnished to do so, subject to */
  17. /* the following conditions: */
  18. /* */
  19. /* The above copyright notice and this permission notice shall be */
  20. /* included in all copies or substantial portions of the Software. */
  21. /* */
  22. /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
  23. /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
  24. /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
  25. /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
  26. /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
  27. /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
  28. /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
  29. /**************************************************************************/
  30. #import "joypad_apple.h"
  31. #include <CoreHaptics/CoreHaptics.h>
  32. #import <os/log.h>
  33. #include "core/config/project_settings.h"
  34. #include "main/main.h"
  35. class API_AVAILABLE(macos(11), ios(14.0), tvos(14.0)) RumbleMotor {
  36. CHHapticEngine *engine;
  37. id<CHHapticPatternPlayer> player;
  38. bool is_started;
  39. RumbleMotor(GCController *p_controller, GCHapticsLocality p_locality) {
  40. engine = [p_controller.haptics createEngineWithLocality:p_locality];
  41. engine.autoShutdownEnabled = YES;
  42. }
  43. public:
  44. static RumbleMotor *create(GCController *p_controller, GCHapticsLocality p_locality) {
  45. if ([p_controller.haptics.supportedLocalities containsObject:p_locality]) {
  46. return memnew(RumbleMotor(p_controller, p_locality));
  47. }
  48. return nullptr;
  49. }
  50. _ALWAYS_INLINE_ bool has_active_player() {
  51. return player != nil;
  52. }
  53. void execute_pattern(CHHapticPattern *p_pattern) {
  54. NSError *error;
  55. if (!is_started) {
  56. ERR_FAIL_COND_MSG(![engine startAndReturnError:&error], "Couldn't start controller haptic engine: " + String::utf8(error.localizedDescription.UTF8String));
  57. is_started = YES;
  58. }
  59. player = [engine createPlayerWithPattern:p_pattern error:&error];
  60. ERR_FAIL_COND_MSG(error, "Couldn't create controller haptic pattern player: " + String::utf8(error.localizedDescription.UTF8String));
  61. ERR_FAIL_COND_MSG(![player startAtTime:CHHapticTimeImmediate error:&error], "Couldn't execute controller haptic pattern: " + String::utf8(error.localizedDescription.UTF8String));
  62. }
  63. void stop() {
  64. id<CHHapticPatternPlayer> old_player = player;
  65. player = nil;
  66. NSError *error;
  67. ERR_FAIL_COND_MSG(![old_player stopAtTime:CHHapticTimeImmediate error:&error], "Couldn't stop controller haptic pattern: " + String::utf8(error.localizedDescription.UTF8String));
  68. }
  69. };
  70. class API_AVAILABLE(macos(11), ios(14.0), tvos(14.0)) RumbleContext {
  71. RumbleMotor *weak_motor;
  72. RumbleMotor *strong_motor;
  73. public:
  74. RumbleContext(GCController *p_controller) {
  75. weak_motor = RumbleMotor::create(p_controller, GCHapticsLocalityRightHandle);
  76. strong_motor = RumbleMotor::create(p_controller, GCHapticsLocalityLeftHandle);
  77. }
  78. ~RumbleContext() {
  79. if (weak_motor) {
  80. memdelete(weak_motor);
  81. }
  82. if (strong_motor) {
  83. memdelete(strong_motor);
  84. }
  85. }
  86. _ALWAYS_INLINE_ bool has_motors() {
  87. return weak_motor != nullptr && strong_motor != nullptr;
  88. }
  89. _ALWAYS_INLINE_ bool has_active_players() {
  90. if (!has_motors()) {
  91. return false;
  92. }
  93. return (weak_motor && weak_motor->has_active_player()) || (strong_motor && strong_motor->has_active_player());
  94. }
  95. void stop() {
  96. if (weak_motor) {
  97. weak_motor->stop();
  98. }
  99. if (strong_motor) {
  100. strong_motor->stop();
  101. }
  102. }
  103. void play_weak_pattern(CHHapticPattern *p_pattern) {
  104. if (weak_motor) {
  105. weak_motor->execute_pattern(p_pattern);
  106. }
  107. }
  108. void play_strong_pattern(CHHapticPattern *p_pattern) {
  109. if (strong_motor) {
  110. strong_motor->execute_pattern(p_pattern);
  111. }
  112. }
  113. };
  114. GameController::GameController(int p_joy_id, GCController *p_controller) :
  115. joy_id(p_joy_id), controller(p_controller) {
  116. force_feedback = NO;
  117. if (@available(macOS 11.0, iOS 14.0, tvOS 14.0, *)) {
  118. if (controller.haptics != nil) {
  119. // Create a rumble context for the controller.
  120. rumble_context = memnew(RumbleContext(p_controller));
  121. // If the rumble motors aren't available, disable force feedback.
  122. force_feedback = rumble_context->has_motors();
  123. }
  124. }
  125. int l_joy_id = joy_id;
  126. auto BUTTON = [l_joy_id](JoyButton p_button) {
  127. return ^(GCControllerButtonInput *button, float value, BOOL pressed) {
  128. Input::get_singleton()->joy_button(l_joy_id, p_button, pressed);
  129. };
  130. };
  131. if (controller.extendedGamepad != nil) {
  132. GCExtendedGamepad *gamepad = controller.extendedGamepad;
  133. gamepad.buttonA.pressedChangedHandler = BUTTON(JoyButton::A);
  134. gamepad.buttonB.pressedChangedHandler = BUTTON(JoyButton::B);
  135. gamepad.buttonX.pressedChangedHandler = BUTTON(JoyButton::X);
  136. gamepad.buttonY.pressedChangedHandler = BUTTON(JoyButton::Y);
  137. gamepad.leftShoulder.pressedChangedHandler = BUTTON(JoyButton::LEFT_SHOULDER);
  138. gamepad.rightShoulder.pressedChangedHandler = BUTTON(JoyButton::RIGHT_SHOULDER);
  139. gamepad.dpad.up.pressedChangedHandler = BUTTON(JoyButton::DPAD_UP);
  140. gamepad.dpad.down.pressedChangedHandler = BUTTON(JoyButton::DPAD_DOWN);
  141. gamepad.dpad.left.pressedChangedHandler = BUTTON(JoyButton::DPAD_LEFT);
  142. gamepad.dpad.right.pressedChangedHandler = BUTTON(JoyButton::DPAD_RIGHT);
  143. gamepad.leftThumbstick.valueChangedHandler = ^(GCControllerDirectionPad *dpad, float xValue, float yValue) {
  144. Input::get_singleton()->joy_axis(l_joy_id, JoyAxis::LEFT_X, xValue);
  145. Input::get_singleton()->joy_axis(l_joy_id, JoyAxis::LEFT_Y, -yValue);
  146. };
  147. gamepad.rightThumbstick.valueChangedHandler = ^(GCControllerDirectionPad *dpad, float xValue, float yValue) {
  148. Input::get_singleton()->joy_axis(l_joy_id, JoyAxis::RIGHT_X, xValue);
  149. Input::get_singleton()->joy_axis(l_joy_id, JoyAxis::RIGHT_Y, -yValue);
  150. };
  151. gamepad.leftTrigger.valueChangedHandler = ^(GCControllerButtonInput *button, float value, BOOL pressed) {
  152. Input::get_singleton()->joy_axis(l_joy_id, JoyAxis::TRIGGER_LEFT, value);
  153. };
  154. gamepad.rightTrigger.valueChangedHandler = ^(GCControllerButtonInput *button, float value, BOOL pressed) {
  155. Input::get_singleton()->joy_axis(l_joy_id, JoyAxis::TRIGGER_RIGHT, value);
  156. };
  157. if (@available(macOS 10.14.1, iOS 12.1, tvOS 12.1, *)) {
  158. gamepad.leftThumbstickButton.pressedChangedHandler = BUTTON(JoyButton::LEFT_STICK);
  159. gamepad.rightThumbstickButton.pressedChangedHandler = BUTTON(JoyButton::RIGHT_STICK);
  160. }
  161. if (@available(macOS 10.15, iOS 13.0, tvOS 13.0, *)) {
  162. gamepad.buttonOptions.pressedChangedHandler = BUTTON(JoyButton::BACK);
  163. gamepad.buttonMenu.pressedChangedHandler = BUTTON(JoyButton::START);
  164. }
  165. if (@available(macOS 11, iOS 14.0, tvOS 14.0, *)) {
  166. gamepad.buttonHome.pressedChangedHandler = BUTTON(JoyButton::GUIDE);
  167. if ([gamepad isKindOfClass:[GCXboxGamepad class]]) {
  168. GCXboxGamepad *xboxGamepad = (GCXboxGamepad *)gamepad;
  169. xboxGamepad.paddleButton1.pressedChangedHandler = BUTTON(JoyButton::PADDLE1);
  170. xboxGamepad.paddleButton2.pressedChangedHandler = BUTTON(JoyButton::PADDLE2);
  171. xboxGamepad.paddleButton3.pressedChangedHandler = BUTTON(JoyButton::PADDLE3);
  172. xboxGamepad.paddleButton4.pressedChangedHandler = BUTTON(JoyButton::PADDLE4);
  173. }
  174. }
  175. if (@available(macOS 12, iOS 15.0, tvOS 15.0, *)) {
  176. if ([gamepad isKindOfClass:[GCXboxGamepad class]]) {
  177. GCXboxGamepad *xboxGamepad = (GCXboxGamepad *)gamepad;
  178. xboxGamepad.buttonShare.pressedChangedHandler = BUTTON(JoyButton::MISC1);
  179. }
  180. }
  181. } else if (controller.microGamepad != nil) {
  182. GCMicroGamepad *gamepad = controller.microGamepad;
  183. gamepad.buttonA.pressedChangedHandler = BUTTON(JoyButton::A);
  184. gamepad.buttonX.pressedChangedHandler = BUTTON(JoyButton::X);
  185. gamepad.dpad.up.pressedChangedHandler = BUTTON(JoyButton::DPAD_UP);
  186. gamepad.dpad.down.pressedChangedHandler = BUTTON(JoyButton::DPAD_DOWN);
  187. gamepad.dpad.left.pressedChangedHandler = BUTTON(JoyButton::DPAD_LEFT);
  188. gamepad.dpad.right.pressedChangedHandler = BUTTON(JoyButton::DPAD_RIGHT);
  189. }
  190. // TODO: Need to add support for controller.motion which gives us access to
  191. // the orientation of the device (if supported).
  192. }
  193. GameController::~GameController() {
  194. if (@available(macOS 11.0, iOS 14.0, tvOS 14.0, *)) {
  195. if (rumble_context) {
  196. memdelete(rumble_context);
  197. }
  198. }
  199. }
  200. JoypadApple::JoypadApple() {
  201. connect_observer = [NSNotificationCenter.defaultCenter
  202. addObserverForName:GCControllerDidConnectNotification
  203. object:nil
  204. queue:NSOperationQueue.mainQueue
  205. usingBlock:^(NSNotification *notification) {
  206. GCController *controller = notification.object;
  207. if (!controller) {
  208. return;
  209. }
  210. add_joypad(controller);
  211. }];
  212. disconnect_observer = [NSNotificationCenter.defaultCenter
  213. addObserverForName:GCControllerDidDisconnectNotification
  214. object:nil
  215. queue:NSOperationQueue.mainQueue
  216. usingBlock:^(NSNotification *notification) {
  217. GCController *controller = notification.object;
  218. if (!controller) {
  219. return;
  220. }
  221. remove_joypad(controller);
  222. }];
  223. if (@available(macOS 11.3, iOS 14.5, tvOS 14.5, *)) {
  224. GCController.shouldMonitorBackgroundEvents = YES;
  225. }
  226. }
  227. JoypadApple::~JoypadApple() {
  228. for (KeyValue<int, GameController *> &E : joypads) {
  229. memdelete(E.value);
  230. E.value = nullptr;
  231. }
  232. [NSNotificationCenter.defaultCenter removeObserver:connect_observer];
  233. [NSNotificationCenter.defaultCenter removeObserver:disconnect_observer];
  234. }
  235. // Finds the rightmost set bit in a number, n.
  236. // variation of https://www.geeksforgeeks.org/position-of-rightmost-set-bit/
  237. int rightmost_one(int n) {
  238. return __builtin_ctz(n & -n) + 1;
  239. }
  240. GCControllerPlayerIndex JoypadApple::get_free_player_index() {
  241. // player_set will be a bitfield where each bit represents a player index.
  242. __block uint32_t player_set = 0;
  243. for (const KeyValue<GCController *, int> &E : controller_to_joy_id) {
  244. player_set |= 1U << E.key.playerIndex;
  245. }
  246. // invert, as we want to find the first unset player index.
  247. int n = rightmost_one((int)(~player_set));
  248. if (n >= 5) {
  249. return GCControllerPlayerIndexUnset;
  250. }
  251. return (GCControllerPlayerIndex)(n - 1);
  252. }
  253. void JoypadApple::add_joypad(GCController *p_controller) {
  254. if (controller_to_joy_id.has(p_controller)) {
  255. return;
  256. }
  257. // Get a new id for our controller.
  258. int joy_id = Input::get_singleton()->get_unused_joy_id();
  259. if (joy_id == -1) {
  260. print_verbose("Couldn't retrieve new joy ID.");
  261. return;
  262. }
  263. // Assign our player index.
  264. if (p_controller.playerIndex == GCControllerPlayerIndexUnset) {
  265. p_controller.playerIndex = get_free_player_index();
  266. }
  267. // Tell Godot about our new controller.
  268. Input::get_singleton()->joy_connection_changed(joy_id, true, String::utf8(p_controller.vendorName.UTF8String));
  269. // Assign our player index.
  270. joypads.insert(joy_id, memnew(GameController(joy_id, p_controller)));
  271. controller_to_joy_id.insert(p_controller, joy_id);
  272. }
  273. void JoypadApple::remove_joypad(GCController *p_controller) {
  274. if (!controller_to_joy_id.has(p_controller)) {
  275. return;
  276. }
  277. int joy_id = controller_to_joy_id[p_controller];
  278. controller_to_joy_id.erase(p_controller);
  279. // Tell Godot this joystick is no longer there.
  280. Input::get_singleton()->joy_connection_changed(joy_id, false, "");
  281. // And remove it from our dictionary.
  282. GameController **old = joypads.getptr(joy_id);
  283. memdelete(*old);
  284. *old = nullptr;
  285. joypads.erase(joy_id);
  286. }
  287. API_AVAILABLE(macos(10.15), ios(13.0), tvos(14.0))
  288. CHHapticPattern *get_vibration_pattern(float p_magnitude, float p_duration) {
  289. // Creates a vibration pattern with an intensity and duration.
  290. NSDictionary *hapticDict = @{
  291. CHHapticPatternKeyPattern : @[
  292. @{
  293. CHHapticPatternKeyEvent : @{
  294. CHHapticPatternKeyEventType : CHHapticEventTypeHapticContinuous,
  295. CHHapticPatternKeyTime : @(CHHapticTimeImmediate),
  296. CHHapticPatternKeyEventDuration : [NSNumber numberWithFloat:p_duration],
  297. CHHapticPatternKeyEventParameters : @[
  298. @{
  299. CHHapticPatternKeyParameterID : CHHapticEventParameterIDHapticIntensity,
  300. CHHapticPatternKeyParameterValue : [NSNumber numberWithFloat:p_magnitude]
  301. },
  302. ],
  303. },
  304. },
  305. ],
  306. };
  307. NSError *error;
  308. CHHapticPattern *pattern = [[CHHapticPattern alloc] initWithDictionary:hapticDict error:&error];
  309. return pattern;
  310. }
  311. void JoypadApple::joypad_vibration_start(GameController &p_joypad, float p_weak_magnitude, float p_strong_magnitude, float p_duration, uint64_t p_timestamp) {
  312. if (!p_joypad.force_feedback || p_weak_magnitude < 0.f || p_weak_magnitude > 1.f || p_strong_magnitude < 0.f || p_strong_magnitude > 1.f) {
  313. return;
  314. }
  315. // If there is active vibration players, stop them.
  316. if (p_joypad.rumble_context->has_active_players()) {
  317. joypad_vibration_stop(p_joypad, p_timestamp);
  318. }
  319. // Gets the default vibration pattern and creates a player for each motor.
  320. CHHapticPattern *weak_pattern = get_vibration_pattern(p_weak_magnitude, p_duration);
  321. CHHapticPattern *strong_pattern = get_vibration_pattern(p_strong_magnitude, p_duration);
  322. p_joypad.rumble_context->play_weak_pattern(weak_pattern);
  323. p_joypad.rumble_context->play_strong_pattern(strong_pattern);
  324. p_joypad.ff_effect_timestamp = p_timestamp;
  325. }
  326. void JoypadApple::joypad_vibration_stop(GameController &p_joypad, uint64_t p_timestamp) {
  327. if (!p_joypad.force_feedback) {
  328. return;
  329. }
  330. // If there is no active vibration players, exit.
  331. if (!p_joypad.rumble_context->has_active_players()) {
  332. return;
  333. }
  334. p_joypad.rumble_context->stop();
  335. p_joypad.ff_effect_timestamp = p_timestamp;
  336. }
  337. void JoypadApple::process_joypads() {
  338. if (@available(macOS 11.0, iOS 14.0, tvOS 14.0, *)) {
  339. for (KeyValue<int, GameController *> &E : joypads) {
  340. int id = E.key;
  341. GameController &joypad = *E.value;
  342. if (joypad.force_feedback) {
  343. Input *input = Input::get_singleton();
  344. uint64_t timestamp = input->get_joy_vibration_timestamp(id);
  345. if (timestamp > (unsigned)joypad.ff_effect_timestamp) {
  346. Vector2 strength = input->get_joy_vibration_strength(id);
  347. float duration = input->get_joy_vibration_duration(id);
  348. if (duration == 0) {
  349. duration = GCHapticDurationInfinite;
  350. }
  351. if (strength.x == 0 && strength.y == 0) {
  352. joypad_vibration_stop(joypad, timestamp);
  353. } else {
  354. joypad_vibration_start(joypad, strength.x, strength.y, duration, timestamp);
  355. }
  356. }
  357. }
  358. }
  359. }
  360. }