diff --git a/Common/Input/InputState.cpp b/Common/Input/InputState.cpp index 67c2b6415a42..a788ed10a855 100644 --- a/Common/Input/InputState.cpp +++ b/Common/Input/InputState.cpp @@ -1,6 +1,8 @@ +#include +#include + #include "Common/Input/InputState.h" #include "Common/Input/KeyCodes.h" -#include const char *GetDeviceName(int deviceId) { switch (deviceId) { @@ -76,3 +78,13 @@ int GetAnalogYDirection(int deviceId) { return configured->second; return 0; } + +void InputMapping::FormatDebug(char *buffer, size_t bufSize) const { + if (IsAxis()) { + int direction; + int axis = Axis(&direction); + snprintf(buffer, bufSize, "Device: %d Axis: %d (%d)", deviceId, axis, direction); + } else { + snprintf(buffer, bufSize, "Device: %d Key: %d", deviceId, keyCode); + } +} diff --git a/Common/Input/InputState.h b/Common/Input/InputState.h index 5de44996ede6..b1e30acefa7d 100644 --- a/Common/Input/InputState.h +++ b/Common/Input/InputState.h @@ -117,6 +117,13 @@ class InputMapping { return TranslateKeyCodeToAxis(keyCode, direction); } + InputMapping FlipDirection() const { + _dbg_assert_(IsAxis()); + InputMapping other = *this; + other.keyCode ^= 1; + return other; + } + // If you want to use std::find and match ANY, you need to perform an explicit search for that. bool operator < (const InputMapping &other) const { if (deviceId < other.deviceId) return true; @@ -129,6 +136,8 @@ class InputMapping { if (keyCode != other.keyCode) return false; return true; } + + void FormatDebug(char *buffer, size_t bufSize) const; }; enum { @@ -183,7 +192,6 @@ struct AxisInput { int deviceId; int axisId; // Android axis Ids are the canonical ones. float value; - int flags; }; // Is there a nicer place for this stuff? It's here to avoid dozens of linking errors in UnitTest.. diff --git a/Common/VR/PPSSPPVR.cpp b/Common/VR/PPSSPPVR.cpp index 9e00e75aa495..30df54352c54 100644 --- a/Common/VR/PPSSPPVR.cpp +++ b/Common/VR/PPSSPPVR.cpp @@ -9,6 +9,9 @@ #include "Common/VR/VRMath.h" #include "Common/VR/VRRenderer.h" +#include "Common/Input/InputState.h" +#include "Common/Input/KeyCodes.h" + #include "Common/GPU/Vulkan/VulkanContext.h" #include "Common/Math/lin/matrix4x4.h" diff --git a/Core/Config.cpp b/Core/Config.cpp index b93e04886a15..1debc4692036 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -849,7 +849,6 @@ static const ConfigSetting graphicsSettings[] = { ConfigSetting("FrameRate", &g_Config.iFpsLimit1, 0, true, true), ConfigSetting("FrameRate2", &g_Config.iFpsLimit2, -1, true, true), ConfigSetting("AnalogFrameRate", &g_Config.iAnalogFpsLimit, 240, true, true), - ConfigSetting("AnalogFrameRateMode", &g_Config.iAnalogFpsMode, 0, true, true), ConfigSetting("UnthrottlingMode", &g_Config.iFastForwardMode, &DefaultFastForwardMode, &FastForwardModeToString, &FastForwardModeFromString, true, true), #if defined(USING_WIN_UI) ConfigSetting("RestartRequired", &g_Config.bRestartRequired, false, false), diff --git a/Core/Config.h b/Core/Config.h index a022e8105da7..17c5197a9712 100644 --- a/Core/Config.h +++ b/Core/Config.h @@ -220,7 +220,6 @@ struct Config { int iFpsLimit1; int iFpsLimit2; int iAnalogFpsLimit; - int iAnalogFpsMode; // 0 = auto, 1 = single direction, 2 = mapped to opposite int iMaxRecent; int iCurrentStateSlot; int iRewindSnapshotInterval; @@ -277,9 +276,11 @@ struct Config { std::string sThemeName; + // These aren't saved, just for instant debugging. bool bLogFrameDrops; bool bShowDebugStats; bool bShowAudioDebug; + bool bShowControlDebug; bool bShowGpuProfile; // Analog stick tilting diff --git a/Core/ConfigValues.h b/Core/ConfigValues.h index 6945dfacf0ac..96690ecc5d46 100644 --- a/Core/ConfigValues.h +++ b/Core/ConfigValues.h @@ -132,12 +132,6 @@ enum class BackgroundAnimation { MOVING_BACKGROUND = 4, }; -enum class AnalogFpsMode { - AUTO = 0, - MAPPED_DIRECTION = 1, - MAPPED_DIR_TO_OPPOSITE_DIR = 2, -}; - // for Config.iShowStatusFlags enum class ShowStatusFlags { FPS_COUNTER = 1 << 1, diff --git a/Core/ControlMapper.cpp b/Core/ControlMapper.cpp index 3cef1079f123..60b071d88002 100644 --- a/Core/ControlMapper.cpp +++ b/Core/ControlMapper.cpp @@ -1,7 +1,9 @@ #include +#include #include "Common/Math/math_util.h" #include "Common/TimeUtil.h" +#include "Common/StringUtils.h" #include "Common/Log.h" #include "Core/HLE/sceCtrl.h" @@ -11,6 +13,50 @@ #include "Core/CoreParameter.h" #include "Core/System.h" +// TODO: Possibly make these thresholds configurable? +static float GetDeviceAxisThreshold(int device) { + return device == DEVICE_ID_MOUSE ? AXIS_BIND_THRESHOLD_MOUSE : AXIS_BIND_THRESHOLD; +} + +static int GetOppositeVKey(int vkey) { + switch (vkey) { + case VIRTKEY_AXIS_X_MIN: return VIRTKEY_AXIS_X_MAX; break; + case VIRTKEY_AXIS_X_MAX: return VIRTKEY_AXIS_X_MIN; break; + case VIRTKEY_AXIS_Y_MIN: return VIRTKEY_AXIS_Y_MAX; break; + case VIRTKEY_AXIS_Y_MAX: return VIRTKEY_AXIS_Y_MIN; break; + case VIRTKEY_AXIS_RIGHT_X_MIN: return VIRTKEY_AXIS_RIGHT_X_MAX; break; + case VIRTKEY_AXIS_RIGHT_X_MAX: return VIRTKEY_AXIS_RIGHT_X_MIN; break; + case VIRTKEY_AXIS_RIGHT_Y_MIN: return VIRTKEY_AXIS_RIGHT_Y_MAX; break; + case VIRTKEY_AXIS_RIGHT_Y_MAX: return VIRTKEY_AXIS_RIGHT_Y_MIN; break; + default: + return 0; + } +} + +static bool IsAxisVKey(int vkey) { + // Little hacky but works, of course. + return GetOppositeVKey(vkey) != 0; +} + +static bool IsUnsignedMapping(int vkey) { + return vkey == VIRTKEY_SPEED_ANALOG; +} + +static bool IsSignedAxis(int axis) { + switch (axis) { + case JOYSTICK_AXIS_X: + case JOYSTICK_AXIS_Y: + case JOYSTICK_AXIS_Z: + case JOYSTICK_AXIS_RX: + case JOYSTICK_AXIS_RY: + case JOYSTICK_AXIS_RZ: + return true; + default: + return false; + } +} + +// This is applied on the circular radius, not directly on the axes. static float MapAxisValue(float v) { const float deadzone = g_Config.fAnalogDeadzone; const float invDeadzone = g_Config.fAnalogInverseDeadzone; @@ -20,13 +66,15 @@ static float MapAxisValue(float v) { return sign * Clamp(invDeadzone + (abs(v) - deadzone) / (1.0f - deadzone) * (sensitivity - invDeadzone), 0.0f, 1.0f); } -void ConvertAnalogStick(float &x, float &y) { +void ConvertAnalogStick(float x, float y, float *outX, float *outY) { const bool isCircular = g_Config.bAnalogIsCircular; float norm = std::max(fabsf(x), fabsf(y)); - - if (norm == 0.0f) + if (norm == 0.0f) { + *outX = x; + *outY = y; return; + } if (isCircular) { float newNorm = sqrtf(x * x + y * y); @@ -37,26 +85,24 @@ void ConvertAnalogStick(float &x, float &y) { } float mappedNorm = MapAxisValue(norm); - x = Clamp(x / norm * mappedNorm, -1.0f, 1.0f); - y = Clamp(y / norm * mappedNorm, -1.0f, 1.0f); + *outX = Clamp(x / norm * mappedNorm, -1.0f, 1.0f); + *outY = Clamp(y / norm * mappedNorm, -1.0f, 1.0f); } void ControlMapper::SetCallbacks( std::function onVKey, - std::function setAllPSPButtonStates, - std::function setPSPButtonState, - std::function setPSPAnalog) { + std::function onVKeyAnalog, + std::function updatePSPButtons, + std::function setPSPAnalog, + std::function setRawAnalog) { onVKey_ = onVKey; - setAllPSPButtonStates_ = setAllPSPButtonStates; - setPSPButtonState_ = setPSPButtonState; + onVKeyAnalog_ = onVKeyAnalog; + updatePSPButtons_ = updatePSPButtons; setPSPAnalog_ = setPSPAnalog; -} - -void ControlMapper::SetRawCallback(std::function setRawAnalog) { setRawAnalog_ = setRawAnalog; } -void ControlMapper::SetPSPAxis(int device, char axis, float value, int stick) { +void ControlMapper::SetPSPAxis(int device, int stick, char axis, float value) { int axisId = axis == 'X' ? 0 : 1; float position[2]; @@ -88,49 +134,229 @@ void ControlMapper::SetPSPAxis(int device, char axis, float value, int stick) { if (!ignore) { history_[stick][axisId] = value; + float x, y; + ConvertAnalogStick(history_[stick][0], history_[stick][1], &x, &y); + converted_[stick][0] = x; + converted_[stick][1] = y; + setPSPAnalog_(stick, x, y); + } +} - float x = history_[stick][0]; - float y = history_[stick][1]; +static int RotatePSPKeyCode(int x) { + switch (x) { + case CTRL_UP: return CTRL_RIGHT; + case CTRL_RIGHT: return CTRL_DOWN; + case CTRL_DOWN: return CTRL_LEFT; + case CTRL_LEFT: return CTRL_UP; + default: + return x; + } +} - ConvertAnalogStick(x, y); - setPSPAnalog_(stick, x, y); +// Used to decay analog values when clashing with digital ones. +static float ReduceMagnitude(float value) { + value *= 0.75f; + if ((value > 0.0f && value < 0.05f) || (value < 0.0f && value > -0.05f)) { + value = 0.0f; } + return value; } -bool ControlMapper::Key(const KeyInput &key, bool *pauseTrigger) { - std::vector pspKeys; - KeyMap::InputMappingToPspButton(InputMapping(key.deviceId, key.keyCode), &pspKeys); +float ControlMapper::MapAxisValue(float value, int vkId, const InputMapping &mapping, const InputMapping &changedMapping, bool *oppositeTouched) { + if (IsUnsignedMapping(vkId)) { + // If a signed axis is mapped to an unsigned mapping, + // convert it. This happens when mapping DirectInput triggers to analog speed, + // for example. + int direction; + if (IsSignedAxis(mapping.Axis(&direction))) { + // The value has been split up into two curInput values, so we need to go fetch the other + // and put them back together again. Kind of awkward, but at least makes the regular case simple... + InputMapping other = mapping.FlipDirection(); + if (other == changedMapping) { + *oppositeTouched = true; + } + float valueOther = curInput_[other]; + float signedValue = value - valueOther; + float ranged = (signedValue + 1.0f) * 0.5f; + if (direction == -1) { + ranged = 1.0f - ranged; + } + // NOTICE_LOG(SYSTEM, "rawValue: %f other: %f signed: %f ranged: %f", iter->second, valueOther, signedValue, ranged); + return ranged; + } else { + return value; + } + } else { + return value; + } +} + +// Can only be called from Key or Axis. +bool ControlMapper::UpdatePSPState(const InputMapping &changedMapping) { + // Instead of taking an input key and finding what it outputs, we loop through the OUTPUTS and + // see if the input that corresponds to it has a value. That way we can easily implement all sorts + // of crazy input combos if needed. + + int rotations = 0; + switch (g_Config.iInternalScreenRotation) { + case ROTATION_LOCKED_HORIZONTAL180: rotations = 2; break; + case ROTATION_LOCKED_VERTICAL: rotations = 1; break; + case ROTATION_LOCKED_VERTICAL180: rotations = 3; break; + } + + // For the PSP's button inputs, we just go through and put the flags together. + uint32_t buttonMask = 0; + uint32_t changedButtonMask = 0; + for (int i = 0; i < 32; i++) { + uint32_t mask = 1 << i; + if (!(mask & CTRL_MASK_USER)) { + // Not a mappable button bit + continue; + } + + uint32_t mappingBit = mask; + for (int i = 0; i < rotations; i++) { + mappingBit = RotatePSPKeyCode(mappingBit); + } + + std::vector inputMappings; + if (!KeyMap::InputMappingsFromPspButton(mappingBit, &inputMappings, false)) + continue; + + // If a mapping could consist of a combo, we could trivially check it here. + for (auto &mapping : inputMappings) { + // Check if the changed mapping was involved in this PSP key. + if (changedMapping == mapping) { + changedButtonMask |= mask; + } + + auto iter = curInput_.find(mapping); + if (iter != curInput_.end() && iter->second > GetDeviceAxisThreshold(iter->first.deviceId)) { + buttonMask |= mask; + } + } + } + + // We only request changing the buttons where the mapped input was involved. + updatePSPButtons_(buttonMask & changedButtonMask, (~buttonMask) & changedButtonMask); + + // OK, handle all the virtual keys next. For these we need to do deltas here and send events. + for (int i = 0; i < VIRTKEY_COUNT; i++) { + int vkId = i + VIRTKEY_FIRST; + std::vector inputMappings; + if (!KeyMap::InputMappingsFromPspButton(vkId, &inputMappings, false)) + continue; + + // If a mapping could consist of a combo, we could trivially check it here. + // Save the first device ID so we can pass it into onVKeyDown, which in turn needs it for the analog + // mapping which gets a little hacky. + float threshold = 1.0f; + bool touchedByMapping = false; + float value = 0.0f; + for (auto &mapping : inputMappings) { + if (mapping == changedMapping) { + touchedByMapping = true; + } + + auto iter = curInput_.find(mapping); + if (iter != curInput_.end()) { + if (mapping.IsAxis()) { + threshold = GetDeviceAxisThreshold(iter->first.deviceId); + value += MapAxisValue(iter->second, vkId, mapping, changedMapping, &touchedByMapping); + } else { + value += iter->second; + } + } + } + + if (!touchedByMapping) { + continue; + } + + // Small values from analog inputs like gamepad sticks can linger around, which is bad here because we sum + // up before applying deadzone etc. This means that it can be impossible to reach the min/max values with digital input! + // So if non-analog events clash with analog ones mapped to the same input, decay the analog input, + // which will quickly get things back to normal, while if it's intentional to use both at the same time for some reason, + // that still works, though a bit weaker. We could also zero here, but you never know who relies on such strange tricks.. + // Note: This is an old problem, it didn't appear with the refactoring. + if (!changedMapping.IsAxis()) { + for (auto &mapping : inputMappings) { + if (mapping.IsAxis()) { + curInput_[mapping] = ReduceMagnitude(curInput_[mapping]); + } + } + } + + value = clamp_value(value, 0.0f, 1.0f); + + // Derive bools from the floats using the device's threshold. + // NOTE: This must be before the equality check below. + bool bPrevValue = virtKeys_[i] >= threshold; + bool bValue = value >= threshold; + + if (virtKeys_[i] != value) { + // INFO_LOG(G3D, "vkeyanalog %s : %f", KeyMap::GetVirtKeyName(vkId), value); + onVKeyAnalog(changedMapping.deviceId, vkId, value); + virtKeys_[i] = value; + } + + if (!bPrevValue && bValue) { + // INFO_LOG(G3D, "vkeyon %s", KeyMap::GetVirtKeyName(vkId)); + onVKey(vkId, true); + } else if (bPrevValue && !bValue) { + // INFO_LOG(G3D, "vkeyoff %s", KeyMap::GetVirtKeyName(vkId)); + onVKey(vkId, false); + } + } + + return true; +} - if (pspKeys.size() && (key.flags & KEY_IS_REPEAT)) { +bool ControlMapper::Key(const KeyInput &key, bool *pauseTrigger) { + if (key.flags & KEY_IS_REPEAT) { // Claim that we handled this. Prevents volume key repeats from popping up the volume control on Android. return true; } - for (size_t i = 0; i < pspKeys.size(); i++) { - SetPSPKey(key.deviceId, pspKeys[i], key.flags); - } + std::lock_guard guard(mutex_); - DEBUG_LOG(SYSTEM, "Key: %d DeviceId: %d", key.keyCode, key.deviceId); + InputMapping mapping(key.deviceId, key.keyCode); + + if (key.flags & KEY_DOWN) { + curInput_[mapping] = 1.0f; + } else if (key.flags & KEY_UP) { + curInput_[mapping] = 0.0f; + } - if (!pspKeys.size() || key.deviceId == DEVICE_ID_DEFAULT) { - if ((key.flags & KEY_DOWN) && key.keyCode == NKCODE_BACK) { + // TODO: See if this can be simplified further somehow. + if ((key.flags & KEY_DOWN) && key.keyCode == NKCODE_BACK) { + bool mappingFound = KeyMap::InputMappingToPspButton(mapping, nullptr); + DEBUG_LOG(SYSTEM, "Key: %d DeviceId: %d", key.keyCode, key.deviceId); + if (!mappingFound || key.deviceId == DEVICE_ID_DEFAULT) { *pauseTrigger = true; return true; } } - return pspKeys.size() > 0; + return UpdatePSPState(mapping); } void ControlMapper::Axis(const AxisInput &axis) { - if (axis.value > 0) { - ProcessAxis(axis, 1); - } else if (axis.value < 0) { - ProcessAxis(axis, -1); - } else if (axis.value == 0) { - // Both directions! Prevents sticking for digital input devices that are axises (like HAT) - ProcessAxis(axis, 1); - ProcessAxis(axis, -1); + std::lock_guard guard(mutex_); + if (axis.value >= 0.0f) { + InputMapping mapping(axis.deviceId, axis.axisId, 1); + InputMapping opposite(axis.deviceId, axis.axisId, -1); + curInput_[mapping] = axis.value; + curInput_[opposite] = 0.0f; + UpdatePSPState(mapping); + UpdatePSPState(opposite); + } else if (axis.value < 0.0f) { + InputMapping mapping(axis.deviceId, axis.axisId, -1); + InputMapping opposite(axis.deviceId, axis.axisId, 1); + curInput_[mapping] = -axis.value; + curInput_[opposite] = 0.0f; + UpdatePSPState(mapping); + UpdatePSPState(opposite); } } @@ -151,117 +377,60 @@ void ControlMapper::Update() { } } -inline bool IsAnalogStickKey(int key) { - switch (key) { - case VIRTKEY_AXIS_X_MIN: - case VIRTKEY_AXIS_X_MAX: - case VIRTKEY_AXIS_Y_MIN: - case VIRTKEY_AXIS_Y_MAX: - case VIRTKEY_AXIS_RIGHT_X_MIN: - case VIRTKEY_AXIS_RIGHT_X_MAX: - case VIRTKEY_AXIS_RIGHT_Y_MIN: - case VIRTKEY_AXIS_RIGHT_Y_MAX: - return true; - default: - return false; - } -} - -static int RotatePSPKeyCode(int x) { - switch (x) { - case CTRL_UP: return CTRL_RIGHT; - case CTRL_RIGHT: return CTRL_DOWN; - case CTRL_DOWN: return CTRL_LEFT; - case CTRL_LEFT: return CTRL_UP; - default: - return x; - } -} - -void ControlMapper::SetVKeyAnalog(int deviceId, char axis, int stick, int virtualKeyMin, int virtualKeyMax, bool setZero) { - // The down events can repeat, so just trust the virtKeys_ array. - bool minDown = virtKeys_[virtualKeyMin - VIRTKEY_FIRST]; - bool maxDown = virtKeys_[virtualKeyMax - VIRTKEY_FIRST]; - - const float scale = virtKeys_[VIRTKEY_ANALOG_LIGHTLY - VIRTKEY_FIRST] ? g_Config.fAnalogLimiterDeadzone : 1.0f; - float value = 0.0f; - if (minDown) - value -= scale; - if (maxDown) - value += scale; - if (setZero || minDown || maxDown) { - SetPSPAxis(deviceId, axis, value, stick); - } -} - void ControlMapper::PSPKey(int deviceId, int pspKeyCode, int flags) { - SetPSPKey(deviceId, pspKeyCode, flags); -} - -void ControlMapper::SetPSPKey(int deviceId, int pspKeyCode, int flags) { + std::lock_guard guard(mutex_); if (pspKeyCode >= VIRTKEY_FIRST) { int vk = pspKeyCode - VIRTKEY_FIRST; if (flags & KEY_DOWN) { - virtKeys_[vk] = true; - onVKey(deviceId, pspKeyCode, true); + virtKeys_[vk] = 1.0f; + onVKey(pspKeyCode, true); } if (flags & KEY_UP) { - virtKeys_[vk] = false; - onVKey(deviceId, pspKeyCode, false); + virtKeys_[vk] = 0.0f; + onVKey(pspKeyCode, false); } } else { - int rotations = 0; - switch (g_Config.iInternalScreenRotation) { - case ROTATION_LOCKED_HORIZONTAL180: - rotations = 2; - break; - case ROTATION_LOCKED_VERTICAL: - rotations = 1; - break; - case ROTATION_LOCKED_VERTICAL180: - rotations = 3; - break; - } - - for (int i = 0; i < rotations; i++) { - pspKeyCode = RotatePSPKeyCode(pspKeyCode); - } - // INFO_LOG(SYSTEM, "pspKey %d %d", pspKeyCode, flags); if (flags & KEY_DOWN) - setPSPButtonState_(pspKeyCode, true); + updatePSPButtons_(pspKeyCode, 0); if (flags & KEY_UP) - setPSPButtonState_(pspKeyCode, false); + updatePSPButtons_(0, pspKeyCode); } } -void ControlMapper::onVKey(int deviceId, int vkey, bool down) { +void ControlMapper::onVKeyAnalog(int deviceId, int vkey, float value) { + // Unfortunately, for digital->analog inputs to work sanely, we need to sum up + // with the opposite value too. + int stick = 0; + int axis = 'X'; + int oppositeVKey = GetOppositeVKey(vkey); + float sign = 1.0f; switch (vkey) { - case VIRTKEY_AXIS_X_MIN: - case VIRTKEY_AXIS_X_MAX: - SetVKeyAnalog(deviceId, 'X', CTRL_STICK_LEFT, VIRTKEY_AXIS_X_MIN, VIRTKEY_AXIS_X_MAX); - break; - case VIRTKEY_AXIS_Y_MIN: - case VIRTKEY_AXIS_Y_MAX: - SetVKeyAnalog(deviceId, 'Y', CTRL_STICK_LEFT, VIRTKEY_AXIS_Y_MIN, VIRTKEY_AXIS_Y_MAX); - break; - - case VIRTKEY_AXIS_RIGHT_X_MIN: - case VIRTKEY_AXIS_RIGHT_X_MAX: - SetVKeyAnalog(deviceId, 'X', CTRL_STICK_RIGHT, VIRTKEY_AXIS_RIGHT_X_MIN, VIRTKEY_AXIS_RIGHT_X_MAX); - break; - case VIRTKEY_AXIS_RIGHT_Y_MIN: - case VIRTKEY_AXIS_RIGHT_Y_MAX: - SetVKeyAnalog(deviceId, 'Y', CTRL_STICK_RIGHT, VIRTKEY_AXIS_RIGHT_Y_MIN, VIRTKEY_AXIS_RIGHT_Y_MAX); - break; - - case VIRTKEY_ANALOG_LIGHTLY: - SetVKeyAnalog(deviceId, 'X', CTRL_STICK_LEFT, VIRTKEY_AXIS_X_MIN, VIRTKEY_AXIS_X_MAX, false); - SetVKeyAnalog(deviceId, 'Y', CTRL_STICK_LEFT, VIRTKEY_AXIS_Y_MIN, VIRTKEY_AXIS_Y_MAX, false); - SetVKeyAnalog(deviceId, 'X', CTRL_STICK_RIGHT, VIRTKEY_AXIS_RIGHT_X_MIN, VIRTKEY_AXIS_RIGHT_X_MAX, false); - SetVKeyAnalog(deviceId, 'Y', CTRL_STICK_RIGHT, VIRTKEY_AXIS_RIGHT_Y_MIN, VIRTKEY_AXIS_RIGHT_Y_MAX, false); - break; + case VIRTKEY_AXIS_X_MIN: sign = -1.0f; break; + case VIRTKEY_AXIS_X_MAX: break; + case VIRTKEY_AXIS_Y_MIN: axis = 'Y'; sign = -1.0f; break; + case VIRTKEY_AXIS_Y_MAX: axis = 'Y'; break; + case VIRTKEY_AXIS_RIGHT_X_MIN: stick = CTRL_STICK_RIGHT; sign = -1.0f; break; + case VIRTKEY_AXIS_RIGHT_X_MAX: stick = CTRL_STICK_RIGHT; break; + case VIRTKEY_AXIS_RIGHT_Y_MIN: stick = CTRL_STICK_RIGHT; axis = 'Y'; sign = -1.0f; break; + case VIRTKEY_AXIS_RIGHT_Y_MAX: stick = CTRL_STICK_RIGHT; axis = 'Y'; break; + default: + if (onVKeyAnalog_) + onVKeyAnalog_(vkey, value); + return; + } + if (oppositeVKey != 0) { + float oppVal = virtKeys_[oppositeVKey - VIRTKEY_FIRST]; + if (oppVal != 0.0f) { + value -= oppVal; + // NOTICE_LOG(SCECTRL, "Reducing %f by %f (from %08x : %s)", value, oppVal, oppositeVKey, KeyMap::GetPspButtonName(oppositeVKey).c_str()); + } + } + SetPSPAxis(deviceId, stick, axis, sign * value); +} +void ControlMapper::onVKey(int vkey, bool down) { + switch (vkey) { case VIRTKEY_ANALOG_ROTATE_CW: if (down) { autoRotatingAnalogCW_ = true; @@ -280,7 +449,6 @@ void ControlMapper::onVKey(int deviceId, int vkey, bool down) { setPSPAnalog_(0, 0.0f, 0.0f); } break; - default: if (onVKey_) onVKey_(vkey, down); @@ -288,151 +456,19 @@ void ControlMapper::onVKey(int deviceId, int vkey, bool down) { } } -void ControlMapper::ProcessAxis(const AxisInput &axis, int direction) { - // Sanity check - if (axis.axisId < 0 || axis.axisId >= JOYSTICK_AXIS_MAX) { - return; +void ControlMapper::GetDebugString(char *buffer, size_t bufSize) const { + std::stringstream str; + for (auto iter : curInput_) { + char temp[256]; + iter.first.FormatDebug(temp, sizeof(temp)); + str << temp << ": " << iter.second << std::endl; } - - const float scale = virtKeys_[VIRTKEY_ANALOG_LIGHTLY - VIRTKEY_FIRST] ? g_Config.fAnalogLimiterDeadzone : 1.0f; - - std::vector results; - KeyMap::InputMappingToPspButton(InputMapping(axis.deviceId, axis.axisId, direction), &results); - - for (int result : results) { - float value = fabs(axis.value) * scale; - switch (result) { - case VIRTKEY_AXIS_X_MIN: - SetPSPAxis(axis.deviceId, 'X', -value, CTRL_STICK_LEFT); - break; - case VIRTKEY_AXIS_X_MAX: - SetPSPAxis(axis.deviceId, 'X', value, CTRL_STICK_LEFT); - break; - case VIRTKEY_AXIS_Y_MIN: - SetPSPAxis(axis.deviceId, 'Y', -value, CTRL_STICK_LEFT); - break; - case VIRTKEY_AXIS_Y_MAX: - SetPSPAxis(axis.deviceId, 'Y', value, CTRL_STICK_LEFT); - break; - - case VIRTKEY_AXIS_RIGHT_X_MIN: - SetPSPAxis(axis.deviceId, 'X', -value, CTRL_STICK_RIGHT); - break; - case VIRTKEY_AXIS_RIGHT_X_MAX: - SetPSPAxis(axis.deviceId, 'X', value, CTRL_STICK_RIGHT); - break; - case VIRTKEY_AXIS_RIGHT_Y_MIN: - SetPSPAxis(axis.deviceId, 'Y', -value, CTRL_STICK_RIGHT); - break; - case VIRTKEY_AXIS_RIGHT_Y_MAX: - SetPSPAxis(axis.deviceId, 'Y', value, CTRL_STICK_RIGHT); - break; - - case VIRTKEY_SPEED_ANALOG: - ProcessAnalogSpeed(axis, false); - break; + for (int i = 0; i < ARRAY_SIZE(virtKeys_); i++) { + int vkId = VIRTKEY_FIRST + i; + if ((vkId >= VIRTKEY_AXIS_X_MIN && vkId <= VIRTKEY_AXIS_Y_MAX) || vkId == VIRTKEY_ANALOG_LIGHTLY || vkId == VIRTKEY_SPEED_ANALOG) { + str << KeyMap::GetPspButtonName(vkId) << ": " << virtKeys_[i] << std::endl; } } - - std::vector resultsOpposite; - KeyMap::InputMappingToPspButton(InputMapping(axis.deviceId, axis.axisId, -direction), &resultsOpposite); - - for (int result : resultsOpposite) { - if (result == VIRTKEY_SPEED_ANALOG) - ProcessAnalogSpeed(axis, true); - } - - int axisState = 0; - float threshold = axis.deviceId == DEVICE_ID_MOUSE ? AXIS_BIND_THRESHOLD_MOUSE : AXIS_BIND_THRESHOLD; - if (direction == 1 && axis.value >= threshold) { - axisState = 1; - } else if (direction == -1 && axis.value <= -threshold) { - axisState = -1; - } else { - axisState = 0; - } - - if (axisState != axisState_[axis.axisId]) { - axisState_[axis.axisId] = axisState; - if (axisState != 0) { - for (size_t i = 0; i < results.size(); i++) { - if (!IsAnalogStickKey(results[i])) - SetPSPKey(axis.deviceId, results[i], KEY_DOWN); - } - // Also unpress the other direction (unless both directions press the same key.) - for (size_t i = 0; i < resultsOpposite.size(); i++) { - if (!IsAnalogStickKey(resultsOpposite[i]) && std::find(results.begin(), results.end(), resultsOpposite[i]) == results.end()) - SetPSPKey(axis.deviceId, resultsOpposite[i], KEY_UP); - } - } else if (axisState == 0) { - // Release both directions, trying to deal with some erratic controllers that can cause it to stick. - for (size_t i = 0; i < results.size(); i++) { - if (!IsAnalogStickKey(results[i])) - SetPSPKey(axis.deviceId, results[i], KEY_UP); - } - for (size_t i = 0; i < resultsOpposite.size(); i++) { - if (!IsAnalogStickKey(resultsOpposite[i])) - SetPSPKey(axis.deviceId, resultsOpposite[i], KEY_UP); - } - } - } -} - -void ControlMapper::ProcessAnalogSpeed(const AxisInput &axis, bool opposite) { - static constexpr float DEADZONE_THRESHOLD = 0.15f; - static constexpr float DEADZONE_SCALE = 1.0f / (1.0f - DEADZONE_THRESHOLD); - - FPSLimit &limitMode = PSP_CoreParameter().fpsLimit; - // If we're using an alternate speed already, let that win. - if (limitMode != FPSLimit::NORMAL && limitMode != FPSLimit::ANALOG) - return; - // Don't even try if the limit is invalid. - if (g_Config.iAnalogFpsLimit <= 0) - return; - - AnalogFpsMode mode = (AnalogFpsMode)g_Config.iAnalogFpsMode; - float value = axis.value; - if (mode == AnalogFpsMode::AUTO) { - // TODO: Consider the pad name for better auto? KeyMap::PadName(axis.deviceId); - switch (axis.axisId) { - case JOYSTICK_AXIS_X: - case JOYSTICK_AXIS_Y: - case JOYSTICK_AXIS_Z: - case JOYSTICK_AXIS_RX: - case JOYSTICK_AXIS_RY: - case JOYSTICK_AXIS_RZ: - // These, at least on directinput, can be used for triggers that go from mapped to opposite. - mode = AnalogFpsMode::MAPPED_DIR_TO_OPPOSITE_DIR; - break; - - default: - // Other axises probably don't go from negative to positive. - mode = AnalogFpsMode::MAPPED_DIRECTION; - break; - } - } - - // Okay, now let's map it as appropriate. - if (mode == AnalogFpsMode::MAPPED_DIRECTION) { - value = fabsf(value); - // Clamp to 0 in this case if we're processing the opposite direction. - if (opposite) - value = 0.0f; - } else if (mode == AnalogFpsMode::MAPPED_DIR_TO_OPPOSITE_DIR) { - value = fabsf(value); - if (opposite) - value = -value; - value = 0.5f - value * 0.5f; - } - - // Apply a small deadzone (against the resting position.) - value = std::max(0.0f, (value - DEADZONE_THRESHOLD) * DEADZONE_SCALE); - - // If target is above 60, value is how much to speed up over 60. Otherwise, it's how much slower. - // So normalize the target. - int target = g_Config.iAnalogFpsLimit - 60; - PSP_CoreParameter().analogFpsLimit = 60 + (int)(target * value); - - // If we've reset back to normal, turn it off. - limitMode = PSP_CoreParameter().analogFpsLimit == 60 ? FPSLimit::NORMAL : FPSLimit::ANALOG; + str << "Lstick: " << converted_[0][0] << ", " << converted_[0][1] << std::endl; + truncate_cpy(buffer, bufSize, str.str().c_str()); } diff --git a/Core/ControlMapper.h b/Core/ControlMapper.h index 74783f0a7e5a..9fa2fc8e50a5 100644 --- a/Core/ControlMapper.h +++ b/Core/ControlMapper.h @@ -4,65 +4,68 @@ #include "Core/KeyMap.h" #include +#include +#include // Utilities for mapping input events to PSP inputs and virtual keys. // Main use is of course from EmuScreen.cpp, but also useful from control settings etc. - -// At some point I want to refactor this from using callbacks to simply providing lists of events. -// Still it won't be able to be completely stateless due to the 2-D processing of analog sticks. - class ControlMapper { public: void Update(); // Inputs to the table-based mapping + // These functions are free-threaded. bool Key(const KeyInput &key, bool *pauseTrigger); void Axis(const AxisInput &axis); - // Required callbacks + // Required callbacks. + // TODO: These are so many now that a virtual interface might be more appropriate.. void SetCallbacks( std::function onVKey, - std::function setAllPSPButtonStates_, - std::function setPSPButtonState, - std::function setPSPAnalog); + std::function onVKeyAnalog, + std::function updatePSPButtons, + std::function setPSPAnalog, + std::function setRawAnalog); // Inject raw PSP key input directly, such as from touch screen controls. - // Combined with the mapped input. + // Combined with the mapped input. Unlike __Ctrl APIs, this supports + // virtual key codes, though not analog mappings. void PSPKey(int deviceId, int pspKeyCode, int flags); - // Optional callback, only used in config - void SetRawCallback(std::function setRawAnalog); + void GetDebugString(char *buffer, size_t bufSize) const; private: - void ProcessAxis(const AxisInput &axis, int direction); - void SetVKeyAnalog(int deviceId, char axis, int stick, int virtualKeyMin, int virtualKeyMax, bool setZero = true); + bool UpdatePSPState(const InputMapping &changedMapping); + float MapAxisValue(float value, int vkId, const InputMapping &mapping, const InputMapping &changedMapping, bool *oppositeTouched); - void SetPSPKey(int deviceId, int pspKeyCode, int flags); - void SetPSPAxis(int deviceId, char axis, float value, int stick); - void ProcessAnalogSpeed(const AxisInput &axis, bool opposite); + void SetPSPAxis(int deviceId, int stick, char axis, float value); - void onVKey(int deviceId, int vkey, bool down); + void onVKey(int vkey, bool down); + void onVKeyAnalog(int deviceId, int vkey, float value); // To track mappable virtual keys. We can have as many as we want. - bool virtKeys_[VIRTKEY_COUNT]{}; - - // De-noise mapped axis updates - int axisState_[JOYSTICK_AXIS_MAX]{}; + float virtKeys_[VIRTKEY_COUNT]{}; int lastNonDeadzoneDeviceID_[2]{}; float history_[2][2]{}; + float converted_[2][2]{}; // for debug display // Mappable auto-rotation. Useful for keyboard/dpad->analog in a few games. bool autoRotatingAnalogCW_ = false; bool autoRotatingAnalogCCW_ = false; + // Protects basically all the state. + std::mutex mutex_; + + std::map curInput_; + // Callbacks std::function onVKey_; - std::function setAllPSPButtonStates_; - std::function setPSPButtonState_; + std::function onVKeyAnalog_; + std::function updatePSPButtons_; std::function setPSPAnalog_; std::function setRawAnalog_; }; -void ConvertAnalogStick(float &x, float &y); +void ConvertAnalogStick(float x, float y, float *outX, float *outY); diff --git a/Core/Debugger/WebSocket/InputSubscriber.cpp b/Core/Debugger/WebSocket/InputSubscriber.cpp index fd0f478e76f1..1ef9260686ee 100644 --- a/Core/Debugger/WebSocket/InputSubscriber.cpp +++ b/Core/Debugger/WebSocket/InputSubscriber.cpp @@ -155,12 +155,7 @@ void WebSocketInputState::ButtonsSend(DebuggerRequest &req) { } } - if (downFlags) { - __CtrlButtonDown(downFlags); - } - if (upFlags) { - __CtrlButtonUp(upFlags); - } + __CtrlUpdateButtons(downFlags, upFlags); req.Respond(); } @@ -192,7 +187,7 @@ void WebSocketInputState::ButtonsPress(DebuggerRequest &req) { } press.button = info->second; - __CtrlButtonDown(press.button); + __CtrlUpdateButtons(press.button, 0); pressTickets_.push_back(press); } @@ -205,7 +200,7 @@ void WebSocketInputState::Broadcast(net::WebSocketServer *ws) { for (PressInfo &press : pressTickets_) { press.duration--; if (press.duration == -1) { - __CtrlButtonUp(press.button); + __CtrlUpdateButtons(0, press.button); ws->Send(press.Event()); } } @@ -254,6 +249,7 @@ void WebSocketInputState::AnalogSend(DebuggerRequest &req) { if (!AnalogValue(req, &x, "x") || !AnalogValue(req, &y, "y")) return; + // TODO: Route into the control mapper's PSPKey function or similar instead. __CtrlSetAnalogXY(stick == "left" ? CTRL_STICK_LEFT : CTRL_STICK_RIGHT, x, y); req.Respond(); diff --git a/Core/HLE/sceCtrl.cpp b/Core/HLE/sceCtrl.cpp index 6e8e160da5ad..e2d60a723c09 100644 --- a/Core/HLE/sceCtrl.cpp +++ b/Core/HLE/sceCtrl.cpp @@ -189,22 +189,7 @@ u32 __CtrlReadLatch() return ret; } -// Functions so that the rest of the emulator can control what the sceCtrl interface should return -// to the game: - -void __CtrlButtonDown(u32 buttonBit) -{ - std::lock_guard guard(ctrlMutex); - ctrlCurrent.buttons |= buttonBit; -} - -void __CtrlButtonUp(u32 buttonBit) -{ - std::lock_guard guard(ctrlMutex); - ctrlCurrent.buttons &= ~buttonBit; -} - -void __CtrlSetAllButtons(u32 bitsToSet, u32 bitsToClear) +void __CtrlUpdateButtons(u32 bitsToSet, u32 bitsToClear) { std::lock_guard guard(ctrlMutex); ctrlCurrent.buttons &= ~(bitsToClear & CTRL_MASK_USER); diff --git a/Core/HLE/sceCtrl.h b/Core/HLE/sceCtrl.h index 8307247e1730..02feaceb433c 100644 --- a/Core/HLE/sceCtrl.h +++ b/Core/HLE/sceCtrl.h @@ -64,14 +64,8 @@ void __CtrlInit(); void __CtrlDoState(PointerWrap &p); void __CtrlShutdown(); -// Call this whenever a button is pressed, using the above CTRL_ constants. -// Multiple buttons may be sent in one call OR'd together. -// Resending a currently pressed button is fine but not required. -void __CtrlButtonDown(u32 buttonBit); -// Call this whenever a button is released. Similar to __CtrlButtonDown(). -void __CtrlButtonUp(u32 buttonBit); -// To be used by the new mapping code. -void __CtrlSetAllButtons(u32 bitsToSet, u32 bitsToClear); +// Clears and sets selected buttons. NOTE: Clearing happens first. +void __CtrlUpdateButtons(u32 bitsToSet, u32 bitsToClear); // Call this to set the position of an analog stick, ideally when it changes. // X and Y values should be from -1 to 1, inclusive, in a square (no need to force to a circle.) diff --git a/Core/KeyMap.cpp b/Core/KeyMap.cpp index 21cc22554e81..52f907ed8e8d 100644 --- a/Core/KeyMap.cpp +++ b/Core/KeyMap.cpp @@ -43,7 +43,7 @@ std::set g_seenPads; std::map g_padNames; std::set g_seenDeviceIds; -bool g_swapped_keys = false; +bool g_swapDpadWithLStick = false; // TODO: This is such a mess... void UpdateNativeMenuKeys() { @@ -424,9 +424,10 @@ const KeyMap_IntStrPair psp_button_names[] = { }; static std::string FindName(int key, const KeyMap_IntStrPair list[], size_t size) { - for (size_t i = 0; i < size; i++) + for (size_t i = 0; i < size; i++) { if (list[i].key == key) return list[i].name; + } return StringFromFormat("%02x?", key); } @@ -472,8 +473,8 @@ std::vector GetMappableKeys() { return temp; } -int CheckAxisSwap(int btn) { - if (g_swapped_keys) { +inline int CheckAxisSwap(int btn) { + if (g_swapDpadWithLStick) { switch (btn) { case CTRL_UP: btn = VIRTKEY_AXIS_Y_MAX; break; case VIRTKEY_AXIS_Y_MAX: btn = CTRL_UP; break; @@ -488,19 +489,18 @@ int CheckAxisSwap(int btn) { return btn; } -static bool FindKeyMapping(const InputMapping &mapping, std::vector *pspButtons) { +bool InputMappingToPspButton(const InputMapping &mapping, std::vector *pspButtons) { + bool found = false; for (auto iter = g_controllerMap.begin(); iter != g_controllerMap.end(); ++iter) { for (auto iter2 = iter->second.begin(); iter2 != iter->second.end(); ++iter2) { if (*iter2 == mapping) { - pspButtons->push_back(CheckAxisSwap(iter->first)); + if (pspButtons) + pspButtons->push_back(CheckAxisSwap(iter->first)); + found = true; } } } - return pspButtons->size() > 0; -} - -bool InputMappingToPspButton(const InputMapping &mapping, std::vector *pspButtons) { - return FindKeyMapping(mapping, pspButtons); + return found; } bool InputMappingsFromPspButton(int btn, std::vector *mappings, bool ignoreMouse) { @@ -774,7 +774,7 @@ std::string PadName(int deviceId) { // Swap direction buttons and left analog axis void SwapAxis() { - g_swapped_keys = !g_swapped_keys; + g_swapDpadWithLStick = !g_swapDpadWithLStick; } bool HasChanged(int &prevGeneration) { @@ -785,4 +785,53 @@ bool HasChanged(int &prevGeneration) { return false; } +static const char *g_vKeyNames[] = { + "AXIS_X_MIN", + "AXIS_Y_MIN", + "AXIS_X_MAX", + "AXIS_Y_MAX", + "RAPID_FIRE", + "FASTFORWARD", + "PAUSE", + "SPEED_TOGGLE", + "AXIS_RIGHT_X_MIN", + "AXIS_RIGHT_Y_MIN", + "AXIS_RIGHT_X_MAX", + "AXIS_RIGHT_Y_MAX", + "REWIND", + "SAVE_STATE", + "LOAD_STATE", + "NEXT_SLOT", + "TOGGLE_FULLSCREEN", + "ANALOG_LIGHTLY", + "AXIS_SWAP", + "DEVMENU", + "FRAME_ADVANCE", + "RECORD", + "SPEED_CUSTOM1", + "SPEED_CUSTOM2", + "TEXTURE_DUMP", + "TEXTURE_REPLACE", + "SCREENSHOT", + "MUTE_TOGGLE", + "OPENCHAT", + "ANALOG_ROTATE_CW", + "ANALOG_ROTATE_CCW", + "SCREEN_ROTATION_VERTICAL", + "SCREEN_ROTATION_VERTICAL180", + "SCREEN_ROTATION_HORIZONTAL", + "SCREEN_ROTATION_HORIZONTAL180", + "SPEED_ANALOG", + "VR_CAMERA_ADJUST", + "VR_CAMERA_RESET", +}; + +const char *GetVirtKeyName(int vkey) { + int index = vkey - VIRTKEY_FIRST; + if (index < 0 || index >= ARRAY_SIZE(g_vKeyNames)) { + return "N/A"; + } + return g_vKeyNames[index]; +} + } // KeyMap diff --git a/Core/KeyMap.h b/Core/KeyMap.h index 3d41e86c29ef..113117173b5b 100644 --- a/Core/KeyMap.h +++ b/Core/KeyMap.h @@ -29,6 +29,7 @@ #define KEYMAP_ERROR_KEY_ALREADY_USED -1 #define KEYMAP_ERROR_UNKNOWN_KEY 0 +// Don't change any of these - it'll break backwards compatibility with configs. enum { VIRTKEY_FIRST = 0x40000001, VIRTKEY_AXIS_X_MIN = 0x40000001, @@ -118,7 +119,8 @@ namespace KeyMap { std::string GetKeyOrAxisName(const InputMapping &mapping); std::string GetAxisName(int axisId); std::string GetPspButtonName(int btn); - const char* GetPspButtonNameCharPointer(int btn); + const char *GetVirtKeyName(int vkey); + const char *GetPspButtonNameCharPointer(int btn); std::vector GetMappableKeys(); diff --git a/Core/TiltEventProcessor.cpp b/Core/TiltEventProcessor.cpp index 5d142955a836..f505f23397bd 100644 --- a/Core/TiltEventProcessor.cpp +++ b/Core/TiltEventProcessor.cpp @@ -129,6 +129,8 @@ inline float clamp(float f) { return f; } +// TODO: Instead of __Ctrl, route data into the ControlMapper. + void GenerateAnalogStickEvent(float tiltX, float tiltY) { __CtrlSetAnalogXY(CTRL_STICK_LEFT, clamp(tiltX), clamp(tiltY)); } @@ -137,12 +139,12 @@ void GenerateDPadEvent(int digitalX, int digitalY) { static const int dir[4] = { CTRL_RIGHT, CTRL_DOWN, CTRL_LEFT, CTRL_UP }; if (digitalX == 0) { - __CtrlButtonUp(tiltButtonsDown & (CTRL_RIGHT | CTRL_LEFT)); + __CtrlUpdateButtons(tiltButtonsDown & (CTRL_RIGHT | CTRL_LEFT), 0); tiltButtonsDown &= ~(CTRL_LEFT | CTRL_RIGHT); } if (digitalY == 0) { - __CtrlButtonUp(tiltButtonsDown & (CTRL_UP | CTRL_DOWN)); + __CtrlUpdateButtons(0, tiltButtonsDown & (CTRL_UP | CTRL_DOWN)); tiltButtonsDown &= ~(CTRL_UP | CTRL_DOWN); } @@ -157,7 +159,7 @@ void GenerateDPadEvent(int digitalX, int digitalY) { if (digitalY == 1) ctrlMask |= CTRL_UP; ctrlMask &= ~__CtrlPeekButtons(); - __CtrlButtonDown(ctrlMask); + __CtrlUpdateButtons(ctrlMask, 0); tiltButtonsDown |= ctrlMask; } @@ -165,12 +167,12 @@ void GenerateActionButtonEvent(int digitalX, int digitalY) { static const int buttons[4] = { CTRL_CIRCLE, CTRL_CROSS, CTRL_SQUARE, CTRL_TRIANGLE }; if (digitalX == 0) { - __CtrlButtonUp(tiltButtonsDown & (CTRL_SQUARE | CTRL_CIRCLE)); + __CtrlUpdateButtons(0, tiltButtonsDown & (CTRL_SQUARE | CTRL_CIRCLE)); tiltButtonsDown &= ~(CTRL_SQUARE | CTRL_CIRCLE); } if (digitalY == 0) { - __CtrlButtonUp(tiltButtonsDown & (CTRL_TRIANGLE | CTRL_CROSS)); + __CtrlUpdateButtons(0, tiltButtonsDown & (CTRL_TRIANGLE | CTRL_CROSS)); tiltButtonsDown &= ~(CTRL_TRIANGLE | CTRL_CROSS); } @@ -185,7 +187,7 @@ void GenerateActionButtonEvent(int digitalX, int digitalY) { if (digitalY == 1) ctrlMask |= CTRL_TRIANGLE; ctrlMask &= ~__CtrlPeekButtons(); - __CtrlButtonDown(ctrlMask); + __CtrlUpdateButtons(ctrlMask, 0); tiltButtonsDown |= ctrlMask; } @@ -206,14 +208,13 @@ void GenerateTriggerButtonEvent(int digitalX, int digitalY) { } downButtons &= ~__CtrlPeekButtons(); - __CtrlButtonUp(tiltButtonsDown & upButtons); - __CtrlButtonDown(downButtons); + __CtrlUpdateButtons(downButtons, tiltButtonsDown & upButtons); tiltButtonsDown = (tiltButtonsDown & ~upButtons) | downButtons; } void ResetTiltEvents() { // Reset the buttons we have marked pressed. - __CtrlButtonUp(tiltButtonsDown); + __CtrlUpdateButtons(0, tiltButtonsDown); tiltButtonsDown = 0; __CtrlSetAnalogXY(CTRL_STICK_LEFT, 0.0f, 0.0f); } diff --git a/Qt/QtMain.cpp b/Qt/QtMain.cpp index 411f26e1fdb0..17edeefb741f 100644 --- a/Qt/QtMain.cpp +++ b/Qt/QtMain.cpp @@ -725,7 +725,6 @@ void MainUI::updateAccelerometer() { if (reading) { AxisInput axis; axis.deviceId = DEVICE_ID_ACCELEROMETER; - axis.flags = 0; axis.axisId = JOYSTICK_AXIS_ACCELEROMETER_X; axis.value = reading->x(); diff --git a/SDL/SDLJoystick.cpp b/SDL/SDLJoystick.cpp index 59906f4a3e75..2f6b47171d0e 100644 --- a/SDL/SDLJoystick.cpp +++ b/SDL/SDLJoystick.cpp @@ -187,7 +187,6 @@ void SDLJoystick::ProcessInput(SDL_Event &event){ if (axis.value > 1.0f) axis.value = 1.0f; if (axis.value < -1.0f) axis.value = -1.0f; axis.deviceId = DEVICE_ID_PAD_0 + getDeviceIndex(event.caxis.which); - axis.flags = 0; NativeAxis(axis); break; case SDL_CONTROLLERDEVICEREMOVED: diff --git a/UI/ControlMappingScreen.cpp b/UI/ControlMappingScreen.cpp index 1c4ee31635bd..7d267c2b6018 100644 --- a/UI/ControlMappingScreen.cpp +++ b/UI/ControlMappingScreen.cpp @@ -450,16 +450,16 @@ void KeyMappingNewMouseKeyDialog::axis(const AxisInput &axis) { AnalogSetupScreen::AnalogSetupScreen(const Path &gamePath) : UIDialogScreenWithGameBackground(gamePath) { mapper_.SetCallbacks( [](int vkey, bool down) {}, + [](int vkey, float analogValue) {}, [&](uint32_t bitsToSet, uint32_t bitsToClear) {}, - [&](int button, bool down) {}, [&](int stick, float x, float y) { - analogX_[stick] = x; - analogY_[stick] = y; - }); - mapper_.SetRawCallback([&](int stick, float x, float y) { - rawX_[stick] = x; - rawY_[stick] = y; - }); + analogX_[stick] = x; + analogY_[stick] = y; + }, + [&](int stick, float x, float y) { + rawX_[stick] = x; + rawY_[stick] = y; + }); } void AnalogSetupScreen::update() { diff --git a/UI/DevScreens.cpp b/UI/DevScreens.cpp index 170cb994148b..16c562187cd4 100644 --- a/UI/DevScreens.cpp +++ b/UI/DevScreens.cpp @@ -107,7 +107,14 @@ void DevMenuScreen::CreatePopupContents(UI::ViewGroup *parent) { items->Add(new Choice(dev->T("Toggle Freeze")))->OnClick.Handle(this, &DevMenuScreen::OnFreezeFrame); items->Add(new Choice(dev->T("Dump next frame to log")))->OnClick.Handle(this, &DevMenuScreen::OnDumpFrame); - items->Add(new Choice(dev->T("Toggle Audio Debug")))->OnClick.Handle(this, &DevMenuScreen::OnToggleAudioDebug); + items->Add(new Choice(dev->T("Toggle Audio Debug")))->OnClick.Add([](UI::EventParams &) { + g_Config.bShowAudioDebug = !g_Config.bShowAudioDebug; + return UI::EVENT_DONE; + }); + items->Add(new Choice(dev->T("Toggle Control Debug")))->OnClick.Add([](UI::EventParams &) { + g_Config.bShowControlDebug = !g_Config.bShowControlDebug; + return UI::EVENT_DONE; + }); #ifdef USE_PROFILER items->Add(new CheckBox(&g_Config.bShowFrameProfiler, dev->T("Frame Profiler"), "")); #endif @@ -123,11 +130,6 @@ void DevMenuScreen::CreatePopupContents(UI::ViewGroup *parent) { } } -UI::EventReturn DevMenuScreen::OnToggleAudioDebug(UI::EventParams &e) { - g_Config.bShowAudioDebug = !g_Config.bShowAudioDebug; - return UI::EVENT_DONE; -} - UI::EventReturn DevMenuScreen::OnResetLimitedLogging(UI::EventParams &e) { Reporting::ResetCounts(); return UI::EVENT_DONE; diff --git a/UI/DevScreens.h b/UI/DevScreens.h index 0a871b56fcc6..a582d7951af0 100644 --- a/UI/DevScreens.h +++ b/UI/DevScreens.h @@ -45,7 +45,6 @@ class DevMenuScreen : public PopupScreen { UI::EventReturn OnFreezeFrame(UI::EventParams &e); UI::EventReturn OnDumpFrame(UI::EventParams &e); UI::EventReturn OnDeveloperTools(UI::EventParams &e); - UI::EventReturn OnToggleAudioDebug(UI::EventParams &e); UI::EventReturn OnResetLimitedLogging(UI::EventParams &e); private: diff --git a/UI/EmuScreen.cpp b/UI/EmuScreen.cpp index af665bdd6cb2..22bf7bf76827 100644 --- a/UI/EmuScreen.cpp +++ b/UI/EmuScreen.cpp @@ -177,17 +177,12 @@ EmuScreen::EmuScreen(const Path &filename) startDumping = false; controlMapper_.SetCallbacks( std::bind(&EmuScreen::onVKey, this, _1, _2), + std::bind(&EmuScreen::onVKeyAnalog, this, _1, _2), [](uint32_t bitsToSet, uint32_t bitsToClear) { - __CtrlSetAllButtons(bitsToSet, bitsToClear); + __CtrlUpdateButtons(bitsToSet, bitsToClear); }, - [](int pspButton, bool down) { - if (down) { - __CtrlButtonDown(pspButton); - } else { - __CtrlButtonUp(pspButton); - } - }, - &SetPSPAnalog); + &SetPSPAnalog, + nullptr); // Make sure we don't leave it at powerdown after the last game. // TODO: This really should be handled elsewhere if it isn't. @@ -762,6 +757,38 @@ void EmuScreen::onVKey(int virtualKeyCode, bool down) { } } +void EmuScreen::onVKeyAnalog(int virtualKeyCode, float value) { + if (virtualKeyCode != VIRTKEY_SPEED_ANALOG) { + return; + } + + // We only handle VIRTKEY_SPEED_ANALOG here. + + // Xbox controllers need a pretty big deadzone here to not leave behind small values + // on occasion when releasing the trigger. Still feels right. + static constexpr float DEADZONE_THRESHOLD = 0.20f; + static constexpr float DEADZONE_SCALE = 1.0f / (1.0f - DEADZONE_THRESHOLD); + + FPSLimit &limitMode = PSP_CoreParameter().fpsLimit; + // If we're using an alternate speed already, let that win. + if (limitMode != FPSLimit::NORMAL && limitMode != FPSLimit::ANALOG) + return; + // Don't even try if the limit is invalid. + if (g_Config.iAnalogFpsLimit <= 0) + return; + + // Apply a small deadzone (against the resting position.) + value = std::max(0.0f, (value - DEADZONE_THRESHOLD) * DEADZONE_SCALE); + + // If target is above 60, value is how much to speed up over 60. Otherwise, it's how much slower. + // So normalize the target. + int target = g_Config.iAnalogFpsLimit - 60; + PSP_CoreParameter().analogFpsLimit = 60 + (int)(target * value); + + // If we've reset back to normal, turn it off. + limitMode = PSP_CoreParameter().analogFpsLimit == 60 ? FPSLimit::NORMAL : FPSLimit::ANALOG; +} + bool EmuScreen::key(const KeyInput &key) { Core_NotifyActivity(); @@ -1292,6 +1319,7 @@ Invalid / Unknown (%d) static void DrawAudioDebugStats(UIContext *ctx, const Bounds &bounds) { FontID ubuntu24("UBUNTU24"); + char statbuf[4096] = { 0 }; System_AudioGetDebugStats(statbuf, sizeof(statbuf)); @@ -1305,6 +1333,22 @@ static void DrawAudioDebugStats(UIContext *ctx, const Bounds &bounds) { ctx->RebindTexture(); } +static void DrawControlDebug(UIContext *ctx, const ControlMapper &mapper, const Bounds &bounds) { + FontID ubuntu24("UBUNTU24"); + + char statbuf[4096] = { 0 }; + mapper.GetDebugString(statbuf, sizeof(statbuf)); + + ctx->Flush(); + ctx->BindFontTexture(); + ctx->Draw()->SetFontScale(0.5f, 0.5f); + ctx->Draw()->DrawTextRect(ubuntu24, statbuf, bounds.x + 11, bounds.y + 31, bounds.w - 20, bounds.h - 30, 0xc0000000, FLAG_DYNAMIC_ASCII); + ctx->Draw()->DrawTextRect(ubuntu24, statbuf, bounds.x + 10, bounds.y + 30, bounds.w - 20, bounds.h - 30, 0xFFFFFFFF, FLAG_DYNAMIC_ASCII); + ctx->Draw()->SetFontScale(1.0f, 1.0f); + ctx->Flush(); + ctx->RebindTexture(); +} + static void DrawFPS(UIContext *ctx, const Bounds &bounds) { FontID ubuntu24("UBUNTU24"); float vps, fps, actual_fps; @@ -1517,7 +1561,7 @@ bool EmuScreen::hasVisibleUI() { if (g_Config.bEnableCardboardVR || g_Config.bEnableNetworkChat) return true; // Debug UI. - if (g_Config.bShowDebugStats || g_Config.bShowDeveloperMenu || g_Config.bShowAudioDebug || g_Config.bShowFrameProfiler) + if (g_Config.bShowDebugStats || g_Config.bShowDeveloperMenu || g_Config.bShowAudioDebug || g_Config.bShowFrameProfiler || g_Config.bShowControlDebug) return true; // Exception information. @@ -1551,20 +1595,26 @@ void EmuScreen::renderUI() { root_->Draw(*ctx); } - if (g_Config.bShowDebugStats && !invalid_) { - DrawDebugStats(ctx, ctx->GetLayoutBounds()); - } + if (!invalid_) { + if (g_Config.bShowDebugStats) { + DrawDebugStats(ctx, ctx->GetLayoutBounds()); + } - if (g_Config.bShowAudioDebug && !invalid_) { - DrawAudioDebugStats(ctx, ctx->GetLayoutBounds()); - } + if (g_Config.bShowAudioDebug) { + DrawAudioDebugStats(ctx, ctx->GetLayoutBounds()); + } - if (g_Config.iShowStatusFlags && !invalid_) { - DrawFPS(ctx, ctx->GetLayoutBounds()); - } + if (g_Config.iShowStatusFlags) { + DrawFPS(ctx, ctx->GetLayoutBounds()); + } + + if (g_Config.bDrawFrameGraph) { + DrawFrameTimes(ctx, ctx->GetLayoutBounds()); + } - if (g_Config.bDrawFrameGraph && !invalid_) { - DrawFrameTimes(ctx, ctx->GetLayoutBounds()); + if (g_Config.bShowControlDebug) { + DrawControlDebug(ctx, controlMapper_, ctx->GetLayoutBounds()); + } } #if !PPSSPP_PLATFORM(UWP) && !PPSSPP_PLATFORM(SWITCH) diff --git a/UI/EmuScreen.h b/UI/EmuScreen.h index ebb7800b4c7e..a7ee1c23afec 100644 --- a/UI/EmuScreen.h +++ b/UI/EmuScreen.h @@ -69,6 +69,7 @@ class EmuScreen : public UIScreen { void renderUI(); void onVKey(int virtualKeyCode, bool down); + void onVKeyAnalog(int virtualKeyCode, float value); void autoLoad(); void checkPowerDown(); diff --git a/UI/GamepadEmu.cpp b/UI/GamepadEmu.cpp index 4e6e45e561ee..b26e00400b46 100644 --- a/UI/GamepadEmu.cpp +++ b/UI/GamepadEmu.cpp @@ -182,9 +182,9 @@ bool PSPButton::Touch(const TouchInput &input) { if (g_Config.bHapticFeedback) { System_Vibrate(HAPTIC_VIRTUAL_KEY); } - __CtrlButtonDown(pspButtonBit_); + __CtrlUpdateButtons(pspButtonBit_, 0); } else if (lastDown && !down) { - __CtrlButtonUp(pspButtonBit_); + __CtrlUpdateButtons(0, pspButtonBit_); } return retval; } @@ -361,10 +361,10 @@ void PSPDpad::ProcessTouch(float x, float y, bool down) { if (g_Config.bHapticFeedback) { System_Vibrate(HAPTIC_VIRTUAL_KEY); } - __CtrlButtonDown(dir[i]); + __CtrlUpdateButtons(dir[i], 0); } if (released & dir[i]) { - __CtrlButtonUp(dir[i]); + __CtrlUpdateButtons(0, dir[i]); } } } @@ -652,10 +652,9 @@ void PSPCustomStick::ProcessTouch(float x, float y, bool down) { posY_ = 0.0f; } - if (release != 0) - __CtrlButtonUp(release); - if (press != 0) - __CtrlButtonDown(press); + if (release || press) { + __CtrlUpdateButtons(press, release); + } } void InitPadLayout(float xres, float yres, float globalScale) { diff --git a/UI/JoystickHistoryView.cpp b/UI/JoystickHistoryView.cpp index 3fdd895ac504..3aa1828f9c6b 100644 --- a/UI/JoystickHistoryView.cpp +++ b/UI/JoystickHistoryView.cpp @@ -34,8 +34,8 @@ void JoystickHistoryView::Draw(UIContext &dc) { float by = (iy + 1) * dx; if (type_ == StickHistoryViewType::OUTPUT) { - ConvertAnalogStick(ax, ay); - ConvertAnalogStick(bx, by); + ConvertAnalogStick(ax, ay, &ax, &ay); + ConvertAnalogStick(bx, by, &bx, &by); } ax = ax * minRadius + bounds_.centerX(); @@ -58,8 +58,8 @@ void JoystickHistoryView::Draw(UIContext &dc) { float by = fy; if (type_ == StickHistoryViewType::OUTPUT) { - ConvertAnalogStick(ax, ay); - ConvertAnalogStick(bx, by); + ConvertAnalogStick(ax, ay, &ax, &ay); + ConvertAnalogStick(bx, by, &bx, &by); } ax = ax * minRadius + bounds_.centerX(); diff --git a/android/jni/app-android.cpp b/android/jni/app-android.cpp index 8dfdf662b9d2..78e7a100bc88 100644 --- a/android/jni/app-android.cpp +++ b/android/jni/app-android.cpp @@ -1221,7 +1221,6 @@ extern "C" void JNICALL Java_org_ppsspp_ppsspp_NativeApp_accelerometer(JNIEnv *, AxisInput axis; axis.deviceId = DEVICE_ID_ACCELEROMETER; - axis.flags = 0; axis.axisId = JOYSTICK_AXIS_ACCELEROMETER_X; axis.value = x; diff --git a/ios/ViewController.mm b/ios/ViewController.mm index 4535b39a8095..359eda075b40 100644 --- a/ios/ViewController.mm +++ b/ios/ViewController.mm @@ -460,7 +460,6 @@ - (void)buttonDown:(iCadeState)button break; } axis.deviceId = DEVICE_ID_PAD_0; - axis.flags = 0; NativeAxis(axis); } else { KeyInput key; @@ -530,7 +529,6 @@ - (void)buttonUp:(iCadeState)button break; } axis.deviceId = DEVICE_ID_PAD_0; - axis.flags = 0; NativeAxis(axis); } else { KeyInput key; @@ -687,7 +685,6 @@ - (void)setupController:(GCController *)controller extendedProfile.leftThumbstick.xAxis.valueChangedHandler = ^(GCControllerAxisInput *axis, float value) { AxisInput axisInput; axisInput.deviceId = DEVICE_ID_PAD_0; - axisInput.flags = 0; axisInput.axisId = JOYSTICK_AXIS_X; axisInput.value = value; NativeAxis(axisInput); @@ -696,7 +693,6 @@ - (void)setupController:(GCController *)controller extendedProfile.leftThumbstick.yAxis.valueChangedHandler = ^(GCControllerAxisInput *axis, float value) { AxisInput axisInput; axisInput.deviceId = DEVICE_ID_PAD_0; - axisInput.flags = 0; axisInput.axisId = JOYSTICK_AXIS_Y; axisInput.value = -value; NativeAxis(axisInput); @@ -706,7 +702,6 @@ - (void)setupController:(GCController *)controller extendedProfile.rightThumbstick.xAxis.valueChangedHandler = ^(GCControllerAxisInput *axis, float value) { AxisInput axisInput; axisInput.deviceId = DEVICE_ID_PAD_0; - axisInput.flags = 0; axisInput.axisId = JOYSTICK_AXIS_Z; axisInput.value = value; NativeAxis(axisInput); @@ -715,7 +710,6 @@ - (void)setupController:(GCController *)controller extendedProfile.rightThumbstick.yAxis.valueChangedHandler = ^(GCControllerAxisInput *axis, float value) { AxisInput axisInput; axisInput.deviceId = DEVICE_ID_PAD_0; - axisInput.flags = 0; axisInput.axisId = JOYSTICK_AXIS_RZ; axisInput.value = -value; NativeAxis(axisInput); diff --git a/libretro/libretro.cpp b/libretro/libretro.cpp index 2038e43bd53c..ce7a13b09142 100644 --- a/libretro/libretro.cpp +++ b/libretro/libretro.cpp @@ -1523,11 +1523,11 @@ static void retro_input(void) if (pressed) { - __CtrlButtonDown(map[i].sceCtrl); + __CtrlUpdateButtons(map[i].sceCtrl, 0); } else { - __CtrlButtonUp(map[i].sceCtrl); + __CtrlUpdateButtons(0, map[i].sceCtrl); } } diff --git a/pspautotests b/pspautotests index 46065027500c..2e02c4a7c075 160000 --- a/pspautotests +++ b/pspautotests @@ -1 +1 @@ -Subproject commit 46065027500cd781ce7c15c051c8b0c4751ad1fa +Subproject commit 2e02c4a7c075f1a7cf28ec1add000ecd7077cd09