123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164 |
- // Copyright 2016 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #include "device/gamepad/game_controller_data_fetcher_mac.h"
- #import <GameController/GameController.h>
- #include <string.h>
- #include <string>
- #include "base/mac/mac_util.h"
- #include "base/strings/sys_string_conversions.h"
- #include "base/strings/utf_string_conversions.h"
- #include "device/gamepad/gamepad_standard_mappings.h"
- namespace device {
- namespace {
- const int kGCControllerPlayerIndexCount = 4;
- // Returns true if |controller| should be enumerated by this data fetcher.
- bool IsSupported(GCController* controller) {
- // We only support the extendedGamepad profile, the basic gamepad profile
- // appears to only be for iOS devices.
- if (![controller extendedGamepad])
- return false;
- // In macOS 10.15, support for some console gamepads was added to the Game
- // Controller framework and a productCategory property was added to enable
- // applications to detect the new devices. These gamepads are already
- // supported in Chrome through other data fetchers and must be blocked here to
- // avoid double-enumeration.
- if (@available(macOS 10.15, *)) {
- NSString* product_category = [controller productCategory];
- if ([product_category isEqualToString:@"Xbox One"] ||
- [product_category isEqualToString:@"DualShock 4"] ||
- [product_category isEqualToString:@"DualSense"] ||
- [product_category isEqualToString:@"Switch Pro Controller"] ||
- [product_category isEqualToString:@"Nintendo Switch JoyCon (L/R)"]) {
- return false;
- }
- }
- return true;
- }
- } // namespace
- GameControllerDataFetcherMac::GameControllerDataFetcherMac() = default;
- GameControllerDataFetcherMac::~GameControllerDataFetcherMac() = default;
- GamepadSource GameControllerDataFetcherMac::source() {
- return Factory::static_source();
- }
- void GameControllerDataFetcherMac::GetGamepadData(bool) {
- NSArray* controllers = [GCController controllers];
- // In the first pass, record which player indices are still in use so unused
- // indices can be assigned to newly connected gamepads.
- bool player_indices[Gamepads::kItemsLengthCap];
- std::fill(player_indices, player_indices + Gamepads::kItemsLengthCap, false);
- for (GCController* controller in controllers) {
- if (!IsSupported(controller))
- continue;
- int player_index = controller.playerIndex;
- if (player_index != GCControllerPlayerIndexUnset)
- player_indices[player_index] = true;
- }
- for (size_t i = 0; i < Gamepads::kItemsLengthCap; ++i) {
- if (connected_[i] && !player_indices[i])
- connected_[i] = false;
- }
- // In the second pass, assign indices to newly connected gamepads and fetch
- // the gamepad state.
- for (GCController* controller in controllers) {
- if (!IsSupported(controller))
- continue;
- int player_index = controller.playerIndex;
- if (player_index == GCControllerPlayerIndexUnset) {
- player_index = NextUnusedPlayerIndex();
- if (player_index == GCControllerPlayerIndexUnset)
- continue;
- }
- PadState* state = GetPadState(player_index);
- if (!state)
- continue;
- Gamepad& pad = state->data;
- // This first time we encounter a gamepad, set its name, mapping, and
- // axes/button counts. This information is static, so it only needs to be
- // done once.
- if (!state->is_initialized) {
- state->is_initialized = true;
- NSString* vendorName = [controller vendorName];
- NSString* ident =
- [NSString stringWithFormat:@"%@ (STANDARD GAMEPAD)",
- vendorName ? vendorName : @"Unknown"];
- pad.mapping = GamepadMapping::kStandard;
- pad.SetID(base::SysNSStringToUTF16(ident));
- pad.axes_length = AXIS_INDEX_COUNT;
- pad.buttons_length = BUTTON_INDEX_COUNT - 1;
- pad.connected = true;
- connected_[player_index] = true;
- controller.playerIndex =
- static_cast<GCControllerPlayerIndex>(player_index);
- }
- pad.timestamp = CurrentTimeInMicroseconds();
- auto extended_gamepad = [controller extendedGamepad];
- pad.axes[AXIS_INDEX_LEFT_STICK_X] =
- [[[extended_gamepad leftThumbstick] xAxis] value];
- pad.axes[AXIS_INDEX_LEFT_STICK_Y] =
- -[[[extended_gamepad leftThumbstick] yAxis] value];
- pad.axes[AXIS_INDEX_RIGHT_STICK_X] =
- [[[extended_gamepad rightThumbstick] xAxis] value];
- pad.axes[AXIS_INDEX_RIGHT_STICK_Y] =
- -[[[extended_gamepad rightThumbstick] yAxis] value];
- #define BUTTON(i, b) \
- pad.buttons[i].pressed = [b isPressed]; \
- pad.buttons[i].value = [b value];
- BUTTON(BUTTON_INDEX_PRIMARY, [extended_gamepad buttonA]);
- BUTTON(BUTTON_INDEX_SECONDARY, [extended_gamepad buttonB]);
- BUTTON(BUTTON_INDEX_TERTIARY, [extended_gamepad buttonX]);
- BUTTON(BUTTON_INDEX_QUATERNARY, [extended_gamepad buttonY]);
- BUTTON(BUTTON_INDEX_LEFT_SHOULDER, [extended_gamepad leftShoulder]);
- BUTTON(BUTTON_INDEX_RIGHT_SHOULDER, [extended_gamepad rightShoulder]);
- BUTTON(BUTTON_INDEX_LEFT_TRIGGER, [extended_gamepad leftTrigger]);
- BUTTON(BUTTON_INDEX_RIGHT_TRIGGER, [extended_gamepad rightTrigger]);
- // No start, select, or thumbstick buttons
- BUTTON(BUTTON_INDEX_DPAD_UP, [[extended_gamepad dpad] up]);
- BUTTON(BUTTON_INDEX_DPAD_DOWN, [[extended_gamepad dpad] down]);
- BUTTON(BUTTON_INDEX_DPAD_LEFT, [[extended_gamepad dpad] left]);
- BUTTON(BUTTON_INDEX_DPAD_RIGHT, [[extended_gamepad dpad] right]);
- #undef BUTTON
- }
- }
- int GameControllerDataFetcherMac::NextUnusedPlayerIndex() {
- for (int i = 0; i < kGCControllerPlayerIndexCount; ++i) {
- if (!connected_[i])
- return i;
- }
- return GCControllerPlayerIndexUnset;
- }
- } // namespace device
|