game_controller_data_fetcher_mac.mm 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. // Copyright 2016 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "device/gamepad/game_controller_data_fetcher_mac.h"
  5. #import <GameController/GameController.h>
  6. #include <string.h>
  7. #include <string>
  8. #include "base/mac/mac_util.h"
  9. #include "base/strings/sys_string_conversions.h"
  10. #include "base/strings/utf_string_conversions.h"
  11. #include "device/gamepad/gamepad_standard_mappings.h"
  12. namespace device {
  13. namespace {
  14. const int kGCControllerPlayerIndexCount = 4;
  15. // Returns true if |controller| should be enumerated by this data fetcher.
  16. bool IsSupported(GCController* controller) {
  17. // We only support the extendedGamepad profile, the basic gamepad profile
  18. // appears to only be for iOS devices.
  19. if (![controller extendedGamepad])
  20. return false;
  21. // In macOS 10.15, support for some console gamepads was added to the Game
  22. // Controller framework and a productCategory property was added to enable
  23. // applications to detect the new devices. These gamepads are already
  24. // supported in Chrome through other data fetchers and must be blocked here to
  25. // avoid double-enumeration.
  26. if (@available(macOS 10.15, *)) {
  27. NSString* product_category = [controller productCategory];
  28. if ([product_category isEqualToString:@"Xbox One"] ||
  29. [product_category isEqualToString:@"DualShock 4"] ||
  30. [product_category isEqualToString:@"DualSense"] ||
  31. [product_category isEqualToString:@"Switch Pro Controller"] ||
  32. [product_category isEqualToString:@"Nintendo Switch JoyCon (L/R)"]) {
  33. return false;
  34. }
  35. }
  36. return true;
  37. }
  38. } // namespace
  39. GameControllerDataFetcherMac::GameControllerDataFetcherMac() = default;
  40. GameControllerDataFetcherMac::~GameControllerDataFetcherMac() = default;
  41. GamepadSource GameControllerDataFetcherMac::source() {
  42. return Factory::static_source();
  43. }
  44. void GameControllerDataFetcherMac::GetGamepadData(bool) {
  45. NSArray* controllers = [GCController controllers];
  46. // In the first pass, record which player indices are still in use so unused
  47. // indices can be assigned to newly connected gamepads.
  48. bool player_indices[Gamepads::kItemsLengthCap];
  49. std::fill(player_indices, player_indices + Gamepads::kItemsLengthCap, false);
  50. for (GCController* controller in controllers) {
  51. if (!IsSupported(controller))
  52. continue;
  53. int player_index = controller.playerIndex;
  54. if (player_index != GCControllerPlayerIndexUnset)
  55. player_indices[player_index] = true;
  56. }
  57. for (size_t i = 0; i < Gamepads::kItemsLengthCap; ++i) {
  58. if (connected_[i] && !player_indices[i])
  59. connected_[i] = false;
  60. }
  61. // In the second pass, assign indices to newly connected gamepads and fetch
  62. // the gamepad state.
  63. for (GCController* controller in controllers) {
  64. if (!IsSupported(controller))
  65. continue;
  66. int player_index = controller.playerIndex;
  67. if (player_index == GCControllerPlayerIndexUnset) {
  68. player_index = NextUnusedPlayerIndex();
  69. if (player_index == GCControllerPlayerIndexUnset)
  70. continue;
  71. }
  72. PadState* state = GetPadState(player_index);
  73. if (!state)
  74. continue;
  75. Gamepad& pad = state->data;
  76. // This first time we encounter a gamepad, set its name, mapping, and
  77. // axes/button counts. This information is static, so it only needs to be
  78. // done once.
  79. if (!state->is_initialized) {
  80. state->is_initialized = true;
  81. NSString* vendorName = [controller vendorName];
  82. NSString* ident =
  83. [NSString stringWithFormat:@"%@ (STANDARD GAMEPAD)",
  84. vendorName ? vendorName : @"Unknown"];
  85. pad.mapping = GamepadMapping::kStandard;
  86. pad.SetID(base::SysNSStringToUTF16(ident));
  87. pad.axes_length = AXIS_INDEX_COUNT;
  88. pad.buttons_length = BUTTON_INDEX_COUNT - 1;
  89. pad.connected = true;
  90. connected_[player_index] = true;
  91. controller.playerIndex =
  92. static_cast<GCControllerPlayerIndex>(player_index);
  93. }
  94. pad.timestamp = CurrentTimeInMicroseconds();
  95. auto extended_gamepad = [controller extendedGamepad];
  96. pad.axes[AXIS_INDEX_LEFT_STICK_X] =
  97. [[[extended_gamepad leftThumbstick] xAxis] value];
  98. pad.axes[AXIS_INDEX_LEFT_STICK_Y] =
  99. -[[[extended_gamepad leftThumbstick] yAxis] value];
  100. pad.axes[AXIS_INDEX_RIGHT_STICK_X] =
  101. [[[extended_gamepad rightThumbstick] xAxis] value];
  102. pad.axes[AXIS_INDEX_RIGHT_STICK_Y] =
  103. -[[[extended_gamepad rightThumbstick] yAxis] value];
  104. #define BUTTON(i, b) \
  105. pad.buttons[i].pressed = [b isPressed]; \
  106. pad.buttons[i].value = [b value];
  107. BUTTON(BUTTON_INDEX_PRIMARY, [extended_gamepad buttonA]);
  108. BUTTON(BUTTON_INDEX_SECONDARY, [extended_gamepad buttonB]);
  109. BUTTON(BUTTON_INDEX_TERTIARY, [extended_gamepad buttonX]);
  110. BUTTON(BUTTON_INDEX_QUATERNARY, [extended_gamepad buttonY]);
  111. BUTTON(BUTTON_INDEX_LEFT_SHOULDER, [extended_gamepad leftShoulder]);
  112. BUTTON(BUTTON_INDEX_RIGHT_SHOULDER, [extended_gamepad rightShoulder]);
  113. BUTTON(BUTTON_INDEX_LEFT_TRIGGER, [extended_gamepad leftTrigger]);
  114. BUTTON(BUTTON_INDEX_RIGHT_TRIGGER, [extended_gamepad rightTrigger]);
  115. // No start, select, or thumbstick buttons
  116. BUTTON(BUTTON_INDEX_DPAD_UP, [[extended_gamepad dpad] up]);
  117. BUTTON(BUTTON_INDEX_DPAD_DOWN, [[extended_gamepad dpad] down]);
  118. BUTTON(BUTTON_INDEX_DPAD_LEFT, [[extended_gamepad dpad] left]);
  119. BUTTON(BUTTON_INDEX_DPAD_RIGHT, [[extended_gamepad dpad] right]);
  120. #undef BUTTON
  121. }
  122. }
  123. int GameControllerDataFetcherMac::NextUnusedPlayerIndex() {
  124. for (int i = 0; i < kGCControllerPlayerIndexCount; ++i) {
  125. if (!connected_[i])
  126. return i;
  127. }
  128. return GCControllerPlayerIndexUnset;
  129. }
  130. } // namespace device