/* * Copyright (C) 2021 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "GameController.h" #include #include #include #include #include #include "GameControllerInternalConstants.h" namespace paddleboat { struct ControllerButtonMap { int32_t buttonKeycodes[PADDLEBOAT_BUTTON_COUNT]; }; const ControllerButtonMap defaultButtonMap = {{ AKEYCODE_DPAD_UP, // PADDLEBOAT_BUTTON_DPAD_UP AKEYCODE_DPAD_LEFT, // PADDLEBOAT_BUTTON_DPAD_LEFT AKEYCODE_DPAD_DOWN, // PADDLEBOAT_BUTTON_DPAD_DOWN AKEYCODE_DPAD_RIGHT, // PADDLEBOAT_BUTTON_DPAD_RIGHT AKEYCODE_BUTTON_A, // PADDLEBOAT_BUTTON_A AKEYCODE_BUTTON_B, // PADDLEBOAT_BUTTON_B AKEYCODE_BUTTON_X, // PADDLEBOAT_BUTTON_X AKEYCODE_BUTTON_Y, // PADDLEBOAT_BUTTON_Y AKEYCODE_BUTTON_L1, // PADDLEBOAT_BUTTON_L1 AKEYCODE_BUTTON_L2, // PADDLEBOAT_BUTTON_L2 AKEYCODE_BUTTON_THUMBL, // PADDLEBOAT_BUTTON_L3 AKEYCODE_BUTTON_R1, // PADDLEBOAT_BUTTON_R1 AKEYCODE_BUTTON_R2, // PADDLEBOAT_BUTTON_R2 AKEYCODE_BUTTON_THUMBR, // PADDLEBOAT_BUTTON_R3 AKEYCODE_BUTTON_SELECT, // PADDLEBOAT_BUTTON_SELECT AKEYCODE_BUTTON_START, // PADDLEBOAT_BUTTON_START AKEYCODE_BUTTON_MODE, // PADDLEBOAT_BUTTON_SYSTEM 0, // PADDLEBOAT_BUTTON_TOUCHPAD 0, // PADDLEBOAT_BUTTON_AUX1 0, // PADDLEBOAT_BUTTON_AUX2 0, // PADDLEBOAT_BUTTON_AUX3 0 // PADDLEBOAT_BUTTON_AUX4 }}; // Axis must be at least this value to trigger a mapped button press constexpr float AXIS_BUTTON_THRESHOLD = 0.1f; void resetData(Paddleboat_Controller_Data &pbData) { pbData.timestamp = 0; pbData.buttonsDown = 0; pbData.leftStick.stickX = 0.0f; pbData.leftStick.stickY = 0.0f; pbData.rightStick.stickX = 0.0f; pbData.rightStick.stickY = 0.0f; pbData.triggerL1 = 0.0f; pbData.triggerL2 = 0.0f; pbData.triggerR1 = 0.0f; pbData.triggerR2 = 0.0f; pbData.virtualPointer.pointerX = 0.0f; pbData.virtualPointer.pointerY = 0.0f; } void resetInfo(Paddleboat_Controller_Info &pbInfo) { pbInfo.controllerNumber = -1; pbInfo.controllerFlags = 0; pbInfo.vendorId = 0; pbInfo.productId = 0; pbInfo.deviceId = -1; pbInfo.leftStickPrecision.stickFlatX = 0.0f; pbInfo.leftStickPrecision.stickFlatY = 0.0f; pbInfo.leftStickPrecision.stickFuzzX = 0.0f; pbInfo.leftStickPrecision.stickFuzzY = 0.0f; pbInfo.rightStickPrecision.stickFlatX = 0.0f; pbInfo.rightStickPrecision.stickFlatY = 0.0f; pbInfo.rightStickPrecision.stickFuzzX = 0.0f; pbInfo.rightStickPrecision.stickFuzzY = 0.0f; } GameController::GameController() : mConnectionIndex(-1), mControllerData(), mControllerInfo(), mAxisInfo(), mDeviceInfo(), mControllerDataDirty(true) { memset(mButtonKeycodes, 0, sizeof(mButtonKeycodes)); resetControllerData(); } void GameController::resetControllerData() { resetData(mControllerData); resetInfo(mControllerInfo); } void GameController::setupController( const Paddleboat_Controller_Mapping_Data *mappingData) { const GameControllerDeviceInfo::InfoFields &infoFields = *(mDeviceInfo.getInfo()); uint64_t axisLow = static_cast(infoFields.mAxisBitsLow); uint64_t axisHigh = static_cast(infoFields.mAxisBitsHigh); mControllerAxisMask = axisLow | (axisHigh << 32ULL); mControllerInfo.controllerFlags = infoFields.mControllerFlags; mControllerInfo.controllerNumber = infoFields.mControllerNumber; mControllerInfo.deviceId = infoFields.mDeviceId; mControllerInfo.productId = infoFields.mProductId; mControllerInfo.vendorId = infoFields.mVendorId; if (mappingData != nullptr) { mControllerInfo.controllerFlags |= mappingData->flags; for (int32_t i = 0; i < PADDLEBOAT_BUTTON_COUNT; ++i) { if (mappingData->buttonMapping[i] != PADDLEBOAT_BUTTON_IGNORED) { mButtonKeycodes[i] = mappingData->buttonMapping[i]; } } for (int32_t i = 0; i < PADDLEBOAT_MAPPING_AXIS_COUNT; ++i) { if (mappingData->axisMapping[i] != PADDLEBOAT_AXIS_IGNORED) { const GameControllerAxis gcAxis = static_cast(i); const int32_t nativeAxisId = mappingData->axisMapping[i]; const int32_t positiveButton = (mappingData->axisPositiveButtonMapping[i] == PADDLEBOAT_AXIS_BUTTON_IGNORED) ? 0 : (1 << mappingData->axisPositiveButtonMapping[i]); const int32_t negativeButton = (mappingData->axisNegativeButtonMapping[i] == PADDLEBOAT_AXIS_BUTTON_IGNORED) ? 0 : (1 << mappingData->axisNegativeButtonMapping[i]); setupAxis(gcAxis, nativeAxisId, nativeAxisId, positiveButton, negativeButton); } } adjustAxisConstants(); } else { // Fallback defaults if there wasn't mapping data provided initializeDefaultAxisMapping(); memcpy(mButtonKeycodes, &defaultButtonMap, sizeof(ControllerButtonMap)); mControllerInfo.controllerFlags |= PADDLEBOAT_CONTROLLER_FLAG_GENERIC_PROFILE; } } void GameController::initializeDefaultAxisMapping() { setupAxis(GAMECONTROLLER_AXIS_LSTICK_X, AMOTION_EVENT_AXIS_X, AMOTION_EVENT_AXIS_X, 0, 0); setupAxis(GAMECONTROLLER_AXIS_LSTICK_Y, AMOTION_EVENT_AXIS_Y, AMOTION_EVENT_AXIS_Y, 0, 0); setupAxis(GAMECONTROLLER_AXIS_RSTICK_X, AMOTION_EVENT_AXIS_Z, AMOTION_EVENT_AXIS_RX, 0, 0); setupAxis(GAMECONTROLLER_AXIS_RSTICK_Y, AMOTION_EVENT_AXIS_RZ, AMOTION_EVENT_AXIS_RY, 0, 0); setupAxis(GAMECONTROLLER_AXIS_L2, AMOTION_EVENT_AXIS_LTRIGGER, AMOTION_EVENT_AXIS_BRAKE, PADDLEBOAT_BUTTON_L2, 0); setupAxis(GAMECONTROLLER_AXIS_R2, AMOTION_EVENT_AXIS_RTRIGGER, AMOTION_EVENT_AXIS_GAS, PADDLEBOAT_BUTTON_R2, 0); setupAxis(GAMECONTROLLER_AXIS_HAT_X, AMOTION_EVENT_AXIS_HAT_X, AMOTION_EVENT_AXIS_HAT_X, PADDLEBOAT_BUTTON_DPAD_RIGHT, PADDLEBOAT_BUTTON_DPAD_LEFT); setupAxis(GAMECONTROLLER_AXIS_HAT_Y, AMOTION_EVENT_AXIS_HAT_Y, AMOTION_EVENT_AXIS_HAT_Y, PADDLEBOAT_BUTTON_DPAD_DOWN, PADDLEBOAT_BUTTON_DPAD_UP); adjustAxisConstants(); } void GameController::adjustAxisConstants() { if (mAxisInfo[GAMECONTROLLER_AXIS_LSTICK_X].axisIndex >= 0) { const bool stickAxisAdjust = ((mAxisInfo[GAMECONTROLLER_AXIS_LSTICK_X].axisFlags & GAMECONTROLLER_AXIS_FLAG_APPLY_ADJUSTMENTS) != 0); mControllerInfo.leftStickPrecision.stickFlatX = mDeviceInfo.getFlatArray()[mAxisInfo[GAMECONTROLLER_AXIS_LSTICK_X] .axisIndex]; mControllerInfo.leftStickPrecision.stickFlatY = mDeviceInfo.getFlatArray()[mAxisInfo[GAMECONTROLLER_AXIS_LSTICK_Y] .axisIndex]; mControllerInfo.leftStickPrecision.stickFuzzX = mDeviceInfo.getFuzzArray()[mAxisInfo[GAMECONTROLLER_AXIS_LSTICK_X] .axisIndex]; mControllerInfo.leftStickPrecision.stickFuzzY = mDeviceInfo.getFuzzArray()[mAxisInfo[GAMECONTROLLER_AXIS_LSTICK_Y] .axisIndex]; if (stickAxisAdjust) { // We are adjusting the raw axis values, so we also adjust the // 'flat' and 'fuzz' values for the sticks mControllerInfo.leftStickPrecision.stickFlatX *= mAxisInfo[GAMECONTROLLER_AXIS_LSTICK_X].axisMultiplier; mControllerInfo.leftStickPrecision.stickFlatY *= mAxisInfo[GAMECONTROLLER_AXIS_LSTICK_Y].axisMultiplier; mControllerInfo.leftStickPrecision.stickFuzzX *= mAxisInfo[GAMECONTROLLER_AXIS_LSTICK_X].axisMultiplier; mControllerInfo.leftStickPrecision.stickFuzzY *= mAxisInfo[GAMECONTROLLER_AXIS_LSTICK_Y].axisMultiplier; } } if (mAxisInfo[GAMECONTROLLER_AXIS_RSTICK_X].axisIndex >= 0) { const bool stickAxisAdjust = ((mAxisInfo[GAMECONTROLLER_AXIS_RSTICK_X].axisFlags & GAMECONTROLLER_AXIS_FLAG_APPLY_ADJUSTMENTS) != 0); mControllerInfo.rightStickPrecision.stickFlatX = mDeviceInfo.getFlatArray()[mAxisInfo[GAMECONTROLLER_AXIS_RSTICK_X] .axisIndex]; mControllerInfo.rightStickPrecision.stickFlatY = mDeviceInfo.getFlatArray()[mAxisInfo[GAMECONTROLLER_AXIS_RSTICK_Y] .axisIndex]; mControllerInfo.rightStickPrecision.stickFuzzX = mDeviceInfo.getFuzzArray()[mAxisInfo[GAMECONTROLLER_AXIS_RSTICK_X] .axisIndex]; mControllerInfo.rightStickPrecision.stickFuzzY = mDeviceInfo.getFuzzArray()[mAxisInfo[GAMECONTROLLER_AXIS_RSTICK_Y] .axisIndex]; if (stickAxisAdjust) { // We are adjusting the raw axis values, so we also adjust the // 'flat' and 'fuzz' values for the sticks mControllerInfo.rightStickPrecision.stickFlatX *= mAxisInfo[GAMECONTROLLER_AXIS_RSTICK_X].axisMultiplier; mControllerInfo.rightStickPrecision.stickFlatY *= mAxisInfo[GAMECONTROLLER_AXIS_RSTICK_Y].axisMultiplier; mControllerInfo.rightStickPrecision.stickFuzzX *= mAxisInfo[GAMECONTROLLER_AXIS_RSTICK_X].axisMultiplier; mControllerInfo.rightStickPrecision.stickFuzzY *= mAxisInfo[GAMECONTROLLER_AXIS_RSTICK_Y].axisMultiplier; } } } void GameController::setupAxis(const GameControllerAxis gcAxis, const int32_t preferredNativeAxisId, const int32_t secondaryNativeAxisId, const int32_t buttonMask, const int32_t buttonNegativeMask) { mAxisInfo[gcAxis].resetInfo(); mAxisInfo[gcAxis].axisButtonMask = buttonMask; mAxisInfo[gcAxis].axisButtonNegativeMask = buttonNegativeMask; // Do we have a mapping for the preferred native axis? if (preferredNativeAxisId < MAX_AXIS_COUNT && secondaryNativeAxisId < MAX_AXIS_COUNT) { const int32_t preferredMask = (1U << preferredNativeAxisId); const int32_t secondaryMask = (1U << secondaryNativeAxisId); if ((mControllerAxisMask & preferredMask) != 0) { // preferred axis is mapped mAxisInfo[gcAxis].axisIndex = preferredNativeAxisId; } else if ((preferredNativeAxisId != secondaryNativeAxisId) && ((mControllerAxisMask & secondaryMask) != 0)) { // secondary axis is mapped mAxisInfo[gcAxis].axisIndex = secondaryNativeAxisId; } else if (buttonMask) { // There wasn't a matching axis, // but we will fake this axis using a digital button mapping mAxisInfo[gcAxis].axisFlags |= GAMECONTROLLER_AXIS_FLAG_DIGITAL_TRIGGER; } // If we found a native axis, check its ranges and see if we need to set // up adjustment values if the ranges aren't -1.0 to 1.0 for a stick or // 0.0 to 1.0 for a trigger if (mAxisInfo[gcAxis].axisIndex >= 0) { bool isStickAxis = (!(gcAxis >= GAMECONTROLLER_AXIS_L1 && gcAxis <= GAMECONTROLLER_AXIS_R2)); const float minAdjust = isStickAxis ? 1.0f : 0.0f; const float rawMin = mDeviceInfo.getMinArray()[mAxisInfo[gcAxis].axisIndex]; const float rawMax = mDeviceInfo.getMaxArray()[mAxisInfo[gcAxis].axisIndex]; const float diffMin = fabsf(rawMin + minAdjust); const float diffMax = fabsf(1.0f - rawMax); if (!(diffMin <= FLT_MIN && diffMax <= FLT_MIN)) { mAxisInfo[gcAxis].axisFlags |= GAMECONTROLLER_AXIS_FLAG_APPLY_ADJUSTMENTS; if (isStickAxis) { // normalize min and max axis to 1.0 mAxisInfo[gcAxis].axisMultiplier = 1.0f / rawMax; // Make sure center of stick is 0.0 const float rawCenter = ((rawMax - rawMin) * 0.5f) + rawMin; if (rawCenter >= FLT_MIN) { mAxisInfo[gcAxis].axisAdjust = -(rawCenter * mAxisInfo[gcAxis].axisMultiplier); } } else { // This case is hit on PS5 API <= 30 having weird trigger // axis mappings of: L2=RX R2=RY with a -1.0 to 1.0 range mAxisInfo[gcAxis].axisMultiplier = 1.0f / (rawMax - rawMin); mAxisInfo[gcAxis].axisAdjust = (-rawMin) * mAxisInfo[gcAxis].axisMultiplier; } } } } } int32_t GameController::processGameActivityKeyEvent( const Paddleboat_GameActivityKeyEvent *event, const size_t eventSize) { return processKeyEventInternal(event->keyCode, event->action); } int32_t GameController::processGameActivityMotionEvent( const Paddleboat_GameActivityMotionEvent *event, const size_t eventSize) { int32_t handledEvent = IGNORED_EVENT; if (event->pointerCount > 0) { handledEvent = processMotionEventInternal(event->pointers->axisValues, nullptr); } return handledEvent; } int32_t GameController::processKeyEvent(const AInputEvent *event) { const int32_t eventKeyCode = AKeyEvent_getKeyCode(event); const int32_t eventKeyAction = AKeyEvent_getAction(event); return processKeyEventInternal(eventKeyCode, eventKeyAction); } int32_t GameController::processKeyEventInternal(const int32_t eventKeyCode, const int32_t eventKeyAction) { int32_t handledEvent = IGNORED_EVENT; int32_t buttonMask = 0; const bool bDown = (eventKeyAction == AKEY_EVENT_ACTION_DOWN); for (uint32_t i = 0; i < PADDLEBOAT_BUTTON_COUNT; ++i) { if (eventKeyCode == mButtonKeycodes[i]) { int32_t newButtonFlag = (1 << i); buttonMask |= newButtonFlag; // If we have no analog axis mapped for a trigger, allow the // button events to set the analog trigger data switch (newButtonFlag) { case PADDLEBOAT_BUTTON_L1: if (mAxisInfo[GAMECONTROLLER_AXIS_L1].axisIndex == -1) { mControllerData.triggerL1 = bDown ? 1.0f : 0.0f; } break; case PADDLEBOAT_BUTTON_L2: if (mAxisInfo[GAMECONTROLLER_AXIS_L2].axisIndex == -1) { mControllerData.triggerL2 = bDown ? 1.0f : 0.0f; } break; case PADDLEBOAT_BUTTON_R1: if (mAxisInfo[GAMECONTROLLER_AXIS_R1].axisIndex == -1) { mControllerData.triggerR1 = bDown ? 1.0f : 0.0f; } break; case PADDLEBOAT_BUTTON_R2: if (mAxisInfo[GAMECONTROLLER_AXIS_R2].axisIndex == -1) { mControllerData.triggerR2 = bDown ? 1.0f : 0.0f; } break; default: break; } break; } } if (buttonMask != 0) { if (bDown) { mControllerData.buttonsDown |= buttonMask; setControllerDataDirty(true); } else { mControllerData.buttonsDown &= (~buttonMask); setControllerDataDirty(true); } handledEvent = HANDLED_EVENT; } return handledEvent; } int32_t GameController::processMotionEvent(const AInputEvent *event) { return processMotionEventInternal(nullptr, event); } int32_t GameController::processMotionEventInternal(const float *axisArray, const AInputEvent *event) { int32_t handledEvent = IGNORED_EVENT; for (uint32_t axis = GAMECONTROLLER_AXIS_LSTICK_X; axis < GAMECONTROLLER_AXIS_COUNT; ++axis) { if (mAxisInfo[axis].axisIndex >= 0 && mAxisInfo[axis].axisIndex < PADDLEBOAT_GAME_ACTIVITY_POINTER_INFO_AXIS_COUNT) { float axisValue = 0.0f; if (axisArray != nullptr) { axisValue = axisArray[mAxisInfo[axis].axisIndex]; } else if (event != nullptr) { axisValue = AMotionEvent_getAxisValue( event, mAxisInfo[axis].axisIndex, 0); } if ((mAxisInfo[axis].axisFlags & GAMECONTROLLER_AXIS_FLAG_APPLY_ADJUSTMENTS) != 0) { axisValue = ((axisValue * mAxisInfo[axis].axisMultiplier) + mAxisInfo[axis].axisAdjust); } // We take advantage of the GameControllerAxis matching the axis // order in the Paddleboat_Controller_Data struct to use the current // axis as an index into the axis entries in the // Paddleboat_Controller_Data struct if (axis < GAMECONTROLLER_AXIS_HAT_X) { float *axisData = &mControllerData.leftStick.stickX; axisData[axis] = axisValue; } // If this axis has a button associated with it, set/clear the flags // as appropriate if (mAxisInfo[axis].axisButtonMask != 0 || mAxisInfo[axis].axisButtonNegativeMask) { if (axisValue > -AXIS_BUTTON_THRESHOLD && axisValue < AXIS_BUTTON_THRESHOLD) { const uint32_t buttonMask = mAxisInfo[axis].axisButtonMask | mAxisInfo[axis].axisButtonNegativeMask; mControllerData.buttonsDown &= (~buttonMask); } else if (axisValue > AXIS_BUTTON_THRESHOLD) { mControllerData.buttonsDown |= mAxisInfo[axis].axisButtonMask; } else if (axisValue < -AXIS_BUTTON_THRESHOLD) { mControllerData.buttonsDown |= mAxisInfo[axis].axisButtonNegativeMask; } } setControllerDataDirty(true); handledEvent = HANDLED_EVENT; } } return handledEvent; } void GameController::setControllerDataDirty(const bool dirty) { mControllerDataDirty = dirty; if (dirty) { // update the timestamp any time we mark dirty const auto timestamp = std::chrono::duration_cast( std::chrono::steady_clock::now().time_since_epoch()) .count(); mControllerData.timestamp = static_cast(timestamp); } } } // namespace paddleboat