From b0ee3a173a34b30a7a9555ab0bc1fdbf22c12670 Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Sat, 6 Jul 2013 20:44:34 +0200 Subject: [PATCH] Add mechanism for unlimited mappable virtual keys (not restricted by some bitfield). Use it to implement X/Y analog key mapping. --- Common/KeyMap.cpp | 116 ++++++++++++++++++++++++++++++++++--- Common/KeyMap.h | 21 +++++-- Core/Config.cpp | 5 ++ UI/EmuScreen.cpp | 49 +++++++++++----- UI/EmuScreen.h | 5 +- UI/MenuScreens.cpp | 35 +++++++---- Windows/KeyboardDevice.cpp | 46 +-------------- native | 2 +- 8 files changed, 197 insertions(+), 82 deletions(-) diff --git a/Common/KeyMap.cpp b/Common/KeyMap.cpp index 9db19e716e..3df733ef1a 100644 --- a/Common/KeyMap.cpp +++ b/Common/KeyMap.cpp @@ -15,6 +15,7 @@ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. +#include "file/ini_file.h" #include "input/input_state.h" #include "../Core/Config.h" #include "KeyMap.h" @@ -42,6 +43,10 @@ struct DefaultKeyMap { m[KeyDef(DEVICE_ID_KEYBOARD, KEYCODE_DPAD_DOWN)] = CTRL_DOWN; m[KeyDef(DEVICE_ID_KEYBOARD, KEYCODE_DPAD_LEFT)] = CTRL_LEFT; m[KeyDef(DEVICE_ID_KEYBOARD, KEYCODE_DPAD_RIGHT)] = CTRL_RIGHT; + m[KeyDef(DEVICE_ID_KEYBOARD, KEYCODE_I)] = VIRTKEY_AXIS_Y_MAX; + m[KeyDef(DEVICE_ID_KEYBOARD, KEYCODE_K)] = VIRTKEY_AXIS_Y_MIN; + m[KeyDef(DEVICE_ID_KEYBOARD, KEYCODE_J)] = VIRTKEY_AXIS_X_MIN; + m[KeyDef(DEVICE_ID_KEYBOARD, KEYCODE_L)] = VIRTKEY_AXIS_X_MAX; return m; } @@ -81,6 +86,24 @@ struct DefaultKeyMap { return m; } + static KeyMapping defaultXperiaPlay() + { + KeyMapping m; + m[KeyDef(DEVICE_ID_DEFAULT, KEYCODE_BUTTON_CROSS)] = CTRL_CROSS; + m[KeyDef(DEVICE_ID_DEFAULT, KEYCODE_BUTTON_CIRCLE)] = CTRL_CIRCLE; + m[KeyDef(DEVICE_ID_DEFAULT, KEYCODE_BUTTON_X)] = CTRL_SQUARE; + m[KeyDef(DEVICE_ID_DEFAULT, KEYCODE_BUTTON_Y)] = CTRL_TRIANGLE; + m[KeyDef(DEVICE_ID_DEFAULT, KEYCODE_DPAD_UP)] = CTRL_UP; + m[KeyDef(DEVICE_ID_DEFAULT, KEYCODE_DPAD_RIGHT)] = CTRL_RIGHT; + m[KeyDef(DEVICE_ID_DEFAULT, KEYCODE_DPAD_DOWN)] = CTRL_DOWN; + m[KeyDef(DEVICE_ID_DEFAULT, KEYCODE_DPAD_LEFT)] = CTRL_LEFT; + m[KeyDef(DEVICE_ID_DEFAULT, KEYCODE_BUTTON_START)] = CTRL_START; + m[KeyDef(DEVICE_ID_DEFAULT, KEYCODE_BACK)] = CTRL_SELECT; + m[KeyDef(DEVICE_ID_DEFAULT, KEYCODE_BUTTON_L1)] = CTRL_LTRIGGER; + m[KeyDef(DEVICE_ID_DEFAULT, KEYCODE_BUTTON_R1)] = CTRL_RTRIGGER; + return m; + } + static std::vector init() { std::vector m; @@ -96,6 +119,15 @@ struct DefaultKeyMap { kbd.keys = defaultKeyboardMap(); kbd.name = "Keyboard"; m.push_back(kbd); + +#ifdef ANDROID + ControllerMap xperia; + xperia.keys = defaultXperiaPlay(); + xperia.name = "Xperia Play"; + xperia.active = false; + m.push_back(xperia); +#endif + #else ControllerMap kbd; kbd.keys = defaultKeyboardMap(); @@ -251,7 +283,7 @@ const KeyMap_IntStrPair key_names[] = { }; static int key_names_count = sizeof(key_names) / sizeof(key_names[0]); -static std::string unknown_key_name = "Unknown"; +static std::string unknown_key_name = "N/A"; const KeyMap_IntStrPair psp_button_names[] = { {CTRL_CIRCLE, "O"}, {CTRL_CROSS, "X"}, @@ -265,7 +297,13 @@ const KeyMap_IntStrPair psp_button_names[] = { {CTRL_DOWN, "Down"}, {CTRL_LEFT, "Left"}, {CTRL_RIGHT, "Right"}, + + {VIRTKEY_AXIS_X_MIN, "An.Left"}, + {VIRTKEY_AXIS_X_MAX, "An.Right"}, + {VIRTKEY_AXIS_Y_MIN, "An.Down"}, + {VIRTKEY_AXIS_Y_MAX, "An.Up"}, }; + static int psp_button_names_count = sizeof(psp_button_names) / sizeof(psp_button_names[0]); static std::string FindName(int key, const KeyMap_IntStrPair list[], int size) @@ -355,16 +393,78 @@ std::string NamePspButtonFromKey(int deviceId, int key) return GetPspButtonName(KeyToPspButton(deviceId, key)); } -int SetKeyMapping(int map, int deviceId, int key, int btn) +void RemoveButtonMapping(int map, int btn) { + for (auto iter = controllerMaps[map].keys.begin(); iter != controllerMaps[map].keys.end(); ++iter) { + if (iter->second == btn) { + controllerMaps[map].keys.erase(iter); + return; + } + } +} + +void SetKeyMapping(int map, int deviceId, int key, int btn) { - if (IsMappedKey(deviceId, key)) { - // TODO: Remove the existing mapping instead, less annoying. - return KEYMAP_ERROR_KEY_ALREADY_USED; - } else { - controllerMaps[map].keys[KeyDef(deviceId, key)] = btn; + RemoveButtonMapping(map, btn); + controllerMaps[map].keys[KeyDef(deviceId, key)] = btn; +} + + +// TODO: Make the ini format nicer. +void LoadFromIni(IniFile &file) { + if (!file.HasSection("ControlMapping")) { + controllerMaps = DefaultKeyMap::KeyMap; + return; } - return btn; + controllerMaps.clear(); + + IniFile::Section *controls = file.GetOrCreateSection("ControlMapping"); + std::vector maps; + controls->Get("ControllerMaps", maps); + if (!maps.size()) { + controllerMaps = DefaultKeyMap::KeyMap; + return; + } + + for (auto x = maps.begin(); x != maps.end(); ++x) { + ControllerMap newMap; + newMap.name = *x; + IniFile::Section *map = file.GetOrCreateSection(newMap.name.c_str()); + map->Get("Active", &newMap.active, true); + std::map strmap = map->ToMap(); + + for (auto x = strmap.begin(); x != strmap.end(); ++x) { + std::vector keyParts; + SplitString(x->first, '-', keyParts); + if (keyParts.size() != 2) + continue; + int deviceId = atoi(keyParts[0].c_str()); + int keyCode = atoi(keyParts[1].c_str()); + newMap.keys[KeyDef(deviceId, keyCode)] = atoi(x->second.c_str()); + } + controllerMaps.push_back(newMap); + } +} + +void SaveToIni(IniFile &file) { + IniFile::Section *controls = file.GetOrCreateSection("ControlMapping"); + std::vector maps; + for (auto x = controllerMaps.begin(); x != controllerMaps.end(); ++x) { + maps.push_back(x->name); + } + controls->Set("ControllerMaps", maps); + + for (auto x = controllerMaps.begin(); x != controllerMaps.end(); ++x) { + IniFile::Section *map = file.GetOrCreateSection(x->name.c_str()); + map->Set("Active", x->active); + for (auto iter = x->keys.begin(); iter != x->keys.end(); ++iter) { + char key[128]; + sprintf(key, "%i-%i", iter->first.deviceId, iter->first.keyCode); + char value[128]; + sprintf(value, "%i", iter->second); + map->Set(key, value); + } + } } } // KeyMap diff --git a/Common/KeyMap.h b/Common/KeyMap.h index b8f0c073a5..92358087ff 100644 --- a/Common/KeyMap.h +++ b/Common/KeyMap.h @@ -19,12 +19,23 @@ #include #include +#include #include "input/keycodes.h" // keyboard keys #include "../Core/HLE/sceCtrl.h" // psp keys #define KEYMAP_ERROR_KEY_ALREADY_USED -1 #define KEYMAP_ERROR_UNKNOWN_KEY 0 +enum { + VIRTKEY_FIRST = 0x10000, + VIRTKEY_AXIS_X_MIN = 0x10000, + VIRTKEY_AXIS_Y_MIN = 0x10001, + VIRTKEY_AXIS_X_MAX = 0x10002, + VIRTKEY_AXIS_Y_MAX = 0x10003, + VIRTKEY_LAST, + VIRTKEY_COUNT = VIRTKEY_LAST - VIRTKEY_FIRST +}; + class KeyDef { public: KeyDef(int devId, int k) : deviceId(devId), keyCode(k) {} @@ -71,6 +82,8 @@ extern std::vector controllerMaps; // // Then have KeyMap transform those into psp buttons. +class IniFile; + namespace KeyMap { // Use if you need to display the textual name std::string GetKeyName(int keyCode); @@ -96,9 +109,9 @@ namespace KeyMap { // Configure the key mapping. // Any configuration will be saved to the Core config. - // - // Returns KEYMAP_ERROR_KEY_ALREADY_USED - // for mapping conflicts. 0 otherwise. - int SetKeyMapping(int map, int deviceId, int keyCode, int psp_key); + void SetKeyMapping(int map, int deviceId, int keyCode, int psp_key); + + void LoadFromIni(IniFile &iniFile); + void SaveToIni(IniFile &iniFile); } diff --git a/Core/Config.cpp b/Core/Config.cpp index 36d1d0ed24..bd2845e6b0 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -17,6 +17,7 @@ #include "base/display.h" +#include "Common/KeyMap.h" #include "Common/FileUtil.h" #include "Config.h" #include "file/ini_file.h" @@ -167,6 +168,8 @@ void Config::Load(const char *iniFileName) debugConfig->Get("ConsoleWindowX", &iConsoleWindowX, -1); debugConfig->Get("ConsoleWindowY", &iConsoleWindowY, -1); + KeyMap::LoadFromIni(iniFile); + CleanRecent(); } @@ -275,6 +278,8 @@ void Config::Save() debugConfig->Set("ConsoleWindowX", iConsoleWindowX); debugConfig->Set("ConsoleWindowY", iConsoleWindowY); + KeyMap::SaveToIni(iniFile); + if (!iniFile.Save(iniFilename_.c_str())) { ERROR_LOG(LOADER, "Error saving config - can't write ini %s", iniFilename_.c_str()); return; diff --git a/UI/EmuScreen.cpp b/UI/EmuScreen.cpp index 14a30c32c2..18177f8813 100644 --- a/UI/EmuScreen.cpp +++ b/UI/EmuScreen.cpp @@ -108,6 +108,8 @@ EmuScreen::EmuScreen(const std::string &filename) : invalid_(true) { } #endif memset(analog_, 0, sizeof(analog_)); + memset(&fakeInputState, 0, sizeof(fakeInputState)); + memset(virtKeys, 0, sizeof(virtKeys)); } EmuScreen::~EmuScreen() { @@ -185,10 +187,17 @@ void EmuScreen::key(const KeyInput &key) { if (result == KEYMAP_ERROR_UNKNOWN_KEY) return; - if (key.flags & KEY_DOWN) - __CtrlButtonDown(result); - if (key.flags & KEY_UP) - __CtrlButtonUp(result); + if (result >= VIRTKEY_FIRST) { + if (key.flags & KEY_DOWN) + virtKeys[result - VIRTKEY_FIRST] = true; + if (key.flags & KEY_UP) + virtKeys[result - VIRTKEY_FIRST] = false; + } else { + if (key.flags & KEY_DOWN) + __CtrlButtonDown(result); + if (key.flags & KEY_UP) + __CtrlButtonUp(result); + } } void EmuScreen::axis(const AxisInput &axis) { @@ -231,6 +240,21 @@ void EmuScreen::update(InputState &input) { if (invalid_) return; + float leftstick_x = analog_[0].x; + float leftstick_y = analog_[0].y; + float rightstick_x = analog_[1].x; + float rightstick_y = analog_[1].y; + + // Virtual keys. + if (virtKeys[VIRTKEY_AXIS_X_MIN - VIRTKEY_FIRST]) + leftstick_x -= 1.0f; + if (virtKeys[VIRTKEY_AXIS_X_MAX - VIRTKEY_FIRST]) + leftstick_x += 1.0f; + if (virtKeys[VIRTKEY_AXIS_Y_MIN - VIRTKEY_FIRST]) + leftstick_y -= 1.0f; + if (virtKeys[VIRTKEY_AXIS_Y_MAX - VIRTKEY_FIRST]) + leftstick_y += 1.0f; + // First translate touches into native pad input. // Do this no matter the value of g_Config.bShowTouchControls, some people // like to use invisible controls... @@ -257,16 +281,15 @@ void EmuScreen::update(InputState &input) { if (fakeInputState.pad_buttons_up & legacy_touch_mapping[i].from) __CtrlButtonUp(legacy_touch_mapping[i].to); } + leftstick_x += fakeInputState.pad_lstick_x; + leftstick_y += fakeInputState.pad_lstick_y; + rightstick_x += fakeInputState.pad_rstick_x; + rightstick_y += fakeInputState.pad_rstick_y; #ifdef _WIN32 } #endif - float leftstick_x = analog_[0].x; - float leftstick_y = analog_[0].y; - float rightstick_x = analog_[1].x; - float rightstick_y = analog_[1].y; - I18NCategory *s = GetI18NCategory("Screen"); // Apply tilt to left stick @@ -276,10 +299,10 @@ void EmuScreen::update(InputState &input) { leftstick_x = clamp1(leftstick_x); } - __CtrlSetAnalogX(leftstick_x, 0); - __CtrlSetAnalogY(leftstick_y, 0); - __CtrlSetAnalogX(rightstick_x, 1); - __CtrlSetAnalogY(rightstick_y, 1); + __CtrlSetAnalogX(clamp1(leftstick_x), 0); + __CtrlSetAnalogY(clamp1(leftstick_y), 0); + __CtrlSetAnalogX(clamp1(rightstick_x), 1); + __CtrlSetAnalogY(clamp1(rightstick_y), 1); if (PSP_CoreParameter().fpsLimit != 2) { // Don't really need to show these, it's pretty obvious what unthrottle does, diff --git a/UI/EmuScreen.h b/UI/EmuScreen.h index 61acc9fac2..a747cc85ae 100644 --- a/UI/EmuScreen.h +++ b/UI/EmuScreen.h @@ -21,6 +21,7 @@ #include #include "ui/screen.h" +#include "Common/KeyMap.h" class EmuScreen : public Screen { @@ -46,7 +47,9 @@ private: // For the virtual touch buttons, that currently can't send key events. InputState fakeInputState; - // Analog is still buffered. struct {float x, y;} analog_[2]; + + // To track mappable virtual keys. We can have as many as we want. + bool virtKeys[VIRTKEY_COUNT]; }; diff --git a/UI/MenuScreens.cpp b/UI/MenuScreens.cpp index dfad96d699..f47de8a15d 100644 --- a/UI/MenuScreens.cpp +++ b/UI/MenuScreens.cpp @@ -1560,7 +1560,17 @@ void KeyMappingScreen::render() { KeyBtn(left+hlfpad, top, CTRL_UP); // ^ KeyBtn(left, top+hlfpad, CTRL_LEFT);// < KeyBtn(left+pad, top+hlfpad, CTRL_RIGHT); // > - KeyBtn(left+hlfpad, top+pad, CTRL_DOWN); // < + KeyBtn(left+hlfpad, top+pad, CTRL_DOWN); // v + +#ifndef ANDROID + top = 10; + left = 250; + KeyBtn(left+hlfpad, top, VIRTKEY_AXIS_Y_MAX); // ^ + KeyBtn(left, top+hlfpad, VIRTKEY_AXIS_X_MIN);// < + KeyBtn(left+pad, top+hlfpad, VIRTKEY_AXIS_X_MAX); // > + KeyBtn(left+hlfpad, top+pad, VIRTKEY_AXIS_Y_MIN); // v + top = 100; +#endif left = 500; KeyBtn(left+hlfpad, top, CTRL_TRIANGLE); // Triangle @@ -1586,10 +1596,12 @@ void KeyMappingScreen::render() { } if (UIButton(GEN_ID, Pos(10 + 10 + LARGE_BUTTON_WIDTH, dp_yres-10), LARGE_BUTTON_WIDTH, 0, generalI18N->T("Next"), ALIGN_BOTTOMLEFT)) { currentMap_++; - if (currentMap_ >= controllerMaps.size()) + if (currentMap_ >= (int)controllerMaps.size()) currentMap_ = 0; } - UIText(0, Pos(10, dp_yres-170), controllerMaps[currentMap_].name.c_str(), 0xFFFFFFFF, 1.0f, ALIGN_BOTTOMLEFT); + char temp[256]; + sprintf(temp, "%s (%i/%i)", controllerMaps[currentMap_].name.c_str(), currentMap_ + 1, controllerMaps.size()); + UIText(0, Pos(10, dp_yres-170), temp, 0xFFFFFFFF, 1.0f, ALIGN_BOTTOMLEFT); UICheckBox(GEN_ID,10, dp_yres - 80, "Mapping Active", ALIGN_BOTTOMLEFT, &controllerMaps[currentMap_].active); UIEnd(); } @@ -1625,17 +1637,16 @@ void KeyMappingNewKeyDialog::render() { KeyText(right, top, keyI18N->T("New Key")); KeyScale(2.0f); if (last_kb_key != 0) { + KeyText(right, top += stride, KeyMap::GetKeyName(last_kb_key).c_str()); + KeyScale(1.4f); + KeyText(right, top + stride, GetDeviceName(last_kb_deviceid)); bool key_used = KeyMap::IsMappedKey(last_kb_deviceid, last_kb_key); - if (!key_used) { - KeyText(right, top += stride, KeyMap::GetKeyName(last_kb_key).c_str()); - KeyScale(1.4f); - KeyText(right, top + stride, GetDeviceName(last_kb_deviceid)); - } else { + if (key_used) { KeyScale(1.0f); KeyText(left + stride, top + 2*stride, - keyI18N->T("Error: Key is already used by")); - //KeyText(left + stride, top + 3*stride, - // (KeyMap::NamePspButtonFromKey(last_kb_key)).c_str()); + keyI18N->T("Warning: Key is already used by")); + KeyText(left + stride, top + 3*stride, + (KeyMap::NamePspButtonFromKey(last_kb_deviceid, last_kb_key)).c_str()); } } @@ -1645,7 +1656,7 @@ void KeyMappingNewKeyDialog::render() { // Save & cancel buttons if (UIButton(GEN_ID, Pos(10, dp_yres - 10), LARGE_BUTTON_WIDTH, 0, keyI18N->T("Save Mapping"), ALIGN_LEFT | ALIGN_BOTTOM)) { - KeyMap::SetKeyMapping(currentMap_, this->last_kb_deviceid, this->last_kb_key, this->pspBtn); + KeyMap::SetKeyMapping(currentMap_, last_kb_deviceid, last_kb_key, pspBtn); g_Config.Save(); screenManager()->finishDialog(this, DR_OK); } diff --git a/Windows/KeyboardDevice.cpp b/Windows/KeyboardDevice.cpp index 3e48fc7b32..011593059e 100644 --- a/Windows/KeyboardDevice.cpp +++ b/Windows/KeyboardDevice.cpp @@ -10,7 +10,8 @@ #include "../Core/HLE/sceCtrl.h" #include "WinUser.h" -unsigned int key_pad_map[] = { +// TODO: remove. almost not used. +static unsigned int key_pad_map[] = { VK_ESCAPE,PAD_BUTTON_MENU, // Open PauseScreen VK_BACK, PAD_BUTTON_BACK, // Toggle PauseScreen & Back Setting Page 'Z', PAD_BUTTON_A, @@ -31,13 +32,6 @@ unsigned int key_pad_map[] = { const unsigned int key_pad_map_size = sizeof(key_pad_map); -unsigned short analog_ctrl_map[] = { - 'I', CTRL_UP, - 'K', CTRL_DOWN, - 'J', CTRL_LEFT, - 'L', CTRL_RIGHT, -}; - // TODO: More keys need to be added, but this is more than // a fair start. std::map windowsTransTable = InitConstMap @@ -121,8 +115,6 @@ std::map windowsTransTable = InitConstMap (VK_OEM_6, KEYCODE_RIGHT_BRACKET) (VK_MENU, KEYCODE_MENU); -const unsigned int analog_ctrl_map_size = sizeof(analog_ctrl_map); - int KeyboardDevice::UpdateState(InputState &input_state) { if (MainWindow::GetHWND() != GetForegroundWindow()) return -1; bool alternate = GetAsyncKeyState(VK_SHIFT) != 0; @@ -135,13 +127,13 @@ int KeyboardDevice::UpdateState(InputState &input_state) { input_state.pad_buttons |= PAD_BUTTON_UNTHROTTLE; } + // TODO: remove for (int i = 0; i < sizeof(key_pad_map)/sizeof(key_pad_map[0]); i += 2) { if (!GetAsyncKeyState(key_pad_map[i])) { continue; } if (!doAlternate || key_pad_map[i + 1] > PAD_BUTTON_SELECT) { - // TODO: remove once EmuScreen supports virtual keys switch (key_pad_map[i + 1]) { case PAD_BUTTON_MENU: case PAD_BUTTON_BACK: @@ -172,38 +164,6 @@ int KeyboardDevice::UpdateState(InputState &input_state) { } upKeys.clear(); - // TODO: Better axis mapping - float analogX = 0; - float analogY = 0; - for (int i = 0; i < sizeof(analog_ctrl_map)/sizeof(analog_ctrl_map[0]); i += 2) { - if (!GetAsyncKeyState(analog_ctrl_map[i])) { - continue; - } - - switch (analog_ctrl_map[i + 1]) { - case CTRL_UP: - analogY += 1.0f; - break; - case CTRL_DOWN: - analogY -= 1.0f; - break; - case CTRL_LEFT: - analogX -= 1.0f; - break; - case CTRL_RIGHT: - analogX += 1.0f; - break; - } - } - - AxisInput axis; - axis.deviceId = DEVICE_ID_KEYBOARD; - axis.axisId = JOYSTICK_AXIS_X; - axis.value = analogX; - NativeAxis(axis); - axis.axisId = JOYSTICK_AXIS_Y; - axis.value = analogY; - NativeAxis(axis); return 0; } diff --git a/native b/native index 1dec6e8154..4aa16ddb86 160000 --- a/native +++ b/native @@ -1 +1 @@ -Subproject commit 1dec6e8154610699e99a479a01e26ed03ea044df +Subproject commit 4aa16ddb86994c1246f96707b94d21917befe7ad