Make reverse mapping lookup work for the simple PSP buttons.

This commit is contained in:
Henrik Rydgård 2023-03-29 10:21:49 +02:00
parent 2814668cf5
commit 38f4cc4cc9
12 changed files with 129 additions and 64 deletions

View file

@ -183,7 +183,6 @@ struct AxisInput {
int deviceId; int deviceId;
int axisId; // Android axis Ids are the canonical ones. int axisId; // Android axis Ids are the canonical ones.
float value; float value;
int flags;
}; };
// Is there a nicer place for this stuff? It's here to avoid dozens of linking errors in UnitTest.. // Is there a nicer place for this stuff? It's here to avoid dozens of linking errors in UnitTest..

View file

@ -9,6 +9,9 @@
#include "Common/VR/VRMath.h" #include "Common/VR/VRMath.h"
#include "Common/VR/VRRenderer.h" #include "Common/VR/VRRenderer.h"
#include "Common/Input/InputState.h"
#include "Common/Input/KeyCodes.h"
#include "Common/GPU/Vulkan/VulkanContext.h" #include "Common/GPU/Vulkan/VulkanContext.h"
#include "Common/Math/lin/matrix4x4.h" #include "Common/Math/lin/matrix4x4.h"

View file

@ -20,6 +20,11 @@ static float MapAxisValue(float v) {
return sign * Clamp(invDeadzone + (abs(v) - deadzone) / (1.0f - deadzone) * (sensitivity - invDeadzone), 0.0f, 1.0f); return sign * Clamp(invDeadzone + (abs(v) - deadzone) / (1.0f - deadzone) * (sensitivity - invDeadzone), 0.0f, 1.0f);
} }
// TODO: Possibly make these configurable?
float GetDeviceAxisThreshold(int device) {
return device == DEVICE_ID_MOUSE ? AXIS_BIND_THRESHOLD_MOUSE : AXIS_BIND_THRESHOLD;
}
void ConvertAnalogStick(float &x, float &y) { void ConvertAnalogStick(float &x, float &y) {
const bool isCircular = g_Config.bAnalogIsCircular; const bool isCircular = g_Config.bAnalogIsCircular;
@ -97,21 +102,89 @@ void ControlMapper::SetPSPAxis(int device, char axis, float value, int stick) {
} }
} }
bool ControlMapper::Key(const KeyInput &key, bool *pauseTrigger) { static int RotatePSPKeyCode(int x) {
std::vector<int> pspKeys; switch (x) {
KeyMap::InputMappingToPspButton(InputMapping(key.deviceId, key.keyCode), &pspKeys); 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;
}
}
if (pspKeys.size() && (key.flags & KEY_IS_REPEAT)) { bool ControlMapper::UpdatePSPState() {
// 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.
// For the PSP's button inputs, we just go through and put the flags together.
uint32_t buttonMask = 0;
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 < 32; i++) {
uint32_t mask = 1 << i;
if (!(mask & CTRL_MASK_USER)) {
// Not a mappable button bit
continue;
}
uint32_t mapping = mask;
for (int i = 0; i < rotations; i++) {
mapping = RotatePSPKeyCode(mapping);
}
std::vector<InputMapping> inputMappings;
// This is really "MappingsFromPspButtons".
if (!KeyMap::InputMappingsFromPspButton(mapping, &inputMappings, false))
continue;
for (int j = 0; j < inputMappings.size(); j++) {
auto iter = curInput_.find(inputMappings[j]) ;
if (iter != curInput_.end() && iter->second > 0.0f) {
buttonMask |= mask;
}
}
}
setAllPSPButtonStates_(buttonMask);
// OK, handle all the virtual keys next. For these we need to do deltas here and send events.
// Now let's look at the four axes.
for (int i = 0; i < 4; i++) {
}
// TODO: Here we need to diff pspState with prevPspState_ to generate
// any new PSP key events. Though for the actual PSP buttons themselves (not the sticks),
// we could just or them together and set them all at once.
return true;
}
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. // Claim that we handled this. Prevents volume key repeats from popping up the volume control on Android.
return true; return true;
} }
for (size_t i = 0; i < pspKeys.size(); i++) { InputMapping mapping(key.deviceId, key.keyCode);
SetPSPKey(key.deviceId, pspKeys[i], key.flags);
if (key.flags & KEY_DOWN) {
curInput_[mapping] = 1.0f;
} else if (key.flags & KEY_UP) {
curInput_.erase(mapping);
} }
std::vector<int> pspKeys;
KeyMap::InputMappingToPspButton(InputMapping(key.deviceId, key.keyCode), &pspKeys);
DEBUG_LOG(SYSTEM, "Key: %d DeviceId: %d", key.keyCode, key.deviceId); DEBUG_LOG(SYSTEM, "Key: %d DeviceId: %d", key.keyCode, key.deviceId);
if (!pspKeys.size() || key.deviceId == DEVICE_ID_DEFAULT) { if (!pspKeys.size() || key.deviceId == DEVICE_ID_DEFAULT) {
if ((key.flags & KEY_DOWN) && key.keyCode == NKCODE_BACK) { if ((key.flags & KEY_DOWN) && key.keyCode == NKCODE_BACK) {
*pauseTrigger = true; *pauseTrigger = true;
@ -119,18 +192,22 @@ bool ControlMapper::Key(const KeyInput &key, bool *pauseTrigger) {
} }
} }
return pspKeys.size() > 0; return UpdatePSPState();
} }
void ControlMapper::Axis(const AxisInput &axis) { void ControlMapper::Axis(const AxisInput &axis) {
if (axis.value > 0) { if (axis.value > 0) {
ProcessAxis(axis, 1); InputMapping mapping(axis.deviceId, axis.axisId, 1);
curInput_[mapping] = axis.value;
} else if (axis.value < 0) { } else if (axis.value < 0) {
ProcessAxis(axis, -1); InputMapping mapping(axis.deviceId, axis.axisId, -1);
} else if (axis.value == 0) { curInput_[mapping] = axis.value;
} else if (axis.value == 0.0f) { // Threshold?
// Both directions! Prevents sticking for digital input devices that are axises (like HAT) // Both directions! Prevents sticking for digital input devices that are axises (like HAT)
ProcessAxis(axis, 1); InputMapping mappingPositive(axis.deviceId, axis.axisId, -1);
ProcessAxis(axis, -1); InputMapping mappingNegative(axis.deviceId, axis.axisId, -1);
curInput_[mappingPositive] = 0.0f;
curInput_[mappingNegative] = 0.0f;
} }
} }
@ -167,17 +244,6 @@ inline bool IsAnalogStickKey(int key) {
} }
} }
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) { 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. // The down events can repeat, so just trust the virtKeys_ array.
bool minDown = virtKeys_[virtualKeyMin - VIRTKEY_FIRST]; bool minDown = virtKeys_[virtualKeyMin - VIRTKEY_FIRST];
@ -199,6 +265,7 @@ void ControlMapper::PSPKey(int deviceId, int pspKeyCode, int flags) {
} }
void ControlMapper::SetPSPKey(int deviceId, int pspKeyCode, int flags) { void ControlMapper::SetPSPKey(int deviceId, int pspKeyCode, int flags) {
/*
if (pspKeyCode >= VIRTKEY_FIRST) { if (pspKeyCode >= VIRTKEY_FIRST) {
int vk = pspKeyCode - VIRTKEY_FIRST; int vk = pspKeyCode - VIRTKEY_FIRST;
if (flags & KEY_DOWN) { if (flags & KEY_DOWN) {
@ -210,29 +277,13 @@ void ControlMapper::SetPSPKey(int deviceId, int pspKeyCode, int flags) {
onVKey(deviceId, pspKeyCode, false); onVKey(deviceId, pspKeyCode, false);
} }
} else { } 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); // INFO_LOG(SYSTEM, "pspKey %d %d", pspKeyCode, flags);
if (flags & KEY_DOWN) if (flags & KEY_DOWN)
setPSPButtonState_(pspKeyCode, true); setPSPButtonState_(pspKeyCode, true);
if (flags & KEY_UP) if (flags & KEY_UP)
setPSPButtonState_(pspKeyCode, false); setPSPButtonState_(pspKeyCode, false);
} }
*/
} }
void ControlMapper::onVKey(int deviceId, int vkey, bool down) { void ControlMapper::onVKey(int deviceId, int vkey, bool down) {
@ -343,7 +394,7 @@ void ControlMapper::ProcessAxis(const AxisInput &axis, int direction) {
} }
int axisState = 0; int axisState = 0;
float threshold = axis.deviceId == DEVICE_ID_MOUSE ? AXIS_BIND_THRESHOLD_MOUSE : AXIS_BIND_THRESHOLD; float threshold = GetDeviceAxisThreshold(axis.deviceId);
if (direction == 1 && axis.value >= threshold) { if (direction == 1 && axis.value >= threshold) {
axisState = 1; axisState = 1;
} else if (direction == -1 && axis.value <= -threshold) { } else if (direction == -1 && axis.value <= -threshold) {

View file

@ -4,6 +4,7 @@
#include "Core/KeyMap.h" #include "Core/KeyMap.h"
#include <functional> #include <functional>
#include <cstring>
// Utilities for mapping input events to PSP inputs and virtual keys. // 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. // Main use is of course from EmuScreen.cpp, but also useful from control settings etc.
@ -11,6 +12,17 @@
// At some point I want to refactor this from using callbacks to simply providing lists of events. // 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. // Still it won't be able to be completely stateless due to the 2-D processing of analog sticks.
// Unified representation. Might spread across the code base later.
struct ControlInputKey {
int direction; // 0 if key, 1 or -1 if axis.
int deviceId;
int controlId; // key or axis.
bool operator < (const ControlInputKey &other) const {
return memcmp(this, &other, sizeof(*this)) < 0;
}
};
class ControlMapper { class ControlMapper {
public: public:
void Update(); void Update();
@ -34,6 +46,8 @@ public:
void SetRawCallback(std::function<void(int, float, float)> setRawAnalog); void SetRawCallback(std::function<void(int, float, float)> setRawAnalog);
private: private:
bool UpdatePSPState();
void ProcessAxis(const AxisInput &axis, int direction); void ProcessAxis(const AxisInput &axis, int direction);
void SetVKeyAnalog(int deviceId, char axis, int stick, int virtualKeyMin, int virtualKeyMax, bool setZero = true); void SetVKeyAnalog(int deviceId, char axis, int stick, int virtualKeyMin, int virtualKeyMax, bool setZero = true);
@ -57,6 +71,8 @@ private:
bool autoRotatingAnalogCW_ = false; bool autoRotatingAnalogCW_ = false;
bool autoRotatingAnalogCCW_ = false; bool autoRotatingAnalogCCW_ = false;
std::map<InputMapping, float> curInput_;
// Callbacks // Callbacks
std::function<void(int, bool)> onVKey_; std::function<void(int, bool)> onVKey_;
std::function<void(uint32_t, uint32_t)> setAllPSPButtonStates_; std::function<void(uint32_t, uint32_t)> setAllPSPButtonStates_;

View file

@ -192,6 +192,7 @@ void WebSocketInputState::ButtonsPress(DebuggerRequest &req) {
} }
press.button = info->second; press.button = info->second;
// TODO: Route into the control mapper's PSPKey function instead.
__CtrlButtonDown(press.button); __CtrlButtonDown(press.button);
pressTickets_.push_back(press); pressTickets_.push_back(press);
} }
@ -205,6 +206,7 @@ void WebSocketInputState::Broadcast(net::WebSocketServer *ws) {
for (PressInfo &press : pressTickets_) { for (PressInfo &press : pressTickets_) {
press.duration--; press.duration--;
if (press.duration == -1) { if (press.duration == -1) {
// TODO: Route into the control mapper's PSPKey function instead.
__CtrlButtonUp(press.button); __CtrlButtonUp(press.button);
ws->Send(press.Event()); ws->Send(press.Event());
} }
@ -254,6 +256,7 @@ void WebSocketInputState::AnalogSend(DebuggerRequest &req) {
if (!AnalogValue(req, &x, "x") || !AnalogValue(req, &y, "y")) if (!AnalogValue(req, &x, "x") || !AnalogValue(req, &y, "y"))
return; return;
// TODO: Route into the control mapper's PSPKey function or similar instead.
__CtrlSetAnalogXY(stick == "left" ? CTRL_STICK_LEFT : CTRL_STICK_RIGHT, x, y); __CtrlSetAnalogXY(stick == "left" ? CTRL_STICK_LEFT : CTRL_STICK_RIGHT, x, y);
req.Respond(); req.Respond();

View file

@ -70,7 +70,8 @@ void __CtrlShutdown();
void __CtrlButtonDown(u32 buttonBit); void __CtrlButtonDown(u32 buttonBit);
// Call this whenever a button is released. Similar to __CtrlButtonDown(). // Call this whenever a button is released. Similar to __CtrlButtonDown().
void __CtrlButtonUp(u32 buttonBit); void __CtrlButtonUp(u32 buttonBit);
// To be used by the new mapping code. // Sets the full state. Used by the new control mapper. The above two functions
// should go away over time.
void __CtrlSetAllButtons(u32 bitsToSet, u32 bitsToClear); void __CtrlSetAllButtons(u32 bitsToSet, u32 bitsToClear);
// Call this to set the position of an analog stick, ideally when it changes. // Call this to set the position of an analog stick, ideally when it changes.

View file

@ -43,7 +43,7 @@ std::set<std::string> g_seenPads;
std::map<int, std::string> g_padNames; std::map<int, std::string> g_padNames;
std::set<int> g_seenDeviceIds; std::set<int> g_seenDeviceIds;
bool g_swapped_keys = false; bool g_swapDpadWithLStick = false;
// TODO: This is such a mess... // TODO: This is such a mess...
void UpdateNativeMenuKeys() { void UpdateNativeMenuKeys() {
@ -472,8 +472,8 @@ std::vector<KeyMap_IntStrPair> GetMappableKeys() {
return temp; return temp;
} }
int CheckAxisSwap(int btn) { inline int CheckAxisSwap(int btn) {
if (g_swapped_keys) { if (g_swapDpadWithLStick) {
switch (btn) { switch (btn) {
case CTRL_UP: btn = VIRTKEY_AXIS_Y_MAX; break; case CTRL_UP: btn = VIRTKEY_AXIS_Y_MAX; break;
case VIRTKEY_AXIS_Y_MAX: btn = CTRL_UP; break; case VIRTKEY_AXIS_Y_MAX: btn = CTRL_UP; break;
@ -488,19 +488,18 @@ int CheckAxisSwap(int btn) {
return btn; return btn;
} }
static bool FindKeyMapping(const InputMapping &mapping, std::vector<int> *pspButtons) { bool InputMappingToPspButton(const InputMapping &mapping, std::vector<int> *pspButtons) {
bool found = false;
for (auto iter = g_controllerMap.begin(); iter != g_controllerMap.end(); ++iter) { for (auto iter = g_controllerMap.begin(); iter != g_controllerMap.end(); ++iter) {
for (auto iter2 = iter->second.begin(); iter2 != iter->second.end(); ++iter2) { for (auto iter2 = iter->second.begin(); iter2 != iter->second.end(); ++iter2) {
if (*iter2 == mapping) { if (*iter2 == mapping) {
if (pspButtons)
pspButtons->push_back(CheckAxisSwap(iter->first)); pspButtons->push_back(CheckAxisSwap(iter->first));
found = true;
} }
} }
} }
return pspButtons->size() > 0; return found;
}
bool InputMappingToPspButton(const InputMapping &mapping, std::vector<int> *pspButtons) {
return FindKeyMapping(mapping, pspButtons);
} }
bool InputMappingsFromPspButton(int btn, std::vector<InputMapping> *mappings, bool ignoreMouse) { bool InputMappingsFromPspButton(int btn, std::vector<InputMapping> *mappings, bool ignoreMouse) {
@ -774,7 +773,7 @@ std::string PadName(int deviceId) {
// Swap direction buttons and left analog axis // Swap direction buttons and left analog axis
void SwapAxis() { void SwapAxis() {
g_swapped_keys = !g_swapped_keys; g_swapDpadWithLStick = !g_swapDpadWithLStick;
} }
bool HasChanged(int &prevGeneration) { bool HasChanged(int &prevGeneration) {

View file

@ -129,6 +129,8 @@ inline float clamp(float f) {
return f; return f;
} }
// TODO: Instead of __Ctrl, route data into the ControlMapper.
void GenerateAnalogStickEvent(float tiltX, float tiltY) { void GenerateAnalogStickEvent(float tiltX, float tiltY) {
__CtrlSetAnalogXY(CTRL_STICK_LEFT, clamp(tiltX), clamp(tiltY)); __CtrlSetAnalogXY(CTRL_STICK_LEFT, clamp(tiltX), clamp(tiltY));
} }

View file

@ -725,7 +725,6 @@ void MainUI::updateAccelerometer() {
if (reading) { if (reading) {
AxisInput axis; AxisInput axis;
axis.deviceId = DEVICE_ID_ACCELEROMETER; axis.deviceId = DEVICE_ID_ACCELEROMETER;
axis.flags = 0;
axis.axisId = JOYSTICK_AXIS_ACCELEROMETER_X; axis.axisId = JOYSTICK_AXIS_ACCELEROMETER_X;
axis.value = reading->x(); axis.value = reading->x();

View file

@ -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;
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.deviceId = DEVICE_ID_PAD_0 + getDeviceIndex(event.caxis.which);
axis.flags = 0;
NativeAxis(axis); NativeAxis(axis);
break; break;
case SDL_CONTROLLERDEVICEREMOVED: case SDL_CONTROLLERDEVICEREMOVED:

View file

@ -1221,7 +1221,6 @@ extern "C" void JNICALL Java_org_ppsspp_ppsspp_NativeApp_accelerometer(JNIEnv *,
AxisInput axis; AxisInput axis;
axis.deviceId = DEVICE_ID_ACCELEROMETER; axis.deviceId = DEVICE_ID_ACCELEROMETER;
axis.flags = 0;
axis.axisId = JOYSTICK_AXIS_ACCELEROMETER_X; axis.axisId = JOYSTICK_AXIS_ACCELEROMETER_X;
axis.value = x; axis.value = x;

View file

@ -460,7 +460,6 @@ int ToTouchID(UITouch *uiTouch, bool allowAllocate) {
break; break;
} }
axis.deviceId = DEVICE_ID_PAD_0; axis.deviceId = DEVICE_ID_PAD_0;
axis.flags = 0;
NativeAxis(axis); NativeAxis(axis);
} else { } else {
KeyInput key; KeyInput key;
@ -530,7 +529,6 @@ int ToTouchID(UITouch *uiTouch, bool allowAllocate) {
break; break;
} }
axis.deviceId = DEVICE_ID_PAD_0; axis.deviceId = DEVICE_ID_PAD_0;
axis.flags = 0;
NativeAxis(axis); NativeAxis(axis);
} else { } else {
KeyInput key; KeyInput key;
@ -687,7 +685,6 @@ int ToTouchID(UITouch *uiTouch, bool allowAllocate) {
extendedProfile.leftThumbstick.xAxis.valueChangedHandler = ^(GCControllerAxisInput *axis, float value) { extendedProfile.leftThumbstick.xAxis.valueChangedHandler = ^(GCControllerAxisInput *axis, float value) {
AxisInput axisInput; AxisInput axisInput;
axisInput.deviceId = DEVICE_ID_PAD_0; axisInput.deviceId = DEVICE_ID_PAD_0;
axisInput.flags = 0;
axisInput.axisId = JOYSTICK_AXIS_X; axisInput.axisId = JOYSTICK_AXIS_X;
axisInput.value = value; axisInput.value = value;
NativeAxis(axisInput); NativeAxis(axisInput);
@ -696,7 +693,6 @@ int ToTouchID(UITouch *uiTouch, bool allowAllocate) {
extendedProfile.leftThumbstick.yAxis.valueChangedHandler = ^(GCControllerAxisInput *axis, float value) { extendedProfile.leftThumbstick.yAxis.valueChangedHandler = ^(GCControllerAxisInput *axis, float value) {
AxisInput axisInput; AxisInput axisInput;
axisInput.deviceId = DEVICE_ID_PAD_0; axisInput.deviceId = DEVICE_ID_PAD_0;
axisInput.flags = 0;
axisInput.axisId = JOYSTICK_AXIS_Y; axisInput.axisId = JOYSTICK_AXIS_Y;
axisInput.value = -value; axisInput.value = -value;
NativeAxis(axisInput); NativeAxis(axisInput);
@ -706,7 +702,6 @@ int ToTouchID(UITouch *uiTouch, bool allowAllocate) {
extendedProfile.rightThumbstick.xAxis.valueChangedHandler = ^(GCControllerAxisInput *axis, float value) { extendedProfile.rightThumbstick.xAxis.valueChangedHandler = ^(GCControllerAxisInput *axis, float value) {
AxisInput axisInput; AxisInput axisInput;
axisInput.deviceId = DEVICE_ID_PAD_0; axisInput.deviceId = DEVICE_ID_PAD_0;
axisInput.flags = 0;
axisInput.axisId = JOYSTICK_AXIS_Z; axisInput.axisId = JOYSTICK_AXIS_Z;
axisInput.value = value; axisInput.value = value;
NativeAxis(axisInput); NativeAxis(axisInput);
@ -715,7 +710,6 @@ int ToTouchID(UITouch *uiTouch, bool allowAllocate) {
extendedProfile.rightThumbstick.yAxis.valueChangedHandler = ^(GCControllerAxisInput *axis, float value) { extendedProfile.rightThumbstick.yAxis.valueChangedHandler = ^(GCControllerAxisInput *axis, float value) {
AxisInput axisInput; AxisInput axisInput;
axisInput.deviceId = DEVICE_ID_PAD_0; axisInput.deviceId = DEVICE_ID_PAD_0;
axisInput.flags = 0;
axisInput.axisId = JOYSTICK_AXIS_RZ; axisInput.axisId = JOYSTICK_AXIS_RZ;
axisInput.value = -value; axisInput.value = -value;
NativeAxis(axisInput); NativeAxis(axisInput);