CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
Path: blob/master/Core/KeyMap.cpp
Views: 1401
// Copyright (c) 2013- PPSSPP Project.12// This program is free software: you can redistribute it and/or modify3// it under the terms of the GNU General Public License as published by4// the Free Software Foundation, version 2.0 or later versions.56// This program is distributed in the hope that it will be useful,7// but WITHOUT ANY WARRANTY; without even the implied warranty of8// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the9// GNU General Public License 2.0 for more details.1011// A copy of the GPL 2.0 should have been included with the program.12// If not, see http://www.gnu.org/licenses/1314// Official git repository and contact information can be found at15// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.1617#include <algorithm>18#include <set>19#include <unordered_map>20#include <mutex>2122#include "ppsspp_config.h"2324#include "Common/System/NativeApp.h"25#include "Common/System/System.h"26#include "Common/Data/Format/IniFile.h"27#include "Common/Input/InputState.h"28#include "Common/VR/PPSSPPVR.h"29#include "Common/Log.h"30#include "Common/StringUtils.h"31#include "Core/HLE/sceUtility.h"32#include "Core/HLE/sceCtrl.h" // psp keys33#include "Core/Config.h"34#include "Core/KeyMap.h"35#include "Core/KeyMapDefaults.h"3637namespace KeyMap {3839// We actually need to lock g_controllerMap since it can be modified! Crashes will probably be rare though,40// but I've seen one. Let's just protect it with a mutex.41std::recursive_mutex g_controllerMapLock;42KeyMapping g_controllerMap;4344// Incremented on modification, so we know when to update menus.45int g_controllerMapGeneration = 0;46std::set<std::string> g_seenPads;47std::map<InputDeviceID, std::string> g_padNames;48std::set<InputDeviceID> g_seenDeviceIds;4950AxisType GetAxisType(InputAxis input) {51switch (input) {52case JOYSTICK_AXIS_GAS:53case JOYSTICK_AXIS_BRAKE:54case JOYSTICK_AXIS_LTRIGGER:55case JOYSTICK_AXIS_RTRIGGER:56return AxisType::TRIGGER;57case JOYSTICK_AXIS_X:58case JOYSTICK_AXIS_Y:59case JOYSTICK_AXIS_Z:60case JOYSTICK_AXIS_RX:61case JOYSTICK_AXIS_RY:62case JOYSTICK_AXIS_RZ:63return AxisType::STICK;64default:65return AxisType::OTHER;66}67}6869// Utility for UI navigation70void SingleInputMappingFromPspButton(int btn, std::vector<InputMapping> *mappings, bool ignoreMouse) {71std::vector<MultiInputMapping> multiMappings;72InputMappingsFromPspButton(btn, &multiMappings, ignoreMouse);73mappings->clear();74for (auto &mapping : multiMappings) {75if (!mapping.empty()) {76mappings->push_back(mapping.mappings[0]);77} else {78WARN_LOG(Log::Common, "Encountered empty mapping in multi-mapping for button %d", btn);79}80}81}8283// TODO: This is such a mess...84void UpdateNativeMenuKeys() {85std::vector<InputMapping> confirmKeys, cancelKeys;86std::vector<InputMapping> tabLeft, tabRight;87std::vector<InputMapping> upKeys, downKeys, leftKeys, rightKeys;88std::vector<InputMapping> infoKeys;8990// Mouse mapping might be problematic in UI, so let's ignore mouse for UI9192int confirmKey = g_Config.iButtonPreference == PSP_SYSTEMPARAM_BUTTON_CROSS ? CTRL_CROSS : CTRL_CIRCLE;93int cancelKey = g_Config.iButtonPreference == PSP_SYSTEMPARAM_BUTTON_CROSS ? CTRL_CIRCLE : CTRL_CROSS;9495SingleInputMappingFromPspButton(confirmKey, &confirmKeys, true);96SingleInputMappingFromPspButton(cancelKey, &cancelKeys, true);97SingleInputMappingFromPspButton(CTRL_TRIANGLE, &infoKeys, true);98SingleInputMappingFromPspButton(CTRL_LTRIGGER, &tabLeft, true);99SingleInputMappingFromPspButton(CTRL_RTRIGGER, &tabRight, true);100SingleInputMappingFromPspButton(CTRL_UP, &upKeys, true);101SingleInputMappingFromPspButton(CTRL_DOWN, &downKeys, true);102SingleInputMappingFromPspButton(CTRL_LEFT, &leftKeys, true);103SingleInputMappingFromPspButton(CTRL_RIGHT, &rightKeys, true);104105#ifdef __ANDROID__106// Hardcode DPAD on Android107upKeys.push_back(InputMapping(DEVICE_ID_ANY, NKCODE_DPAD_UP));108downKeys.push_back(InputMapping(DEVICE_ID_ANY, NKCODE_DPAD_DOWN));109leftKeys.push_back(InputMapping(DEVICE_ID_ANY, NKCODE_DPAD_LEFT));110rightKeys.push_back(InputMapping(DEVICE_ID_ANY, NKCODE_DPAD_RIGHT));111#endif112113// Push several hard-coded keys before submitting to native.114const InputMapping hardcodedConfirmKeys[] = {115InputMapping(DEVICE_ID_KEYBOARD, NKCODE_SPACE),116InputMapping(DEVICE_ID_KEYBOARD, NKCODE_ENTER),117InputMapping(DEVICE_ID_KEYBOARD, NKCODE_NUMPAD_ENTER),118InputMapping(DEVICE_ID_ANY, NKCODE_BUTTON_A),119InputMapping(DEVICE_ID_PAD_0, NKCODE_DPAD_CENTER), // A number of Android devices.120};121122// If they're not already bound, add them in.123for (size_t i = 0; i < ARRAY_SIZE(hardcodedConfirmKeys); i++) {124if (std::find(confirmKeys.begin(), confirmKeys.end(), hardcodedConfirmKeys[i]) == confirmKeys.end())125confirmKeys.push_back(hardcodedConfirmKeys[i]);126}127128const InputMapping hardcodedCancelKeys[] = {129InputMapping(DEVICE_ID_KEYBOARD, NKCODE_ESCAPE),130InputMapping(DEVICE_ID_ANY, NKCODE_BACK),131InputMapping(DEVICE_ID_ANY, NKCODE_BUTTON_B),132InputMapping(DEVICE_ID_MOUSE, NKCODE_EXT_MOUSEBUTTON_4),133};134135for (size_t i = 0; i < ARRAY_SIZE(hardcodedCancelKeys); i++) {136if (std::find(cancelKeys.begin(), cancelKeys.end(), hardcodedCancelKeys[i]) == cancelKeys.end())137cancelKeys.push_back(hardcodedCancelKeys[i]);138}139140const InputMapping hardcodedInfoKeys[] = {141InputMapping(DEVICE_ID_KEYBOARD, NKCODE_S),142InputMapping(DEVICE_ID_KEYBOARD, NKCODE_NUMPAD_ADD),143InputMapping(DEVICE_ID_PAD_0, NKCODE_BUTTON_Y), // Also triangle144};145146for (size_t i = 0; i < ARRAY_SIZE(hardcodedInfoKeys); i++) {147if (std::find(infoKeys.begin(), infoKeys.end(), hardcodedInfoKeys[i]) == infoKeys.end())148infoKeys.push_back(hardcodedInfoKeys[i]);149}150151SetDPadKeys(upKeys, downKeys, leftKeys, rightKeys);152SetConfirmCancelKeys(confirmKeys, cancelKeys);153SetTabLeftRightKeys(tabLeft, tabRight);154SetInfoKeys(infoKeys);155156std::unordered_map<InputDeviceID, int> flipYByDeviceId;157for (InputDeviceID deviceId : g_seenDeviceIds) {158auto analogs = MappedAxesForDevice(deviceId);159flipYByDeviceId[deviceId] = analogs.leftY.direction;160}161SetAnalogFlipY(flipYByDeviceId);162}163164static const KeyMap_IntStrPair key_names[] = {165{NKCODE_A, "A"},166{NKCODE_B, "B"},167{NKCODE_C, "C"},168{NKCODE_D, "D"},169{NKCODE_E, "E"},170{NKCODE_F, "F"},171{NKCODE_G, "G"},172{NKCODE_H, "H"},173{NKCODE_I, "I"},174{NKCODE_J, "J"},175{NKCODE_K, "K"},176{NKCODE_L, "L"},177{NKCODE_M, "M"},178{NKCODE_N, "N"},179{NKCODE_O, "O"},180{NKCODE_P, "P"},181{NKCODE_Q, "Q"},182{NKCODE_R, "R"},183{NKCODE_S, "S"},184{NKCODE_T, "T"},185{NKCODE_U, "U"},186{NKCODE_V, "V"},187{NKCODE_W, "W"},188{NKCODE_X, "X"},189{NKCODE_Y, "Y"},190{NKCODE_Z, "Z"},191192{NKCODE_0, "0"},193{NKCODE_1, "1"},194{NKCODE_2, "2"},195{NKCODE_3, "3"},196{NKCODE_4, "4"},197{NKCODE_5, "5"},198{NKCODE_6, "6"},199{NKCODE_7, "7"},200{NKCODE_8, "8"},201{NKCODE_9, "9"},202203{NKCODE_F1, "F1"},204{NKCODE_F2, "F2"},205{NKCODE_F3, "F3"},206{NKCODE_F4, "F4"},207{NKCODE_F5, "F5"},208{NKCODE_F6, "F6"},209{NKCODE_F7, "F7"},210{NKCODE_F8, "F8"},211{NKCODE_F9, "F9"},212{NKCODE_F10, "F10"},213{NKCODE_F11, "F11"},214{NKCODE_F12, "F12"},215216{NKCODE_GRAVE, "`"},217{NKCODE_SLASH, "/"},218{NKCODE_BACKSLASH, "\\"},219{NKCODE_SEMICOLON, ";"},220{NKCODE_COMMA, ","},221{NKCODE_PERIOD, "."},222{NKCODE_LEFT_BRACKET, "["},223{NKCODE_RIGHT_BRACKET, "]"},224{NKCODE_APOSTROPHE, "'"},225{NKCODE_MINUS, "-"},226{NKCODE_PLUS, "+"},227{NKCODE_SYSRQ, "Print"},228{NKCODE_SCROLL_LOCK, "ScrLock"},229{NKCODE_BREAK, "Pause"},230231{NKCODE_BACK, "Back"},232{NKCODE_TAB, "Tab"},233{NKCODE_ENTER, "Enter"},234{NKCODE_SHIFT_LEFT, "LShift"},235{NKCODE_SHIFT_RIGHT, "RShift"},236{NKCODE_CTRL_LEFT, "LCtrl"},237{NKCODE_CTRL_RIGHT, "RCtrl"},238{NKCODE_ALT_LEFT, "LAlt"},239{NKCODE_ALT_RIGHT, "RAlt"},240{NKCODE_SPACE, "Space"},241{NKCODE_WINDOW, "Windows"},242{NKCODE_DEL, "Backspace"},243{NKCODE_FORWARD_DEL, "Delete"},244{NKCODE_MOVE_HOME, "Home"},245{NKCODE_MOVE_END, "End"},246{NKCODE_ESCAPE, "Esc"},247{NKCODE_CAPS_LOCK, "CapsLock"},248249{NKCODE_VOLUME_UP, "Vol +"},250{NKCODE_VOLUME_DOWN, "Vol -"},251{NKCODE_HOME, "Home"},252{NKCODE_INSERT, "Ins"},253{NKCODE_PAGE_UP, "PgUp"},254{NKCODE_PAGE_DOWN, "PgDn"},255{NKCODE_CLEAR, "Clear"}, // 5 when numlock off256{NKCODE_CALL, "Call"},257{NKCODE_ENDCALL, "End Call"},258259{NKCODE_DPAD_LEFT, "Left"},260{NKCODE_DPAD_UP, "Up"},261{NKCODE_DPAD_RIGHT, "Right"},262{NKCODE_DPAD_DOWN, "Down"},263264{NKCODE_BUTTON_L1, "L1"},265{NKCODE_BUTTON_L2, "L2"},266{NKCODE_BUTTON_R1, "R1"},267{NKCODE_BUTTON_R2, "R2"},268269{NKCODE_BUTTON_A, "[A]"},270{NKCODE_BUTTON_B, "[B]"},271{NKCODE_BUTTON_C, "[C]"},272{NKCODE_BUTTON_X, "[X]"},273{NKCODE_BUTTON_Y, "[Y]"},274{NKCODE_BUTTON_Z, "[Z]"},275{NKCODE_BUTTON_1, "b1"},276{NKCODE_BUTTON_2, "b2"},277{NKCODE_BUTTON_3, "b3"},278{NKCODE_BUTTON_4, "b4"},279{NKCODE_BUTTON_5, "b5"},280{NKCODE_BUTTON_6, "b6"},281{NKCODE_BUTTON_7, "b7"},282{NKCODE_BUTTON_8, "b8"},283{NKCODE_BUTTON_9, "b9"},284{NKCODE_BUTTON_10, "b10"},285{NKCODE_BUTTON_11, "b11"},286{NKCODE_BUTTON_12, "b12"},287{NKCODE_BUTTON_13, "b13"},288{NKCODE_BUTTON_14, "b14"},289{NKCODE_BUTTON_15, "b15"},290{NKCODE_BUTTON_16, "b16"},291{NKCODE_BUTTON_START, "Start"},292{NKCODE_BUTTON_SELECT, "Select"},293{NKCODE_BUTTON_CIRCLE, "Circle"},294{NKCODE_BUTTON_CIRCLE_PS3, "Circle3"},295{NKCODE_BUTTON_CROSS, "Cross"},296{NKCODE_BUTTON_CROSS_PS3, "Cross3"},297{NKCODE_BUTTON_TRIANGLE, "Triangle"},298{NKCODE_BUTTON_SQUARE, "Square"},299{NKCODE_BUTTON_THUMBL, "ThumbL"},300{NKCODE_BUTTON_THUMBR, "ThumbR"},301{NKCODE_BUTTON_MODE, "Mode"},302303{NKCODE_EXT_PIPE, "|"},304{NKCODE_NUMPAD_DIVIDE, "Num/"},305{NKCODE_NUMPAD_MULTIPLY, "Num*"},306{NKCODE_NUMPAD_ADD, "Num+"},307{NKCODE_NUMPAD_SUBTRACT, "Num-"},308{NKCODE_NUMPAD_DOT, "Num."},309{NKCODE_NUMPAD_COMMA, "Num,"},310{NKCODE_NUMPAD_ENTER, "NumEnter"},311{NKCODE_NUMPAD_EQUALS, "Num="},312{NKCODE_NUMPAD_LEFT_PAREN, "Num("},313{NKCODE_NUMPAD_RIGHT_PAREN, "Num)"},314{NKCODE_NUMPAD_0, "Num0"},315{NKCODE_NUMPAD_1, "Num1"},316{NKCODE_NUMPAD_2, "Num2"},317{NKCODE_NUMPAD_3, "Num3"},318{NKCODE_NUMPAD_4, "Num4"},319{NKCODE_NUMPAD_5, "Num5"},320{NKCODE_NUMPAD_6, "Num6"},321{NKCODE_NUMPAD_7, "Num7"},322{NKCODE_NUMPAD_8, "Num8"},323{NKCODE_NUMPAD_9, "Num9"},324325{NKCODE_LANGUAGE_SWITCH, "Language"},326{NKCODE_MANNER_MODE, "Manner"},327{NKCODE_3D_MODE, "3D Mode"},328{NKCODE_CONTACTS, "Contacts"},329{NKCODE_CALENDAR, "Calendar"},330{NKCODE_MUSIC, "Music"},331{NKCODE_CALCULATOR, "Calc"},332{NKCODE_ZENKAKU_HANKAKU, "Zenkaku"},333{NKCODE_EISU, "Eisu"},334{NKCODE_MUHENKAN, "Muhenkan"},335{NKCODE_HENKAN, "Henkan"},336{NKCODE_KATAKANA_HIRAGANA, "Katakana"},337{NKCODE_YEN, "Yen"},338{NKCODE_RO, "Ro"},339{NKCODE_KANA, "Kana"},340{NKCODE_ASSIST, "Assist"},341342{NKCODE_EXT_MOUSEBUTTON_1, "MB1"},343{NKCODE_EXT_MOUSEBUTTON_2, "MB2"},344{NKCODE_EXT_MOUSEBUTTON_3, "MB3"},345{NKCODE_EXT_MOUSEBUTTON_4, "MB4"},346{NKCODE_EXT_MOUSEBUTTON_5, "MB5"},347{NKCODE_EXT_MOUSEWHEEL_UP, "MWheelU"},348{NKCODE_EXT_MOUSEWHEEL_DOWN, "MWheelD"},349350{NKCODE_START_QUESTION, "¿"},351{NKCODE_LEFTBRACE, "{"},352{NKCODE_RIGHTBRACE, "}"},353354{NKCODE_GUIDE, "Guide"},355{NKCODE_INFO, "Info"},356};357358static const KeyMap_IntStrPair axis_names[] = {359{JOYSTICK_AXIS_X, "X Axis"},360{JOYSTICK_AXIS_Y, "Y Axis"},361{JOYSTICK_AXIS_PRESSURE, "Pressure"},362{JOYSTICK_AXIS_SIZE, "Size"},363{JOYSTICK_AXIS_TOUCH_MAJOR, "Touch Major"},364{JOYSTICK_AXIS_TOUCH_MINOR, "Touch Minor"},365{JOYSTICK_AXIS_TOOL_MAJOR, "Tool Major"},366{JOYSTICK_AXIS_TOOL_MINOR, "Tool Minor"},367{JOYSTICK_AXIS_ORIENTATION, "Orient"},368{JOYSTICK_AXIS_VSCROLL, "Vert Scroll"},369{JOYSTICK_AXIS_HSCROLL, "Horiz Scroll"},370{JOYSTICK_AXIS_Z, "Z Axis"}, // Also used as second stick X on many controllers - rename?371{JOYSTICK_AXIS_RX, "X Rotation"},372{JOYSTICK_AXIS_RY, "Y Rotation"},373{JOYSTICK_AXIS_RZ, "Z Rotation"}, // Also used as second stick Y on many controllers - rename?374{JOYSTICK_AXIS_HAT_X, "X HAT"},375{JOYSTICK_AXIS_HAT_Y, "Y HAT"},376{JOYSTICK_AXIS_LTRIGGER, "TriggerL"},377{JOYSTICK_AXIS_RTRIGGER, "TriggerR"},378{JOYSTICK_AXIS_THROTTLE, "Throttle"},379{JOYSTICK_AXIS_RUDDER, "Rudder"},380{JOYSTICK_AXIS_WHEEL, "Wheel"},381{JOYSTICK_AXIS_GAS, "Gas"},382{JOYSTICK_AXIS_BRAKE, "Brake"},383{JOYSTICK_AXIS_DISTANCE, "Distance"},384{JOYSTICK_AXIS_TILT, "Tilt"},385{JOYSTICK_AXIS_MOUSE_REL_X, "MouseDX"},386{JOYSTICK_AXIS_MOUSE_REL_Y, "MouseDY"},387{JOYSTICK_AXIS_ACCELEROMETER_X, "AccelX"},388{JOYSTICK_AXIS_ACCELEROMETER_Y, "AccelY"},389{JOYSTICK_AXIS_ACCELEROMETER_Z, "AccelZ"},390};391392static std::string unknown_key_name = "??";393394const KeyMap_IntStrPair psp_button_names[] = {395{CTRL_UP, "Up"},396{CTRL_DOWN, "Down"},397{CTRL_LEFT, "Left"},398{CTRL_RIGHT, "Right"},399{CTRL_CIRCLE, "Circle"},400{CTRL_CROSS, "Cross"},401{CTRL_SQUARE, "Square"},402{CTRL_TRIANGLE, "Triangle"},403{CTRL_START, "Start"},404{CTRL_SELECT, "Select"},405{CTRL_LTRIGGER, "L"},406{CTRL_RTRIGGER, "R"},407408{VIRTKEY_AXIS_Y_MAX, "An.Up"},409{VIRTKEY_AXIS_Y_MIN, "An.Down"},410{VIRTKEY_AXIS_X_MIN, "An.Left"},411{VIRTKEY_AXIS_X_MAX, "An.Right"},412413{VIRTKEY_ANALOG_ROTATE_CW, "Rotate Analog (CW)"},414{VIRTKEY_ANALOG_ROTATE_CCW, "Rotate Analog (CCW)"},415{VIRTKEY_ANALOG_LIGHTLY, "Analog limiter"},416{VIRTKEY_RAPID_FIRE, "RapidFire"},417{VIRTKEY_AXIS_SWAP, "AxisSwap"},418419{VIRTKEY_FASTFORWARD, "Fast-forward"},420{VIRTKEY_SPEED_TOGGLE, "SpeedToggle"},421{VIRTKEY_SPEED_CUSTOM1, "Alt speed 1"},422{VIRTKEY_SPEED_CUSTOM2, "Alt speed 2"},423{VIRTKEY_SPEED_ANALOG, "Analog speed"},424{VIRTKEY_PAUSE, "Pause"},425{VIRTKEY_RESET_EMULATION, "Reset"},426{VIRTKEY_FRAME_ADVANCE, "Frame Advance"},427#if !defined(MOBILE_DEVICE)428{VIRTKEY_RECORD, "Audio/Video Recording" },429#endif430{VIRTKEY_REWIND, "Rewind"},431{VIRTKEY_SAVE_STATE, "Save State"},432{VIRTKEY_LOAD_STATE, "Load State"},433{VIRTKEY_PREVIOUS_SLOT, "Previous Slot"},434{VIRTKEY_NEXT_SLOT, "Next Slot"},435#if !defined(MOBILE_DEVICE)436{VIRTKEY_TOGGLE_FULLSCREEN, "Toggle Fullscreen"},437#endif438439{VIRTKEY_OPENCHAT, "OpenChat" },440441{VIRTKEY_DEVMENU, "DevMenu"},442{VIRTKEY_TEXTURE_DUMP, "Texture Dumping"},443{VIRTKEY_TEXTURE_REPLACE, "Texture Replacement"},444{VIRTKEY_SCREENSHOT, "Screenshot"},445{VIRTKEY_MUTE_TOGGLE, "Mute toggle"},446447#ifdef OPENXR448{VIRTKEY_VR_CAMERA_ADJUST, "VR camera adjust"},449{VIRTKEY_VR_CAMERA_RESET, "VR camera reset"},450#else451{VIRTKEY_SCREEN_ROTATION_VERTICAL, "Display Portrait"},452{VIRTKEY_SCREEN_ROTATION_VERTICAL180, "Display Portrait Reversed"},453{VIRTKEY_SCREEN_ROTATION_HORIZONTAL, "Display Landscape"},454{VIRTKEY_SCREEN_ROTATION_HORIZONTAL180, "Display Landscape Reversed"},455#endif456457{VIRTKEY_TOGGLE_WLAN, "Toggle WLAN"},458{VIRTKEY_EXIT_APP, "Exit App"},459460{VIRTKEY_TOGGLE_MOUSE, "Toggle mouse input"},461{VIRTKEY_TOGGLE_TOUCH_CONTROLS, "Toggle touch controls"},462463{VIRTKEY_AXIS_RIGHT_Y_MAX, "RightAn.Up"},464{VIRTKEY_AXIS_RIGHT_Y_MIN, "RightAn.Down"},465{VIRTKEY_AXIS_RIGHT_X_MIN, "RightAn.Left"},466{VIRTKEY_AXIS_RIGHT_X_MAX, "RightAn.Right"},467468{CTRL_HOME, "Home"},469{CTRL_HOLD, "Hold"},470{CTRL_WLAN, "Wlan"},471{CTRL_REMOTE_HOLD, "Remote hold"},472{CTRL_VOL_UP, "Vol +"},473{CTRL_VOL_DOWN, "Vol -"},474{CTRL_SCREEN, "Screen"},475{CTRL_NOTE, "Note"},476{CTRL_L2, "Dev-kit L2"},477{CTRL_L3, "Dev-kit L3"},478{CTRL_R2, "Dev-kit R2"},479{CTRL_R3, "Dev-kit R3"},480};481482// key here can be other things than InputKeyCode.483static std::string FindName(int key, const KeyMap_IntStrPair list[], size_t size) {484for (size_t i = 0; i < size; i++) {485if (list[i].key == key)486return list[i].name;487}488return StringFromFormat("%02x?", key);489}490491std::string GetKeyName(InputKeyCode keyCode) {492return FindName(keyCode, key_names, ARRAY_SIZE(key_names));493}494495std::string GetKeyOrAxisName(const InputMapping &mapping) {496if (mapping.IsAxis()) {497int direction;498int axis = mapping.Axis(&direction);499std::string temp = GetAxisName(axis);500if (direction == 1)501temp += "+";502else if (direction == -1)503temp += "-";504return temp;505} else {506return FindName(mapping.keyCode, key_names, ARRAY_SIZE(key_names));507}508}509510std::string GetAxisName(int axisId) {511return FindName(axisId, axis_names, ARRAY_SIZE(axis_names));512}513514std::string GetPspButtonName(int btn) {515return FindName(btn, psp_button_names, ARRAY_SIZE(psp_button_names));516}517518const char* GetPspButtonNameCharPointer(int btn) {519for (size_t i = 0; i < ARRAY_SIZE(psp_button_names); i++)520if (psp_button_names[i].key == btn)521return psp_button_names[i].name;522return nullptr;523}524525const KeyMap::KeyMap_IntStrPair *GetMappableKeys(size_t *count) {526*count = ARRAY_SIZE(psp_button_names);527return psp_button_names;528}529530bool InputMappingToPspButton(const InputMapping &mapping, std::vector<int> *pspButtons) {531bool found = false;532std::lock_guard<std::recursive_mutex> guard(g_controllerMapLock);533for (auto iter = g_controllerMap.begin(); iter != g_controllerMap.end(); ++iter) {534for (auto iter2 = iter->second.begin(); iter2 != iter->second.end(); ++iter2) {535if (iter2->EqualsSingleMapping(mapping)) {536if (pspButtons)537pspButtons->push_back(iter->first);538found = true;539}540}541}542return found;543}544545// This is the main workhorse of the ControlMapper.546bool InputMappingsFromPspButtonNoLock(int btn, std::vector<MultiInputMapping> *mappings, bool ignoreMouse) {547auto iter = g_controllerMap.find(btn);548if (iter == g_controllerMap.end()) {549return false;550}551bool mapped = false;552if (mappings) {553mappings->clear();554}555for (auto &iter2 : iter->second) {556bool ignore = ignoreMouse && iter2.HasMouse();557if (!ignore) {558mapped = true;559if (mappings) {560mappings->push_back(iter2);561}562}563}564return mapped;565}566567bool InputMappingsFromPspButton(int btn, std::vector<MultiInputMapping> *mappings, bool ignoreMouse) {568std::lock_guard<std::recursive_mutex> guard(g_controllerMapLock);569return InputMappingsFromPspButtonNoLock(btn, mappings, ignoreMouse);570}571572void LockMappings() {573g_controllerMapLock.lock();574}575576void UnlockMappings() {577g_controllerMapLock.unlock();578}579580bool PspButtonHasMappings(int btn) {581std::lock_guard<std::recursive_mutex> guard(g_controllerMapLock);582auto iter = g_controllerMap.find(btn);583if (iter == g_controllerMap.end()) {584return false;585}586return !iter->second.empty();587}588589MappedAnalogAxes MappedAxesForDevice(InputDeviceID deviceId) {590// Find the axisId mapped for a specific virtual button.591auto findAxisId = [&](int btn) -> MappedAnalogAxis {592MappedAnalogAxis info{ -1 };593for (const auto &key : g_controllerMap[btn]) {594// Only consider single mappings, combos don't make much sense for these.595if (key.mappings.empty()) continue;596auto &mapping = key.mappings[0];597if (mapping.deviceId == deviceId) {598info.axisId = TranslateKeyCodeToAxis(mapping.keyCode, &info.direction);599return info;600}601}602return info;603};604605// Find the axisId of a pair of opposing buttons.606auto findAxisIdPair = [&](int minBtn, int maxBtn) -> MappedAnalogAxis {607MappedAnalogAxis foundMin = findAxisId(minBtn);608MappedAnalogAxis foundMax = findAxisId(maxBtn);609if (foundMin.axisId == foundMax.axisId) {610return foundMax;611}612return MappedAnalogAxis{ -1 };613};614615MappedAnalogAxes result;616std::lock_guard<std::recursive_mutex> guard(g_controllerMapLock);617result.leftX = findAxisIdPair(VIRTKEY_AXIS_X_MIN, VIRTKEY_AXIS_X_MAX);618result.leftY = findAxisIdPair(VIRTKEY_AXIS_Y_MIN, VIRTKEY_AXIS_Y_MAX);619result.rightX = findAxisIdPair(VIRTKEY_AXIS_RIGHT_X_MIN, VIRTKEY_AXIS_RIGHT_X_MAX);620result.rightY = findAxisIdPair(VIRTKEY_AXIS_RIGHT_Y_MIN, VIRTKEY_AXIS_RIGHT_Y_MAX);621return result;622}623624void RemoveButtonMapping(int btn) {625std::lock_guard<std::recursive_mutex> guard(g_controllerMapLock);626for (auto iter = g_controllerMap.begin(); iter != g_controllerMap.end(); ++iter) {627if (iter->first == btn) {628g_controllerMap.erase(iter);629return;630}631}632}633634bool IsKeyMapped(InputDeviceID device, int key) {635std::lock_guard<std::recursive_mutex> guard(g_controllerMapLock);636for (auto &iter : g_controllerMap) {637for (auto &mappedKey : iter.second) {638if (mappedKey.mappings.contains(InputMapping(device, key))) {639return true;640}641}642}643return false;644}645646bool ReplaceSingleKeyMapping(int btn, int index, const MultiInputMapping &key) {647std::lock_guard<std::recursive_mutex> guard(g_controllerMapLock);648// Check for duplicate649for (int i = 0; i < (int)g_controllerMap[btn].size(); ++i) {650if (i != index && g_controllerMap[btn][i] == key) {651g_controllerMap[btn].erase(g_controllerMap[btn].begin()+index);652g_controllerMapGeneration++;653654UpdateNativeMenuKeys();655return false;656}657}658659if (key.empty()) {660return false;661}662663KeyMap::g_controllerMap[btn][index] = key;664g_controllerMapGeneration++;665666for (auto &mapping : key.mappings) {667g_seenDeviceIds.insert(mapping.deviceId);668}669UpdateNativeMenuKeys();670return true;671}672673void DeleteNthMapping(int key, int number) {674std::lock_guard<std::recursive_mutex> guard(g_controllerMapLock);675auto iter = g_controllerMap.find(key);676if (iter != g_controllerMap.end()) {677if (number < iter->second.size()) {678iter->second.erase(iter->second.begin() + number);679g_controllerMapGeneration++;680}681}682}683684void SetInputMapping(int btn, const MultiInputMapping &key, bool replace) {685std::lock_guard<std::recursive_mutex> guard(g_controllerMapLock);686if (key.empty()) {687g_controllerMap.erase(btn);688return;689}690if (replace) {691RemoveButtonMapping(btn);692g_controllerMap[btn].clear();693g_controllerMap[btn].push_back(key);694} else {695for (auto iter = g_controllerMap[btn].begin(); iter != g_controllerMap[btn].end(); ++iter) {696if (*iter == key)697return;698}699g_controllerMap[btn].push_back(key);700}701g_controllerMapGeneration++;702703for (auto &mapping : key.mappings) {704g_seenDeviceIds.insert(mapping.deviceId);705}706}707708void RestoreDefault() {709std::lock_guard<std::recursive_mutex> guard(g_controllerMapLock);710g_controllerMap.clear();711g_controllerMapGeneration++;712713if (IsVREnabled()) {714SetDefaultKeyMap(DEFAULT_MAPPING_VR_HEADSET, false);715return;716}717718#if PPSSPP_PLATFORM(WINDOWS)719SetDefaultKeyMap(DEFAULT_MAPPING_KEYBOARD, true);720SetDefaultKeyMap(DEFAULT_MAPPING_XINPUT, false);721SetDefaultKeyMap(DEFAULT_MAPPING_PAD, false);722#elif PPSSPP_PLATFORM(ANDROID)723// Autodetect a few common (and less common) devices724// Note that here we check the device name, not the controller name. We don't get725// the controller name until a button has been pressed so can't use it to set defaults.726std::string name = System_GetProperty(SYSPROP_NAME);727if (IsNvidiaShield(name)) {728SetDefaultKeyMap(DEFAULT_MAPPING_SHIELD, false);729} else if (IsOuya(name)) {730SetDefaultKeyMap(DEFAULT_MAPPING_OUYA, false);731} else if (IsXperiaPlay(name)) {732SetDefaultKeyMap(DEFAULT_MAPPING_XPERIA_PLAY, false);733} else if (IsMOQII7S(name)) {734SetDefaultKeyMap(DEFAULT_MAPPING_MOQI_I7S, false);735} else if (IsRetroid(name)) {736SetDefaultKeyMap(DEFAULT_MAPPING_RETRO_STATION_CONTROLLER, false);737} else {738SetDefaultKeyMap(DEFAULT_MAPPING_ANDROID_PAD, false);739}740#elif PPSSPP_PLATFORM(IOS)741SetDefaultKeyMap(DEFAULT_MAPPING_IOS_PAD, false);742#else743SetDefaultKeyMap(DEFAULT_MAPPING_KEYBOARD, true);744SetDefaultKeyMap(DEFAULT_MAPPING_PAD, false);745#endif746}747748// TODO: Make the ini format nicer.749void LoadFromIni(IniFile &file) {750RestoreDefault();751if (!file.HasSection("ControlMapping")) {752return;753}754755std::lock_guard<std::recursive_mutex> guard(g_controllerMapLock);756757Section *controls = file.GetOrCreateSection("ControlMapping");758for (size_t i = 0; i < ARRAY_SIZE(psp_button_names); i++) {759std::string value;760controls->Get(psp_button_names[i].name, &value, "");761762// Erase default mapping763g_controllerMap.erase(psp_button_names[i].key);764if (value.empty())765continue;766767std::vector<std::string> mappings;768SplitString(value, ',', mappings);769770for (size_t j = 0; j < mappings.size(); j++) {771MultiInputMapping input = MultiInputMapping::FromConfigString(mappings[j]);772if (input.empty()) {773continue; // eat empty mappings, however they arose, so they can't keep haunting us.774}775SetInputMapping(psp_button_names[i].key, input, false);776for (auto mapping : input.mappings) {777g_seenDeviceIds.insert(mapping.deviceId);778}779}780}781782UpdateNativeMenuKeys();783}784785void SaveToIni(IniFile &file) {786Section *controls = file.GetOrCreateSection("ControlMapping");787788std::lock_guard<std::recursive_mutex> guard(g_controllerMapLock);789790for (size_t i = 0; i < ARRAY_SIZE(psp_button_names); i++) {791std::vector<MultiInputMapping> keys;792InputMappingsFromPspButton(psp_button_names[i].key, &keys, false);793794std::string value;795for (size_t j = 0; j < keys.size(); j++) {796value += keys[j].ToConfigString();797if (j != keys.size() - 1)798value += ",";799}800801controls->Set(psp_button_names[i].name, value, "");802}803}804805void ClearAllMappings() {806std::lock_guard<std::recursive_mutex> guard(g_controllerMapLock);807g_controllerMap.clear();808g_controllerMapGeneration++;809}810811bool IsOuya(const std::string &name) {812return name == "OUYA:OUYA Console";813}814815bool IsNvidiaShield(const std::string &name) {816return name == "NVIDIA:SHIELD";817}818819bool IsRetroid(const std::string &name) {820// TODO: Not sure if there are differences between different Retroid devices.821// The one I have is a "Retroid Pocket 2+".822return startsWith(name, "Retroid:");823}824825bool IsNvidiaShieldTV(const std::string &name) {826return name == "NVIDIA:SHIELD Android TV";827}828829bool IsXperiaPlay(const std::string &name) {830return name == "Sony Ericsson:R800a" || name == "Sony Ericsson:R800i" || name == "Sony Ericsson:R800x" || name == "Sony Ericsson:R800at" || name == "Sony Ericsson:SO-01D" || name == "Sony Ericsson:zeus";831}832833bool IsMOQII7S(const std::string &name) {834return name == "MOQI:I7S";835}836837bool HasBuiltinController(const std::string &name) {838return IsOuya(name) || IsXperiaPlay(name) || IsNvidiaShield(name) || IsMOQII7S(name) || IsRetroid(name);839}840841void NotifyPadConnected(InputDeviceID deviceId, const std::string &name) {842std::lock_guard<std::recursive_mutex> guard(g_controllerMapLock);843g_seenPads.insert(name);844g_padNames[deviceId] = name;845}846847void AutoConfForPad(const std::string &name) {848std::lock_guard<std::recursive_mutex> guard(g_controllerMapLock);849g_controllerMap.clear();850851INFO_LOG(Log::System, "Autoconfiguring pad for '%s'", name.c_str());852853#if PPSSPP_PLATFORM(ANDROID)854if (name.find("Xbox") != std::string::npos) {855SetDefaultKeyMap(DEFAULT_MAPPING_ANDROID_XBOX, false);856} else if (name == "Retro Station Controller") {857SetDefaultKeyMap(DEFAULT_MAPPING_RETRO_STATION_CONTROLLER, false);858} else {859SetDefaultKeyMap(DEFAULT_MAPPING_ANDROID_PAD, false);860}861#else862#if PPSSPP_PLATFORM(WINDOWS)863const bool platformSupportsXinput = true;864#else865const bool platformSupportsXinput = false;866#endif867if (platformSupportsXinput && name.find("Xbox") != std::string::npos) {868SetDefaultKeyMap(DEFAULT_MAPPING_XINPUT, false);869} else {870SetDefaultKeyMap(DEFAULT_MAPPING_PAD, false);871}872#endif873874// Add a couple of convenient keyboard mappings by default, too.875g_controllerMap[VIRTKEY_PAUSE].push_back(MultiInputMapping(InputMapping(DEVICE_ID_KEYBOARD, NKCODE_ESCAPE)));876g_controllerMap[VIRTKEY_FASTFORWARD].push_back(MultiInputMapping(InputMapping(DEVICE_ID_KEYBOARD, NKCODE_TAB)));877g_controllerMapGeneration++;878}879880const std::set<std::string> &GetSeenPads() {881std::lock_guard<std::recursive_mutex> guard(g_controllerMapLock);882return g_seenPads;883}884885std::string PadName(InputDeviceID deviceId) {886std::lock_guard<std::recursive_mutex> guard(g_controllerMapLock);887auto it = g_padNames.find(deviceId);888if (it != g_padNames.end())889return it->second;890return "";891}892893bool HasChanged(int &prevGeneration) {894if (prevGeneration != g_controllerMapGeneration) {895prevGeneration = g_controllerMapGeneration;896return true;897}898return false;899}900901static const char * const g_vKeyNames[] = {902"AXIS_X_MIN",903"AXIS_Y_MIN",904"AXIS_X_MAX",905"AXIS_Y_MAX",906"RAPID_FIRE",907"FASTFORWARD",908"PAUSE",909"SPEED_TOGGLE",910"AXIS_RIGHT_X_MIN",911"AXIS_RIGHT_Y_MIN",912"AXIS_RIGHT_X_MAX",913"AXIS_RIGHT_Y_MAX",914"REWIND",915"SAVE_STATE",916"LOAD_STATE",917"NEXT_SLOT",918"TOGGLE_FULLSCREEN",919"ANALOG_LIGHTLY",920"AXIS_SWAP",921"DEVMENU",922"FRAME_ADVANCE",923"RECORD",924"SPEED_CUSTOM1",925"SPEED_CUSTOM2",926"TEXTURE_DUMP",927"TEXTURE_REPLACE",928"SCREENSHOT",929"MUTE_TOGGLE",930"OPENCHAT",931"ANALOG_ROTATE_CW",932"ANALOG_ROTATE_CCW",933"SCREEN_ROTATION_VERTICAL",934"SCREEN_ROTATION_VERTICAL180",935"SCREEN_ROTATION_HORIZONTAL",936"SCREEN_ROTATION_HORIZONTAL180",937"SPEED_ANALOG",938"VR_CAMERA_ADJUST",939"VR_CAMERA_RESET",940};941942const char *GetVirtKeyName(int vkey) {943int index = vkey - VIRTKEY_FIRST;944if (index < 0 || index >= ARRAY_SIZE(g_vKeyNames)) {945return "N/A";946}947return g_vKeyNames[index];948}949950MultiInputMapping MultiInputMapping::FromConfigString(std::string_view str) {951MultiInputMapping out;952std::vector<std::string_view> parts;953SplitString(str, ':', parts);954for (auto iter : parts) {955out.mappings.push_back(InputMapping::FromConfigString(iter));956}957return out;958}959960std::string MultiInputMapping::ToConfigString() const {961std::string out;962for (auto iter : mappings) {963out += iter.ToConfigString() + ":";964}965out.pop_back(); // remove the last ':'966return out;967}968969std::string MultiInputMapping::ToVisualString() const {970std::string out;971for (auto iter : mappings) {972out += std::string(GetDeviceName(iter.deviceId)) + "." + GetKeyOrAxisName(iter) + " + ";973}974if (!out.empty()) {975// remove the last ' + '976out.pop_back();977out.pop_back();978out.pop_back();979}980return out;981}982983} // KeyMap984985986