mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-09-11 15:13:37 +02:00
Merge pull request #17215 from hrydgard/multi-mapping-input
Control map multiple keys to one output
This commit is contained in:
@@ -134,3 +134,68 @@ private:
|
||||
T fastLookup_[MaxFastSize];
|
||||
std::vector<T> *slowLookup_ = nullptr;
|
||||
};
|
||||
|
||||
template <class T, int MaxSize>
|
||||
struct FixedTinyVec {
|
||||
~FixedTinyVec() {}
|
||||
// WARNING: Can fail if you exceed MaxSize!
|
||||
inline bool push_back(const T &t) {
|
||||
if (count_ < MaxSize) {
|
||||
data_[count_++] = t;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// WARNING: Can fail if you exceed MaxSize!
|
||||
inline T *add_back() {
|
||||
if (count_ < MaxSize) {
|
||||
return &data_[count_++];
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
// Invalid if empty().
|
||||
void pop_back() { count_--; }
|
||||
|
||||
// Unlike TinySet, we can trivially support begin/end as pointers.
|
||||
T *begin() { return data_; }
|
||||
T *end() { return data_ + count_; }
|
||||
const T *begin() const { return data_; }
|
||||
const T *end() const { return data_ + count_; }
|
||||
|
||||
size_t capacity() const { return MaxSize; }
|
||||
void clear() { count_ = 0; }
|
||||
bool empty() const { return count_ == 0; }
|
||||
size_t size() const { return count_; }
|
||||
|
||||
bool contains(T t) const {
|
||||
for (int i = 0; i < count_; i++) {
|
||||
if (data_[i] == t)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Out of bounds (past size() - 1) is undefined behavior.
|
||||
T &operator[] (const size_t index) { return data_[index]; }
|
||||
const T &operator[] (const size_t index) const { return data_[index]; }
|
||||
|
||||
// These two are invalid if empty().
|
||||
const T &back() const { return (*this)[size() - 1]; }
|
||||
const T &front() const { return (*this)[0]; }
|
||||
|
||||
bool operator == (const FixedTinyVec<T, MaxSize> &other) const {
|
||||
if (count_ != other.count_)
|
||||
return false;
|
||||
for (size_t i = 0; i < count_; i++) {
|
||||
if (!(data_[i] == other.data_[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
int count_ = 0; // first in the struct just so it's more visible in the VS debugger.
|
||||
T data_[MaxSize];
|
||||
};
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
// TODO:
|
||||
// Zoom gesture a la http://www.zdnet.com/blog/burnette/how-to-use-multi-touch-in-android-2-part-6-implementing-the-pinch-zoom-gesture/1847
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include "Common/TimeUtil.h"
|
||||
#include "Common/Input/GestureDetector.h"
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include "Common/Input/InputState.h"
|
||||
#include "Common/Input/KeyCodes.h"
|
||||
#include "Common/StringUtils.h"
|
||||
|
||||
const char *GetDeviceName(int deviceId) {
|
||||
switch (deviceId) {
|
||||
@@ -79,6 +80,23 @@ int GetAnalogYDirection(int deviceId) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// NOTE: Changing the format of FromConfigString/ToConfigString breaks controls.ini backwards compatibility.
|
||||
InputMapping InputMapping::FromConfigString(const std::string &str) {
|
||||
std::vector<std::string> parts;
|
||||
SplitString(str, '-', parts);
|
||||
int deviceId = atoi(parts[0].c_str());
|
||||
int keyCode = atoi(parts[1].c_str());
|
||||
|
||||
InputMapping mapping;
|
||||
mapping.deviceId = deviceId;
|
||||
mapping.keyCode = keyCode;
|
||||
return mapping;
|
||||
}
|
||||
|
||||
std::string InputMapping::ToConfigString() const {
|
||||
return StringFromFormat("%d-%d", deviceId, keyCode);
|
||||
}
|
||||
|
||||
void InputMapping::FormatDebug(char *buffer, size_t bufSize) const {
|
||||
if (IsAxis()) {
|
||||
int direction;
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
#include "Common/Math/lin/vec3.h"
|
||||
#include "Common/Input/KeyCodes.h"
|
||||
#include "Common/Log.h"
|
||||
|
||||
@@ -105,6 +105,9 @@ public:
|
||||
_dbg_assert_(direction != 0);
|
||||
}
|
||||
|
||||
static InputMapping FromConfigString(const std::string &str);
|
||||
std::string ToConfigString() const;
|
||||
|
||||
int deviceId;
|
||||
int keyCode; // Can also represent an axis with direction, if encoded properly.
|
||||
|
||||
@@ -131,6 +134,15 @@ public:
|
||||
if (keyCode < other.keyCode) return true;
|
||||
return false;
|
||||
}
|
||||
// Needed for composition.
|
||||
bool operator > (const InputMapping &other) const {
|
||||
if (deviceId > other.deviceId) return true;
|
||||
if (deviceId < other.deviceId) return false;
|
||||
if (keyCode > other.keyCode) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// This one is iffy with the != ANY checks. Should probably be a named method.
|
||||
bool operator == (const InputMapping &other) const {
|
||||
if (deviceId != other.deviceId && deviceId != DEVICE_ID_ANY && other.deviceId != DEVICE_ID_ANY) return false;
|
||||
if (keyCode != other.keyCode) return false;
|
||||
|
||||
+32
-17
@@ -13,6 +13,8 @@
|
||||
#include "Core/CoreParameter.h"
|
||||
#include "Core/System.h"
|
||||
|
||||
using KeyMap::MultiInputMapping;
|
||||
|
||||
// TODO: Possibly make these thresholds configurable?
|
||||
static float GetDeviceAxisThreshold(int device) {
|
||||
return device == DEVICE_ID_MOUSE ? AXIS_BIND_THRESHOLD_MOUSE : AXIS_BIND_THRESHOLD;
|
||||
@@ -219,19 +221,25 @@ bool ControlMapper::UpdatePSPState(const InputMapping &changedMapping) {
|
||||
mappingBit = RotatePSPKeyCode(mappingBit);
|
||||
}
|
||||
|
||||
std::vector<InputMapping> inputMappings;
|
||||
std::vector<MultiInputMapping> 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) {
|
||||
for (auto &multiMapping : inputMappings) {
|
||||
// Check if the changed mapping was involved in this PSP key.
|
||||
if (changedMapping == mapping) {
|
||||
if (multiMapping.mappings.contains(changedMapping)) {
|
||||
changedButtonMask |= mask;
|
||||
}
|
||||
|
||||
auto iter = curInput_.find(mapping);
|
||||
if (iter != curInput_.end() && iter->second > GetDeviceAxisThreshold(iter->first.deviceId)) {
|
||||
// Check if all inputs are "on".
|
||||
bool all = true;
|
||||
for (auto mapping : multiMapping.mappings) {
|
||||
auto iter = curInput_.find(mapping);
|
||||
bool down = iter != curInput_.end() && iter->second > GetDeviceAxisThreshold(iter->first.deviceId);
|
||||
if (!down)
|
||||
all = false;
|
||||
}
|
||||
if (all) {
|
||||
buttonMask |= mask;
|
||||
}
|
||||
}
|
||||
@@ -243,7 +251,7 @@ bool ControlMapper::UpdatePSPState(const InputMapping &changedMapping) {
|
||||
// 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<InputMapping> inputMappings;
|
||||
std::vector<MultiInputMapping> inputMappings;
|
||||
if (!KeyMap::InputMappingsFromPspButton(vkId, &inputMappings, false))
|
||||
continue;
|
||||
|
||||
@@ -253,20 +261,27 @@ bool ControlMapper::UpdatePSPState(const InputMapping &changedMapping) {
|
||||
float threshold = 1.0f;
|
||||
bool touchedByMapping = false;
|
||||
float value = 0.0f;
|
||||
for (auto &mapping : inputMappings) {
|
||||
if (mapping == changedMapping) {
|
||||
for (auto &multiMapping : inputMappings) {
|
||||
if (multiMapping.mappings.contains(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);
|
||||
float product = 1.0f; // We multiply the various inputs in a combo mapping with each other.
|
||||
for (auto mapping : multiMapping.mappings) {
|
||||
auto iter = curInput_.find(mapping);
|
||||
if (iter != curInput_.end()) {
|
||||
if (mapping.IsAxis()) {
|
||||
threshold = GetDeviceAxisThreshold(iter->first.deviceId);
|
||||
product *= MapAxisValue(iter->second, vkId, mapping, changedMapping, &touchedByMapping);
|
||||
} else {
|
||||
product *= iter->second;
|
||||
}
|
||||
} else {
|
||||
value += iter->second;
|
||||
product = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
value += product;
|
||||
}
|
||||
|
||||
if (!touchedByMapping) {
|
||||
@@ -280,8 +295,8 @@ bool ControlMapper::UpdatePSPState(const InputMapping &changedMapping) {
|
||||
// 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()) {
|
||||
for (auto &multiMapping : inputMappings) {
|
||||
for (auto &mapping : multiMapping.mappings) {
|
||||
curInput_[mapping] = ReduceMagnitude(curInput_[mapping]);
|
||||
}
|
||||
}
|
||||
|
||||
+117
-38
@@ -45,6 +45,17 @@ std::set<int> g_seenDeviceIds;
|
||||
|
||||
bool g_swapDpadWithLStick = false;
|
||||
|
||||
// Utility...
|
||||
void SingleInputMappingFromPspButton(int btn, std::vector<InputMapping> *mappings, bool ignoreMouse) {
|
||||
std::vector<MultiInputMapping> multiMappings;
|
||||
InputMappingsFromPspButton(btn, &multiMappings, ignoreMouse);
|
||||
mappings->clear();
|
||||
for (auto &mapping : multiMappings) {
|
||||
_dbg_assert_(!mapping.empty());
|
||||
mappings->push_back(mapping.mappings[0]);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: This is such a mess...
|
||||
void UpdateNativeMenuKeys() {
|
||||
std::vector<InputMapping> confirmKeys, cancelKeys;
|
||||
@@ -55,14 +66,14 @@ void UpdateNativeMenuKeys() {
|
||||
int cancelKey = g_Config.iButtonPreference == PSP_SYSTEMPARAM_BUTTON_CROSS ? CTRL_CIRCLE : CTRL_CROSS;
|
||||
|
||||
// Mouse mapping might be problematic in UI, so let's ignore mouse for UI
|
||||
InputMappingsFromPspButton(confirmKey, &confirmKeys, true);
|
||||
InputMappingsFromPspButton(cancelKey, &cancelKeys, true);
|
||||
InputMappingsFromPspButton(CTRL_LTRIGGER, &tabLeft, true);
|
||||
InputMappingsFromPspButton(CTRL_RTRIGGER, &tabRight, true);
|
||||
InputMappingsFromPspButton(CTRL_UP, &upKeys, true);
|
||||
InputMappingsFromPspButton(CTRL_DOWN, &downKeys, true);
|
||||
InputMappingsFromPspButton(CTRL_LEFT, &leftKeys, true);
|
||||
InputMappingsFromPspButton(CTRL_RIGHT, &rightKeys, true);
|
||||
SingleInputMappingFromPspButton(confirmKey, &confirmKeys, true);
|
||||
SingleInputMappingFromPspButton(cancelKey, &cancelKeys, true);
|
||||
SingleInputMappingFromPspButton(CTRL_LTRIGGER, &tabLeft, true);
|
||||
SingleInputMappingFromPspButton(CTRL_RTRIGGER, &tabRight, true);
|
||||
SingleInputMappingFromPspButton(CTRL_UP, &upKeys, true);
|
||||
SingleInputMappingFromPspButton(CTRL_DOWN, &downKeys, true);
|
||||
SingleInputMappingFromPspButton(CTRL_LEFT, &leftKeys, true);
|
||||
SingleInputMappingFromPspButton(CTRL_RIGHT, &rightKeys, true);
|
||||
|
||||
#ifdef __ANDROID__
|
||||
// Hardcode DPAD on Android
|
||||
@@ -99,6 +110,12 @@ void UpdateNativeMenuKeys() {
|
||||
cancelKeys.push_back(hardcodedCancelKeys[i]);
|
||||
}
|
||||
|
||||
// For DInput controllers on Windows. Doesn't clash with XInput because that uses BUTTON_X etc.
|
||||
#if PPSSPP_PLATFORM(WINDOWS) && !PPSSPP_PLATFORM(UWP)
|
||||
confirmKeys.push_back(InputMapping(DEVICE_ID_PAD_0, NKCODE_BUTTON_2));
|
||||
cancelKeys.push_back(InputMapping(DEVICE_ID_PAD_0, NKCODE_BUTTON_3));
|
||||
#endif
|
||||
|
||||
SetDPadKeys(upKeys, downKeys, leftKeys, rightKeys);
|
||||
SetConfirmCancelKeys(confirmKeys, cancelKeys);
|
||||
SetTabLeftRightKeys(tabLeft, tabRight);
|
||||
@@ -493,7 +510,7 @@ bool InputMappingToPspButton(const InputMapping &mapping, std::vector<int> *pspB
|
||||
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) {
|
||||
if (iter2->EqualsSingleMapping(mapping)) {
|
||||
if (pspButtons)
|
||||
pspButtons->push_back(CheckAxisSwap(iter->first));
|
||||
found = true;
|
||||
@@ -503,21 +520,29 @@ bool InputMappingToPspButton(const InputMapping &mapping, std::vector<int> *pspB
|
||||
return found;
|
||||
}
|
||||
|
||||
bool InputMappingsFromPspButton(int btn, std::vector<InputMapping> *mappings, bool ignoreMouse) {
|
||||
bool InputMappingsFromPspButton(int btn, std::vector<MultiInputMapping> *mappings, bool ignoreMouse) {
|
||||
auto iter = g_controllerMap.find(btn);
|
||||
if (iter == g_controllerMap.end()) {
|
||||
return false;
|
||||
}
|
||||
bool mapped = false;
|
||||
for (auto iter = g_controllerMap.begin(); iter != g_controllerMap.end(); ++iter) {
|
||||
if (iter->first == btn) {
|
||||
for (auto iter2 = iter->second.begin(); iter2 != iter->second.end(); ++iter2) {
|
||||
if (mappings && (!ignoreMouse || iter2->deviceId != DEVICE_ID_MOUSE)) {
|
||||
mapped = true;
|
||||
mappings->push_back(*iter2);
|
||||
}
|
||||
}
|
||||
for (auto iter2 = iter->second.begin(); iter2 != iter->second.end(); ++iter2) {
|
||||
if (mappings && (!ignoreMouse || iter2->HasMouse())) {
|
||||
mapped = true;
|
||||
mappings->push_back(*iter2);
|
||||
}
|
||||
}
|
||||
return mapped;
|
||||
}
|
||||
|
||||
bool PspButtonHasMappings(int btn) {
|
||||
auto iter = g_controllerMap.find(btn);
|
||||
if (iter == g_controllerMap.end()) {
|
||||
return false;
|
||||
}
|
||||
return !iter->second.empty();
|
||||
}
|
||||
|
||||
MappedAnalogAxes MappedAxesForDevice(int deviceId) {
|
||||
MappedAnalogAxes result{};
|
||||
|
||||
@@ -525,8 +550,11 @@ MappedAnalogAxes MappedAxesForDevice(int deviceId) {
|
||||
auto findAxisId = [&](int btn) -> MappedAnalogAxis {
|
||||
MappedAnalogAxis info{ -1 };
|
||||
for (const auto &key : g_controllerMap[btn]) {
|
||||
if (key.deviceId == deviceId) {
|
||||
info.axisId = TranslateKeyCodeToAxis(key.keyCode, &info.direction);
|
||||
// Only consider single mappings, combos don't make much sense for these.
|
||||
if (key.mappings.empty()) continue;
|
||||
auto &mapping = key.mappings[0];
|
||||
if (mapping.deviceId == deviceId) {
|
||||
info.axisId = TranslateKeyCodeToAxis(mapping.keyCode, &info.direction);
|
||||
return info;
|
||||
}
|
||||
}
|
||||
@@ -562,7 +590,7 @@ void RemoveButtonMapping(int btn) {
|
||||
bool IsKeyMapped(int device, int key) {
|
||||
for (auto &iter : g_controllerMap) {
|
||||
for (auto &mappedKey : iter.second) {
|
||||
if (mappedKey == InputMapping(device, key)) {
|
||||
if (mappedKey.mappings.contains(InputMapping(device, key))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -570,7 +598,7 @@ bool IsKeyMapped(int device, int key) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ReplaceSingleKeyMapping(int btn, int index, InputMapping key) {
|
||||
bool ReplaceSingleKeyMapping(int btn, int index, MultiInputMapping key) {
|
||||
// Check for duplicate
|
||||
for (int i = 0; i < (int)g_controllerMap[btn].size(); ++i) {
|
||||
if (i != index && g_controllerMap[btn][i] == key) {
|
||||
@@ -584,14 +612,28 @@ bool ReplaceSingleKeyMapping(int btn, int index, InputMapping key) {
|
||||
KeyMap::g_controllerMap[btn][index] = key;
|
||||
g_controllerMapGeneration++;
|
||||
|
||||
g_seenDeviceIds.insert(key.deviceId);
|
||||
for (auto &mapping : key.mappings) {
|
||||
g_seenDeviceIds.insert(mapping.deviceId);
|
||||
}
|
||||
UpdateNativeMenuKeys();
|
||||
return true;
|
||||
}
|
||||
|
||||
void SetInputMapping(int btn, const InputMapping &key, bool replace) {
|
||||
if (key.keyCode < 0)
|
||||
void DeleteNthMapping(int key, int number) {
|
||||
auto iter = g_controllerMap.find(key);
|
||||
if (iter != g_controllerMap.end()) {
|
||||
if (number < iter->second.size()) {
|
||||
iter->second.erase(iter->second.begin() + number);
|
||||
g_controllerMapGeneration++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SetInputMapping(int btn, const MultiInputMapping &key, bool replace) {
|
||||
if (key.empty()) {
|
||||
g_controllerMap.erase(btn);
|
||||
return;
|
||||
}
|
||||
if (replace) {
|
||||
RemoveButtonMapping(btn);
|
||||
g_controllerMap[btn].clear();
|
||||
@@ -605,7 +647,9 @@ void SetInputMapping(int btn, const InputMapping &key, bool replace) {
|
||||
}
|
||||
g_controllerMapGeneration++;
|
||||
|
||||
g_seenDeviceIds.insert(key.deviceId);
|
||||
for (auto &mapping : key.mappings) {
|
||||
g_seenDeviceIds.insert(mapping.deviceId);
|
||||
}
|
||||
UpdateNativeMenuKeys();
|
||||
}
|
||||
|
||||
@@ -667,13 +711,12 @@ void LoadFromIni(IniFile &file) {
|
||||
SplitString(value, ',', mappings);
|
||||
|
||||
for (size_t j = 0; j < mappings.size(); j++) {
|
||||
std::vector<std::string> parts;
|
||||
SplitString(mappings[j], '-', parts);
|
||||
int deviceId = atoi(parts[0].c_str());
|
||||
int keyCode = atoi(parts[1].c_str());
|
||||
MultiInputMapping input = MultiInputMapping::FromConfigString(mappings[j]);
|
||||
SetInputMapping(psp_button_names[i].key, input, false);
|
||||
|
||||
SetInputMapping(psp_button_names[i].key, InputMapping(deviceId, keyCode), false);
|
||||
g_seenDeviceIds.insert(deviceId);
|
||||
for (auto mapping : input.mappings) {
|
||||
g_seenDeviceIds.insert(mapping.deviceId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -684,14 +727,12 @@ void SaveToIni(IniFile &file) {
|
||||
Section *controls = file.GetOrCreateSection("ControlMapping");
|
||||
|
||||
for (size_t i = 0; i < ARRAY_SIZE(psp_button_names); i++) {
|
||||
std::vector<InputMapping> keys;
|
||||
std::vector<MultiInputMapping> keys;
|
||||
InputMappingsFromPspButton(psp_button_names[i].key, &keys, false);
|
||||
|
||||
std::string value;
|
||||
for (size_t j = 0; j < keys.size(); j++) {
|
||||
char temp[128];
|
||||
sprintf(temp, "%i-%i", keys[j].deviceId, keys[j].keyCode);
|
||||
value += temp;
|
||||
value += keys[j].ToConfigString();
|
||||
if (j != keys.size() - 1)
|
||||
value += ",";
|
||||
}
|
||||
@@ -700,6 +741,11 @@ void SaveToIni(IniFile &file) {
|
||||
}
|
||||
}
|
||||
|
||||
void ClearAllMappings() {
|
||||
g_controllerMap.clear();
|
||||
g_controllerMapGeneration++;
|
||||
}
|
||||
|
||||
bool IsOuya(const std::string &name) {
|
||||
return name == "OUYA:OUYA Console";
|
||||
}
|
||||
@@ -756,8 +802,8 @@ void AutoConfForPad(const std::string &name) {
|
||||
#endif
|
||||
|
||||
// Add a couple of convenient keyboard mappings by default, too.
|
||||
g_controllerMap[VIRTKEY_PAUSE].push_back(InputMapping(DEVICE_ID_KEYBOARD, NKCODE_ESCAPE));
|
||||
g_controllerMap[VIRTKEY_FASTFORWARD].push_back(InputMapping(DEVICE_ID_KEYBOARD, NKCODE_TAB));
|
||||
g_controllerMap[VIRTKEY_PAUSE].push_back(MultiInputMapping(InputMapping(DEVICE_ID_KEYBOARD, NKCODE_ESCAPE)));
|
||||
g_controllerMap[VIRTKEY_FASTFORWARD].push_back(MultiInputMapping(InputMapping(DEVICE_ID_KEYBOARD, NKCODE_TAB)));
|
||||
g_controllerMapGeneration++;
|
||||
}
|
||||
|
||||
@@ -834,4 +880,37 @@ const char *GetVirtKeyName(int vkey) {
|
||||
return g_vKeyNames[index];
|
||||
}
|
||||
|
||||
MultiInputMapping MultiInputMapping::FromConfigString(const std::string &str) {
|
||||
MultiInputMapping out;
|
||||
std::vector<std::string> parts;
|
||||
SplitString(str, ':', parts);
|
||||
for (auto iter : parts) {
|
||||
out.mappings.push_back(InputMapping::FromConfigString(iter));
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
std::string MultiInputMapping::ToConfigString() const {
|
||||
std::string out;
|
||||
for (auto iter : mappings) {
|
||||
out += iter.ToConfigString() + ":";
|
||||
}
|
||||
out.pop_back(); // remove the last ':'
|
||||
return out;
|
||||
}
|
||||
|
||||
std::string MultiInputMapping::ToVisualString() const {
|
||||
std::string out;
|
||||
for (auto iter : mappings) {
|
||||
out += std::string(GetDeviceName(iter.deviceId)) + "." + GetKeyOrAxisName(iter) + " + ";
|
||||
}
|
||||
if (!out.empty()) {
|
||||
// remove the last ' + '
|
||||
out.pop_back();
|
||||
out.pop_back();
|
||||
out.pop_back();
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
} // KeyMap
|
||||
|
||||
+62
-7
@@ -24,6 +24,7 @@
|
||||
|
||||
#include "Common/Input/InputState.h" // InputMapping
|
||||
#include "Common/Input/KeyCodes.h" // keyboard keys
|
||||
#include "Common/Data/Collections/TinySet.h"
|
||||
#include "Core/KeyMapDefaults.h"
|
||||
|
||||
#define KEYMAP_ERROR_KEY_ALREADY_USED -1
|
||||
@@ -75,10 +76,9 @@ enum {
|
||||
};
|
||||
|
||||
const float AXIS_BIND_THRESHOLD = 0.75f;
|
||||
const float AXIS_BIND_RELEASE_THRESHOLD = 0.35f; // Used during mapping only to detect a "key-up" reliably.
|
||||
const float AXIS_BIND_THRESHOLD_MOUSE = 0.01f;
|
||||
|
||||
typedef std::map<int, std::vector<InputMapping>> KeyMapping;
|
||||
|
||||
struct MappedAnalogAxis {
|
||||
int axisId;
|
||||
int direction;
|
||||
@@ -104,10 +104,58 @@ struct MappedAnalogAxes {
|
||||
class IniFile;
|
||||
|
||||
namespace KeyMap {
|
||||
extern KeyMapping g_controllerMap;
|
||||
// Combo of InputMappings.
|
||||
struct MultiInputMapping {
|
||||
MultiInputMapping() {}
|
||||
explicit MultiInputMapping(const InputMapping &mapping) {
|
||||
mappings.push_back(mapping);
|
||||
}
|
||||
|
||||
static MultiInputMapping FromConfigString(const std::string &str);
|
||||
std::string ToConfigString() const;
|
||||
|
||||
std::string ToVisualString() const;
|
||||
|
||||
bool operator <(const MultiInputMapping &other) {
|
||||
for (size_t i = 0; i < mappings.capacity(); i++) {
|
||||
// If one ran out of entries, the other wins.
|
||||
if (mappings.size() == i && other.mappings.size() > i) return true;
|
||||
if (mappings.size() >= i && other.mappings.size() == i) return false;
|
||||
if (mappings[i] < other.mappings[i]) return true;
|
||||
if (mappings[i] > other.mappings[i]) return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool operator ==(const MultiInputMapping &other) const {
|
||||
return mappings == other.mappings;
|
||||
}
|
||||
|
||||
bool EqualsSingleMapping(const InputMapping &other) const {
|
||||
return mappings.size() == 1 && mappings[0] == other;
|
||||
}
|
||||
|
||||
bool empty() const {
|
||||
return mappings.empty();
|
||||
}
|
||||
|
||||
bool HasMouse() const {
|
||||
for (auto &m : mappings) {
|
||||
return m.deviceId == DEVICE_ID_MOUSE;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
FixedTinyVec<InputMapping, 3> mappings;
|
||||
};
|
||||
|
||||
typedef std::map<int, std::vector<MultiInputMapping>> KeyMapping;
|
||||
|
||||
// Once the multimappings are inserted here, they must not be empty.
|
||||
// If one would be, delete the whole entry from the map instead.
|
||||
// This is automatically handled by SetInputMapping.
|
||||
extern std::set<int> g_seenDeviceIds;
|
||||
extern int g_controllerMapGeneration;
|
||||
|
||||
// Key & Button names
|
||||
struct KeyMap_IntStrPair {
|
||||
int key;
|
||||
@@ -126,19 +174,26 @@ namespace KeyMap {
|
||||
|
||||
// Use to translate input mappings to and from PSP buttons. You should have already translated
|
||||
// your platform's keys to InputMapping keys.
|
||||
|
||||
// Note that this one does not handle combos, since there's only one input.
|
||||
bool InputMappingToPspButton(const InputMapping &mapping, std::vector<int> *pspButtons);
|
||||
bool InputMappingsFromPspButton(int btn, std::vector<InputMapping> *keys, bool ignoreMouse);
|
||||
bool InputMappingsFromPspButton(int btn, std::vector<MultiInputMapping> *keys, bool ignoreMouse);
|
||||
|
||||
// Simplified check.
|
||||
bool PspButtonHasMappings(int btn);
|
||||
|
||||
// Configure the key or axis mapping.
|
||||
// Any configuration will be saved to the Core config.
|
||||
void SetInputMapping(int psp_key, const InputMapping &key, bool replace);
|
||||
void SetInputMapping(int psp_key, const MultiInputMapping &key, bool replace);
|
||||
// Return false if bind was a duplicate and got removed
|
||||
bool ReplaceSingleKeyMapping(int btn, int index, InputMapping key);
|
||||
bool ReplaceSingleKeyMapping(int btn, int index, MultiInputMapping key);
|
||||
|
||||
MappedAnalogAxes MappedAxesForDevice(int deviceId);
|
||||
|
||||
void LoadFromIni(IniFile &iniFile);
|
||||
void SaveToIni(IniFile &iniFile);
|
||||
void ClearAllMappings();
|
||||
void DeleteNthMapping(int key, int number);
|
||||
|
||||
void SetDefaultKeyMap(DefaultMaps dmap, bool replace);
|
||||
|
||||
|
||||
@@ -341,9 +341,9 @@ static const DefMappingStruct defaultVRRightController[] = {
|
||||
static void SetDefaultKeyMap(int deviceId, const DefMappingStruct *array, size_t count, bool replace) {
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
if (array[i].direction == 0)
|
||||
SetInputMapping(array[i].pspKey, InputMapping(deviceId, array[i].keyOrAxis), replace);
|
||||
SetInputMapping(array[i].pspKey, MultiInputMapping(InputMapping(deviceId, array[i].keyOrAxis)), replace);
|
||||
else
|
||||
SetInputMapping(array[i].pspKey, InputMapping(deviceId, array[i].keyOrAxis, array[i].direction), replace);
|
||||
SetInputMapping(array[i].pspKey, MultiInputMapping(InputMapping(deviceId, array[i].keyOrAxis, array[i].direction)), replace);
|
||||
}
|
||||
g_seenDeviceIds.insert(deviceId);
|
||||
}
|
||||
|
||||
+62
-42
@@ -51,6 +51,8 @@
|
||||
#include "android/jni/app-android.h"
|
||||
#endif
|
||||
|
||||
using KeyMap::MultiInputMapping;
|
||||
|
||||
class SingleControlMapper : public UI::LinearLayout {
|
||||
public:
|
||||
SingleControlMapper(int pspKey, std::string keyName, ScreenManager *scrm, UI::LinearLayoutParams *layoutParams = nullptr);
|
||||
@@ -66,7 +68,7 @@ private:
|
||||
UI::EventReturn OnReplace(UI::EventParams ¶ms);
|
||||
UI::EventReturn OnReplaceAll(UI::EventParams ¶ms);
|
||||
|
||||
void MappedCallback(InputMapping key);
|
||||
void MappedCallback(MultiInputMapping key);
|
||||
|
||||
enum Action {
|
||||
NONE,
|
||||
@@ -109,7 +111,7 @@ void SingleControlMapper::Refresh() {
|
||||
float itemH = 55.0f;
|
||||
|
||||
float leftColumnWidth = 200;
|
||||
float rightColumnWidth = 250; // TODO: Should be flexible somehow. Maybe we need to implement Measure.
|
||||
float rightColumnWidth = 350; // TODO: Should be flexible somehow. Maybe we need to implement Measure.
|
||||
|
||||
LinearLayout *root = Add(new LinearLayout(ORIENT_HORIZONTAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)));
|
||||
root->SetSpacing(3.0f);
|
||||
@@ -134,19 +136,17 @@ void SingleControlMapper::Refresh() {
|
||||
|
||||
LinearLayout *rightColumn = root->Add(new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(rightColumnWidth, WRAP_CONTENT)));
|
||||
rightColumn->SetSpacing(2.0f);
|
||||
std::vector<InputMapping> mappings;
|
||||
std::vector<MultiInputMapping> mappings;
|
||||
KeyMap::InputMappingsFromPspButton(pspKey_, &mappings, false);
|
||||
|
||||
rows_.clear();
|
||||
for (size_t i = 0; i < mappings.size(); i++) {
|
||||
std::string deviceName = GetDeviceName(mappings[i].deviceId);
|
||||
std::string keyName = KeyMap::GetKeyOrAxisName(mappings[i]);
|
||||
|
||||
std::string multiMappingString = mappings[i].ToVisualString();
|
||||
LinearLayout *row = rightColumn->Add(new LinearLayout(ORIENT_HORIZONTAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)));
|
||||
row->SetSpacing(2.0f);
|
||||
rows_.push_back(row);
|
||||
|
||||
Choice *c = row->Add(new Choice(deviceName + "." + keyName, new LinearLayoutParams(FILL_PARENT, itemH, 1.0f)));
|
||||
Choice *c = row->Add(new Choice(multiMappingString, new LinearLayoutParams(FILL_PARENT, itemH, 1.0f)));
|
||||
c->SetTag(StringFromFormat("%d_Change%d", (int)i, pspKey_));
|
||||
c->OnClick.Handle(this, &SingleControlMapper::OnReplace);
|
||||
|
||||
@@ -162,7 +162,7 @@ void SingleControlMapper::Refresh() {
|
||||
}
|
||||
}
|
||||
|
||||
void SingleControlMapper::MappedCallback(InputMapping kdf) {
|
||||
void SingleControlMapper::MappedCallback(MultiInputMapping kdf) {
|
||||
switch (action_) {
|
||||
case ADD:
|
||||
KeyMap::SetInputMapping(pspKey_, kdf, false);
|
||||
@@ -222,8 +222,7 @@ UI::EventReturn SingleControlMapper::OnAddMouse(UI::EventParams ¶ms) {
|
||||
|
||||
UI::EventReturn SingleControlMapper::OnDelete(UI::EventParams ¶ms) {
|
||||
int index = atoi(params.v->Tag().c_str());
|
||||
KeyMap::g_controllerMap[pspKey_].erase(KeyMap::g_controllerMap[pspKey_].begin() + index);
|
||||
KeyMap::g_controllerMapGeneration++;
|
||||
KeyMap::DeleteNthMapping(pspKey_, index);
|
||||
|
||||
if (index + 1 < (int)rows_.size())
|
||||
rows_[index]->SetFocus();
|
||||
@@ -285,8 +284,7 @@ void ControlMappingScreen::update() {
|
||||
}
|
||||
|
||||
UI::EventReturn ControlMappingScreen::OnClearMapping(UI::EventParams ¶ms) {
|
||||
KeyMap::g_controllerMap.clear();
|
||||
KeyMap::g_controllerMapGeneration++;
|
||||
KeyMap::ClearAllMappings();
|
||||
return UI::EVENT_DONE;
|
||||
}
|
||||
|
||||
@@ -330,26 +328,39 @@ void KeyMappingNewKeyDialog::CreatePopupContents(UI::ViewGroup *parent) {
|
||||
|
||||
std::string pspButtonName = KeyMap::GetPspButtonName(this->pspBtn_);
|
||||
|
||||
parent->Add(new TextView(std::string(km->T("Map a new key for")) + " " + mc->T(pspButtonName), new LinearLayoutParams(Margins(10,0))));
|
||||
parent->Add(new TextView(std::string(km->T("Map a new key for")) + " " + mc->T(pspButtonName), new LinearLayoutParams(Margins(10, 0))));
|
||||
parent->Add(new TextView(std::string(mapping_.ToVisualString()), new LinearLayoutParams(Margins(10, 0))));
|
||||
|
||||
SetVRAppMode(VRAppMode::VR_CONTROLLER_MAPPING_MODE);
|
||||
}
|
||||
|
||||
bool KeyMappingNewKeyDialog::key(const KeyInput &key) {
|
||||
if (mapped_ || time_now_d() < delayUntil_)
|
||||
return false;
|
||||
if (time_now_d() < delayUntil_)
|
||||
return true;
|
||||
if (key.flags & KEY_DOWN) {
|
||||
if (key.keyCode == NKCODE_EXT_MOUSEBUTTON_1) {
|
||||
// Don't map
|
||||
return true;
|
||||
}
|
||||
// Only map analog values to this mapping.
|
||||
if (pspBtn_ == VIRTKEY_SPEED_ANALOG && !UI::IsEscapeKey(key))
|
||||
return true;
|
||||
|
||||
mapped_ = true;
|
||||
InputMapping kdf(key.deviceId, key.keyCode);
|
||||
if (pspBtn_ == VIRTKEY_SPEED_ANALOG && !UI::IsEscapeKey(key)) {
|
||||
// Only map analog values to this mapping.
|
||||
return true;
|
||||
}
|
||||
|
||||
InputMapping newMapping(key.deviceId, key.keyCode);
|
||||
|
||||
if (!(key.flags & KEY_IS_REPEAT)) {
|
||||
if (!mapping_.mappings.contains(newMapping)) {
|
||||
mapping_.mappings.push_back(newMapping);
|
||||
RecreateViews();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (key.flags & KEY_UP) {
|
||||
if (callback_)
|
||||
callback_(mapping_);
|
||||
TriggerFinish(DR_YES);
|
||||
if (callback_ && pspBtn_ != VIRTKEY_SPEED_ANALOG)
|
||||
callback_(kdf);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -378,7 +389,7 @@ bool KeyMappingNewMouseKeyDialog::key(const KeyInput &key) {
|
||||
}
|
||||
|
||||
mapped_ = true;
|
||||
InputMapping kdf(key.deviceId, key.keyCode);
|
||||
MultiInputMapping kdf(InputMapping(key.deviceId, key.keyCode));
|
||||
TriggerFinish(DR_YES);
|
||||
g_Config.bMapMouse = false;
|
||||
if (callback_)
|
||||
@@ -389,10 +400,11 @@ bool KeyMappingNewMouseKeyDialog::key(const KeyInput &key) {
|
||||
|
||||
static bool IgnoreAxisForMapping(int axis) {
|
||||
switch (axis) {
|
||||
// Ignore the accelerometer for mapping for now.
|
||||
case JOYSTICK_AXIS_ACCELEROMETER_X:
|
||||
case JOYSTICK_AXIS_ACCELEROMETER_Y:
|
||||
case JOYSTICK_AXIS_ACCELEROMETER_Z:
|
||||
// Ignore the accelerometer for mapping for now.
|
||||
// We use tilt control for these.
|
||||
return true;
|
||||
|
||||
default:
|
||||
@@ -400,27 +412,35 @@ static bool IgnoreAxisForMapping(int axis) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void KeyMappingNewKeyDialog::axis(const AxisInput &axis) {
|
||||
if (mapped_ || time_now_d() < delayUntil_)
|
||||
if (time_now_d() < delayUntil_)
|
||||
return;
|
||||
if (IgnoreAxisForMapping(axis.axisId))
|
||||
return;
|
||||
|
||||
if (axis.value > AXIS_BIND_THRESHOLD) {
|
||||
mapped_ = true;
|
||||
InputMapping kdf(axis.deviceId, axis.axisId, 1);
|
||||
TriggerFinish(DR_YES);
|
||||
if (callback_)
|
||||
callback_(kdf);
|
||||
}
|
||||
|
||||
if (axis.value < -AXIS_BIND_THRESHOLD) {
|
||||
mapped_ = true;
|
||||
InputMapping kdf(axis.deviceId, axis.axisId, -1);
|
||||
TriggerFinish(DR_YES);
|
||||
if (callback_)
|
||||
callback_(kdf);
|
||||
InputMapping mapping(axis.deviceId, axis.axisId, 1);
|
||||
triggeredAxes_.insert(mapping);
|
||||
if (!mapping_.mappings.contains(mapping)) {
|
||||
mapping_.mappings.push_back(mapping);
|
||||
RecreateViews();
|
||||
}
|
||||
} else if (axis.value < -AXIS_BIND_THRESHOLD) {
|
||||
InputMapping mapping(axis.deviceId, axis.axisId, -1);
|
||||
triggeredAxes_.insert(mapping);
|
||||
if (!mapping_.mappings.contains(mapping)) {
|
||||
mapping_.mappings.push_back(mapping);
|
||||
RecreateViews();
|
||||
}
|
||||
} else if (fabsf(axis.value) < AXIS_BIND_RELEASE_THRESHOLD) {
|
||||
InputMapping neg(axis.deviceId, axis.axisId, -1);
|
||||
InputMapping pos(axis.deviceId, axis.axisId, 1);
|
||||
if (triggeredAxes_.find(neg) != triggeredAxes_.end() || triggeredAxes_.find(pos) != triggeredAxes_.end()) {
|
||||
// "Key-up" the axis.
|
||||
TriggerFinish(DR_YES);
|
||||
if (callback_)
|
||||
callback_(mapping_);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -432,7 +452,7 @@ void KeyMappingNewMouseKeyDialog::axis(const AxisInput &axis) {
|
||||
|
||||
if (axis.value > AXIS_BIND_THRESHOLD) {
|
||||
mapped_ = true;
|
||||
InputMapping kdf(axis.deviceId, axis.axisId, 1);
|
||||
MultiInputMapping kdf(InputMapping(axis.deviceId, axis.axisId, 1));
|
||||
TriggerFinish(DR_YES);
|
||||
if (callback_)
|
||||
callback_(kdf);
|
||||
@@ -440,7 +460,7 @@ void KeyMappingNewMouseKeyDialog::axis(const AxisInput &axis) {
|
||||
|
||||
if (axis.value < -AXIS_BIND_THRESHOLD) {
|
||||
mapped_ = true;
|
||||
InputMapping kdf(axis.deviceId, axis.axisId, -1);
|
||||
MultiInputMapping kdf(InputMapping(axis.deviceId, axis.axisId, -1));
|
||||
TriggerFinish(DR_YES);
|
||||
if (callback_)
|
||||
callback_(kdf);
|
||||
@@ -1147,7 +1167,7 @@ UI::EventReturn VisualMappingScreen::OnBindAll(UI::EventParams &e) {
|
||||
return UI::EVENT_DONE;
|
||||
}
|
||||
|
||||
void VisualMappingScreen::HandleKeyMapping(InputMapping key) {
|
||||
void VisualMappingScreen::HandleKeyMapping(MultiInputMapping key) {
|
||||
KeyMap::SetInputMapping(nextKey_, key, replace_);
|
||||
|
||||
if (bindAll_ < 0) {
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
|
||||
@@ -56,7 +57,7 @@ private:
|
||||
|
||||
class KeyMappingNewKeyDialog : public PopupScreen {
|
||||
public:
|
||||
explicit KeyMappingNewKeyDialog(int btn, bool replace, std::function<void(InputMapping)> callback, std::shared_ptr<I18NCategory> i18n)
|
||||
explicit KeyMappingNewKeyDialog(int btn, bool replace, std::function<void(KeyMap::MultiInputMapping)> callback, std::shared_ptr<I18NCategory> i18n)
|
||||
: PopupScreen(i18n->T("Map Key"), "Cancel", ""), pspBtn_(btn), callback_(callback) {}
|
||||
|
||||
const char *tag() const override { return "KeyMappingNewKey"; }
|
||||
@@ -75,14 +76,19 @@ protected:
|
||||
|
||||
private:
|
||||
int pspBtn_;
|
||||
std::function<void(InputMapping)> callback_;
|
||||
bool mapped_ = false; // Prevent double registrations
|
||||
std::function<void(KeyMap::MultiInputMapping)> callback_;
|
||||
|
||||
KeyMap::MultiInputMapping mapping_;
|
||||
|
||||
// We need to do our own detection for axis "keyup" here.
|
||||
std::set<InputMapping> triggeredAxes_;
|
||||
|
||||
double delayUntil_ = 0.0f;
|
||||
};
|
||||
|
||||
class KeyMappingNewMouseKeyDialog : public PopupScreen {
|
||||
public:
|
||||
KeyMappingNewMouseKeyDialog(int btn, bool replace, std::function<void(InputMapping)> callback, std::shared_ptr<I18NCategory> i18n)
|
||||
KeyMappingNewMouseKeyDialog(int btn, bool replace, std::function<void(KeyMap::MultiInputMapping)> callback, std::shared_ptr<I18NCategory> i18n)
|
||||
: PopupScreen(i18n->T("Map Mouse"), "", ""), pspBtn_(btn), callback_(callback), mapped_(false) {}
|
||||
|
||||
const char *tag() const override { return "KeyMappingNewMouseKey"; }
|
||||
@@ -99,7 +105,7 @@ protected:
|
||||
|
||||
private:
|
||||
int pspBtn_;
|
||||
std::function<void(InputMapping)> callback_;
|
||||
std::function<void(KeyMap::MultiInputMapping)> callback_;
|
||||
bool mapped_; // Prevent double registrations
|
||||
};
|
||||
|
||||
@@ -189,7 +195,7 @@ protected:
|
||||
private:
|
||||
UI::EventReturn OnMapButton(UI::EventParams &e);
|
||||
UI::EventReturn OnBindAll(UI::EventParams &e);
|
||||
void HandleKeyMapping(InputMapping key);
|
||||
void HandleKeyMapping(KeyMap::MultiInputMapping key);
|
||||
void MapNext(bool successive);
|
||||
|
||||
MockPSP *psp_ = nullptr;
|
||||
|
||||
@@ -244,8 +244,6 @@ int DinputDevice::UpdateState() {
|
||||
AxisInput axis;
|
||||
axis.deviceId = DEVICE_ID_PAD_0 + pDevNum;
|
||||
|
||||
auto axesToSquare = KeyMap::MappedAxesForDevice(axis.deviceId);
|
||||
|
||||
SendNativeAxis(DEVICE_ID_PAD_0 + pDevNum, js.lX, last_lX_, JOYSTICK_AXIS_X);
|
||||
SendNativeAxis(DEVICE_ID_PAD_0 + pDevNum, js.lY, last_lY_, JOYSTICK_AXIS_Y);
|
||||
SendNativeAxis(DEVICE_ID_PAD_0 + pDevNum, js.lZ, last_lZ_, JOYSTICK_AXIS_Z);
|
||||
|
||||
@@ -170,8 +170,8 @@ namespace MainWindow {
|
||||
}
|
||||
|
||||
void DoTranslateMenus(HWND hWnd, HMENU menu) {
|
||||
auto useDefHotkey = [](int virtkey) {
|
||||
return KeyMap::g_controllerMap[virtkey].empty();
|
||||
auto useDefHotkey = [](int virtKey) {
|
||||
return !KeyMap::PspButtonHasMappings(virtKey);
|
||||
};
|
||||
|
||||
TranslateMenuItem(menu, ID_FILE_MENU);
|
||||
@@ -536,8 +536,7 @@ namespace MainWindow {
|
||||
|
||||
case ID_FILE_SAVESTATE_NEXT_SLOT_HC:
|
||||
{
|
||||
if (KeyMap::g_controllerMap[VIRTKEY_NEXT_SLOT].empty())
|
||||
{
|
||||
if (!KeyMap::PspButtonHasMappings(VIRTKEY_NEXT_SLOT)) {
|
||||
SaveState::NextSlot();
|
||||
NativeMessageReceived("savestate_displayslot", "");
|
||||
}
|
||||
@@ -559,7 +558,7 @@ namespace MainWindow {
|
||||
|
||||
case ID_FILE_QUICKLOADSTATE_HC:
|
||||
{
|
||||
if (KeyMap::g_controllerMap[VIRTKEY_LOAD_STATE].empty())
|
||||
if (!KeyMap::PspButtonHasMappings(VIRTKEY_LOAD_STATE))
|
||||
{
|
||||
SetCursor(LoadCursor(0, IDC_WAIT));
|
||||
SaveState::LoadSlot(PSP_CoreParameter().fileToStart, g_Config.iCurrentStateSlot, SaveStateActionFinished);
|
||||
@@ -575,7 +574,7 @@ namespace MainWindow {
|
||||
|
||||
case ID_FILE_QUICKSAVESTATE_HC:
|
||||
{
|
||||
if (KeyMap::g_controllerMap[VIRTKEY_SAVE_STATE].empty())
|
||||
if (!KeyMap::PspButtonHasMappings(VIRTKEY_SAVE_STATE))
|
||||
{
|
||||
SetCursor(LoadCursor(0, IDC_WAIT));
|
||||
SaveState::SaveSlot(PSP_CoreParameter().fileToStart, g_Config.iCurrentStateSlot, SaveStateActionFinished);
|
||||
|
||||
@@ -62,6 +62,7 @@
|
||||
#include "Common/File/VFS/DirectoryReader.h"
|
||||
#include "Core/FileSystems/ISOFileSystem.h"
|
||||
#include "Core/MemMap.h"
|
||||
#include "Core/KeyMap.h"
|
||||
#include "Core/MIPS/MIPSVFPUUtils.h"
|
||||
#include "GPU/Common/TextureDecoder.h"
|
||||
#include "GPU/Common/GPUStateUtils.h"
|
||||
@@ -849,6 +850,47 @@ static bool TestDepthMath() {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TestInputMapping() {
|
||||
InputMapping mapping;
|
||||
mapping.deviceId = 10;
|
||||
mapping.keyCode = 20;
|
||||
InputMapping mapping2;
|
||||
mapping2.deviceId = 18;
|
||||
mapping2.keyCode = 38;
|
||||
std::string cfg = mapping.ToConfigString();
|
||||
|
||||
InputMapping parsedMapping = InputMapping::FromConfigString(cfg);
|
||||
EXPECT_EQ_INT(parsedMapping.deviceId, mapping.deviceId);
|
||||
EXPECT_EQ_INT(parsedMapping.keyCode, mapping.keyCode);
|
||||
|
||||
using KeyMap::MultiInputMapping;
|
||||
MultiInputMapping multi(mapping);
|
||||
|
||||
EXPECT_EQ_STR(multi.ToConfigString(), mapping.ToConfigString());
|
||||
|
||||
multi.mappings.push_back(mapping2);
|
||||
EXPECT_FALSE(multi.EqualsSingleMapping(mapping));
|
||||
EXPECT_TRUE(multi.mappings.contains(mapping2));
|
||||
EXPECT_TRUE(multi.mappings.contains(mapping));
|
||||
|
||||
std::string cfgMulti = multi.ToConfigString();
|
||||
|
||||
EXPECT_EQ_STR(cfgMulti, std::string("10-20:18-38"));
|
||||
|
||||
MultiInputMapping parsedMulti = MultiInputMapping::FromConfigString(cfgMulti);
|
||||
|
||||
EXPECT_EQ_INT((int)parsedMulti.mappings.size(), 2);
|
||||
|
||||
// OK, both single and multiple mappings parse. Let's now see if the old parsing can handle a multimapping.
|
||||
// This is a requirement for the new format.
|
||||
|
||||
InputMapping parsedMultiSingle = InputMapping::FromConfigString(cfgMulti); // yes this is an intentional mismatch
|
||||
// We should get the first mapping.
|
||||
EXPECT_TRUE(parsedMultiSingle == mapping);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
typedef bool (*TestFunc)();
|
||||
struct TestItem {
|
||||
const char *name;
|
||||
@@ -901,6 +943,7 @@ TestItem availableTests[] = {
|
||||
TEST_ITEM(TinySet),
|
||||
TEST_ITEM(SmallDataConvert),
|
||||
TEST_ITEM(DepthMath),
|
||||
TEST_ITEM(InputMapping),
|
||||
};
|
||||
|
||||
int main(int argc, const char *argv[]) {
|
||||
|
||||
Reference in New Issue
Block a user