From 03631f9b8fe75cf1c3a70a3094aeddcebffa4cf9 Mon Sep 17 00:00:00 2001 From: wwylele Date: Thu, 12 May 2016 13:09:36 +0300 Subject: [PATCH] Refactor input subsystem --- src/citra/config.cpp | 5 +- src/citra/default_ini.h | 8 +- src/citra/emu_window/emu_window_sdl2.cpp | 7 +- src/citra_qt/bootmanager.cpp | 7 +- src/citra_qt/config.cpp | 5 +- src/common/emu_window.cpp | 24 ++++- src/common/emu_window.h | 46 ++++++++-- src/common/key_map.cpp | 112 +++++++++++++++++++++-- src/common/key_map.h | 51 ++++++++++- src/core/hle/service/hid/hid.cpp | 64 +++++++------ src/core/hle/service/hid/hid.h | 3 - src/core/settings.h | 18 +++- 12 files changed, 277 insertions(+), 73 deletions(-) diff --git a/src/citra/config.cpp b/src/citra/config.cpp index c5cb4fb38..4f6d0a464 100644 --- a/src/citra/config.cpp +++ b/src/citra/config.cpp @@ -44,12 +44,15 @@ bool Config::LoadINI(const std::string& default_contents, bool retry) { } static const std::array defaults = { + // directly mapped keys SDL_SCANCODE_A, SDL_SCANCODE_S, SDL_SCANCODE_Z, SDL_SCANCODE_X, SDL_SCANCODE_Q, SDL_SCANCODE_W, SDL_SCANCODE_1, SDL_SCANCODE_2, SDL_SCANCODE_M, SDL_SCANCODE_N, SDL_SCANCODE_B, SDL_SCANCODE_T, SDL_SCANCODE_G, SDL_SCANCODE_F, SDL_SCANCODE_H, + SDL_SCANCODE_I, SDL_SCANCODE_K, SDL_SCANCODE_J, SDL_SCANCODE_L, + + // indirectly mapped keys SDL_SCANCODE_UP, SDL_SCANCODE_DOWN, SDL_SCANCODE_LEFT, SDL_SCANCODE_RIGHT, - SDL_SCANCODE_I, SDL_SCANCODE_K, SDL_SCANCODE_J, SDL_SCANCODE_L }; void Config::ReadValues() { diff --git a/src/citra/default_ini.h b/src/citra/default_ini.h index 49126356f..d0b258cab 100644 --- a/src/citra/default_ini.h +++ b/src/citra/default_ini.h @@ -23,14 +23,14 @@ pad_l = pad_r = pad_zl = pad_zr = -pad_sup = -pad_sdown = -pad_sleft = -pad_sright = pad_cup = pad_cdown = pad_cleft = pad_cright = +pad_circle_up = +pad_circle_down = +pad_circle_left = +pad_circle_right = [Core] # The applied frameskip amount. Must be a power of two. diff --git a/src/citra/emu_window/emu_window_sdl2.cpp b/src/citra/emu_window/emu_window_sdl2.cpp index 12cdd9d95..591f68aa4 100644 --- a/src/citra/emu_window/emu_window_sdl2.cpp +++ b/src/citra/emu_window/emu_window_sdl2.cpp @@ -40,9 +40,9 @@ void EmuWindow_SDL2::OnMouseButton(u32 button, u8 state, s32 x, s32 y) { void EmuWindow_SDL2::OnKeyEvent(int key, u8 state) { if (state == SDL_PRESSED) { - KeyPressed({ key, keyboard_id }); + KeyMap::PressKey(*this, { key, keyboard_id }); } else if (state == SDL_RELEASED) { - KeyReleased({ key, keyboard_id }); + KeyMap::ReleaseKey(*this, { key, keyboard_id }); } } @@ -168,8 +168,9 @@ void EmuWindow_SDL2::DoneCurrent() { } void EmuWindow_SDL2::ReloadSetKeymaps() { + KeyMap::ClearKeyMapping(keyboard_id); for (int i = 0; i < Settings::NativeInput::NUM_INPUTS; ++i) { - KeyMap::SetKeyMapping({ Settings::values.input_mappings[Settings::NativeInput::All[i]], keyboard_id }, Service::HID::pad_mapping[i]); + KeyMap::SetKeyMapping({ Settings::values.input_mappings[Settings::NativeInput::All[i]], keyboard_id }, KeyMap::mapping_targets[i]); } } diff --git a/src/citra_qt/bootmanager.cpp b/src/citra_qt/bootmanager.cpp index 01b81c11c..414b2f8af 100644 --- a/src/citra_qt/bootmanager.cpp +++ b/src/citra_qt/bootmanager.cpp @@ -235,12 +235,12 @@ void GRenderWindow::closeEvent(QCloseEvent* event) { void GRenderWindow::keyPressEvent(QKeyEvent* event) { - this->KeyPressed({event->key(), keyboard_id}); + KeyMap::PressKey(*this, { event->key(), keyboard_id }); } void GRenderWindow::keyReleaseEvent(QKeyEvent* event) { - this->KeyReleased({event->key(), keyboard_id}); + KeyMap::ReleaseKey(*this, { event->key(), keyboard_id }); } void GRenderWindow::mousePressEvent(QMouseEvent *event) @@ -270,8 +270,9 @@ void GRenderWindow::mouseReleaseEvent(QMouseEvent *event) void GRenderWindow::ReloadSetKeymaps() { + KeyMap::ClearKeyMapping(keyboard_id); for (int i = 0; i < Settings::NativeInput::NUM_INPUTS; ++i) { - KeyMap::SetKeyMapping({Settings::values.input_mappings[Settings::NativeInput::All[i]], keyboard_id}, Service::HID::pad_mapping[i]); + KeyMap::SetKeyMapping({ Settings::values.input_mappings[Settings::NativeInput::All[i]], keyboard_id }, KeyMap::mapping_targets[i]); } } diff --git a/src/citra_qt/config.cpp b/src/citra_qt/config.cpp index b5bb75537..ebee8c821 100644 --- a/src/citra_qt/config.cpp +++ b/src/citra_qt/config.cpp @@ -22,12 +22,15 @@ Config::Config() { } static const std::array defaults = { + // directly mapped keys Qt::Key_A, Qt::Key_S, Qt::Key_Z, Qt::Key_X, Qt::Key_Q, Qt::Key_W, Qt::Key_1, Qt::Key_2, Qt::Key_M, Qt::Key_N, Qt::Key_B, Qt::Key_T, Qt::Key_G, Qt::Key_F, Qt::Key_H, + Qt::Key_I, Qt::Key_K, Qt::Key_J, Qt::Key_L, + + // indirectly mapped keys Qt::Key_Up, Qt::Key_Down, Qt::Key_Left, Qt::Key_Right, - Qt::Key_I, Qt::Key_K, Qt::Key_J, Qt::Key_L }; void Config::ReadValues() { diff --git a/src/common/emu_window.cpp b/src/common/emu_window.cpp index b2807354a..08270dd88 100644 --- a/src/common/emu_window.cpp +++ b/src/common/emu_window.cpp @@ -11,12 +11,28 @@ #include "emu_window.h" #include "video_core/video_core.h" -void EmuWindow::KeyPressed(KeyMap::HostDeviceKey key) { - pad_state.hex |= KeyMap::GetPadKey(key).hex; +void EmuWindow::ButtonPressed(Service::HID::PadState pad) { + pad_state.hex |= pad.hex; } -void EmuWindow::KeyReleased(KeyMap::HostDeviceKey key) { - pad_state.hex &= ~KeyMap::GetPadKey(key).hex; +void EmuWindow::ButtonReleased(Service::HID::PadState pad) { + pad_state.hex &= ~pad.hex; +} + +void EmuWindow::CirclePadUpdated(float x, float y) { + constexpr int MAX_CIRCLEPAD_POS = 0x9C; // Max value for a circle pad position + + // Make sure the coordinates are in the unit circle, + // otherwise normalize it. + float r = x * x + y * y; + if (r > 1) { + r = std::sqrt(r); + x /= r; + y /= r; + } + + circle_pad_x = static_cast(x * MAX_CIRCLEPAD_POS); + circle_pad_y = static_cast(y * MAX_CIRCLEPAD_POS); } /** diff --git a/src/common/emu_window.h b/src/common/emu_window.h index 7c3486dea..0ae3ea2af 100644 --- a/src/common/emu_window.h +++ b/src/common/emu_window.h @@ -12,10 +12,6 @@ #include "core/hle/service/hid/hid.h" -namespace KeyMap { -struct HostDeviceKey; -} - /** * Abstraction class used to provide an interface between emulation code and the frontend * (e.g. SDL, QGLWidget, GLFW, etc...). @@ -76,11 +72,27 @@ public: virtual void ReloadSetKeymaps() = 0; - /// Signals a key press action to the HID module - void KeyPressed(KeyMap::HostDeviceKey key); + /** + * Signals a button press action to the HID module. + * @param pad_state indicates which button to press + * @note only handle real buttons (A/B/X/Y/...), excluding analog input like circle pad. + */ + void ButtonPressed(Service::HID::PadState pad_state); - /// Signals a key release action to the HID module - void KeyReleased(KeyMap::HostDeviceKey key); + /** + * Signals a button release action to the HID module. + * @param pad_state indicates which button to press + * @note only handle real buttons (A/B/X/Y/...), excluding analog input like circle pad. + */ + void ButtonReleased(Service::HID::PadState pad_state); + + /** + * Signals a circle pad change action to the HID module. + * @param x new x-coordinate of the circle pad, in the range [-1.0, 1.0] + * @param y new y-coordinate of the circle pad, in the range [-1.0, 1.0] + * @note the coordinates will be normalized if the radius is larger than 1 + */ + void CirclePadUpdated(float x, float y); /** * Signal that a touch pressed event has occurred (e.g. mouse click pressed) @@ -100,8 +112,9 @@ public: void TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y); /** - * Gets the current pad state (which buttons are pressed and the circle pad direction). + * Gets the current pad state (which buttons are pressed). * @note This should be called by the core emu thread to get a state set by the window thread. + * @note This doesn't include analog input like circle pad direction * @todo Fix this function to be thread-safe. * @return PadState object indicating the current pad state */ @@ -109,6 +122,16 @@ public: return pad_state; } + /** + * Gets the current cirle pad state. + * @note This should be called by the core emu thread to get a state set by the window thread. + * @todo Fix this function to be thread-safe. + * @return std::tuple of (x, y), where `x` and `y` are the circle pad coordinates + */ + std::tuple GetCirclePadState() const { + return std::make_tuple(circle_pad_x, circle_pad_y); + } + /** * Gets the current touch screen state (touch X/Y coordinates and whether or not it is pressed). * @note This should be called by the core emu thread to get a state set by the window thread. @@ -200,6 +223,8 @@ protected: pad_state.hex = 0; touch_x = 0; touch_y = 0; + circle_pad_x = 0; + circle_pad_y = 0; touch_pressed = false; } virtual ~EmuWindow() {} @@ -260,6 +285,9 @@ private: u16 touch_x; ///< Touchpad X-position in native 3DS pixel coordinates (0-320) u16 touch_y; ///< Touchpad Y-position in native 3DS pixel coordinates (0-240) + s16 circle_pad_x; ///< Circle pad X-position in native 3DS pixel coordinates (-156 - 156) + s16 circle_pad_y; ///< Circle pad Y-position in native 3DS pixel coordinates (-156 - 156) + /** * Clip the provided coordinates to be inside the touchscreen area. */ diff --git a/src/common/key_map.cpp b/src/common/key_map.cpp index 844d5df68..c8f168aa1 100644 --- a/src/common/key_map.cpp +++ b/src/common/key_map.cpp @@ -2,24 +2,124 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include "key_map.h" #include +#include "common/emu_window.h" +#include "common/key_map.h" + namespace KeyMap { -static std::map key_map; +// TODO (wwylele): currently we treat c-stick as four direction buttons +// and map it directly to EmuWindow::ButtonPressed. +// It should go the analog input way like circle pad does. +const std::array mapping_targets = {{ + Service::HID::PAD_A, Service::HID::PAD_B, Service::HID::PAD_X, Service::HID::PAD_Y, + Service::HID::PAD_L, Service::HID::PAD_R, Service::HID::PAD_ZL, Service::HID::PAD_ZR, + Service::HID::PAD_START, Service::HID::PAD_SELECT, Service::HID::PAD_NONE, + Service::HID::PAD_UP, Service::HID::PAD_DOWN, Service::HID::PAD_LEFT, Service::HID::PAD_RIGHT, + Service::HID::PAD_C_UP, Service::HID::PAD_C_DOWN, Service::HID::PAD_C_LEFT, Service::HID::PAD_C_RIGHT, + + IndirectTarget::CIRCLE_PAD_UP, + IndirectTarget::CIRCLE_PAD_DOWN, + IndirectTarget::CIRCLE_PAD_LEFT, + IndirectTarget::CIRCLE_PAD_RIGHT, +}}; + +static std::map key_map; static int next_device_id = 0; +static bool circle_pad_up = false, circle_pad_down = false, circle_pad_left = false, circle_pad_right = false; + +static void UpdateCirclePad(EmuWindow& emu_window) { + constexpr float SQRT_HALF = 0.707106781; + int x = 0, y = 0; + + if (circle_pad_right) + ++x; + if (circle_pad_left) + --x; + if (circle_pad_up) + ++y; + if (circle_pad_down) + --y; + // TODO: apply modifier here + emu_window.CirclePadUpdated(x * (y == 0 ? 1.0 : SQRT_HALF), y * (x == 0 ? 1.0 : SQRT_HALF)); +} + int NewDeviceId() { return next_device_id++; } -void SetKeyMapping(HostDeviceKey key, Service::HID::PadState padState) { - key_map[key].hex = padState.hex; +void SetKeyMapping(HostDeviceKey key, KeyTarget target) { + key_map[key] = target; } -Service::HID::PadState GetPadKey(HostDeviceKey key) { - return key_map[key]; +void ClearKeyMapping(int device_id) { + auto iter = key_map.begin(); + while (iter != key_map.end()) { + if (iter->first.device_id == device_id) + key_map.erase(iter++); + else + ++iter; + } +} + +void PressKey(EmuWindow& emu_window, HostDeviceKey key) { + auto target = key_map.find(key); + if (target == key_map.end()) + return; + + if (target->second.direct) { + emu_window.ButtonPressed({{target->second.target.direct_target_hex}}); + } else { + switch (target->second.target.indirect_target) { + case IndirectTarget::CIRCLE_PAD_UP: + circle_pad_up = true; + UpdateCirclePad(emu_window); + break; + case IndirectTarget::CIRCLE_PAD_DOWN: + circle_pad_down = true; + UpdateCirclePad(emu_window); + break; + case IndirectTarget::CIRCLE_PAD_LEFT: + circle_pad_left = true; + UpdateCirclePad(emu_window); + break; + case IndirectTarget::CIRCLE_PAD_RIGHT: + circle_pad_right = true; + UpdateCirclePad(emu_window); + break; + } + } +} + +void ReleaseKey(EmuWindow& emu_window,HostDeviceKey key) { + auto target = key_map.find(key); + if (target == key_map.end()) + return; + + if (target->second.direct) { + emu_window.ButtonReleased({{target->second.target.direct_target_hex}}); + } else { + switch (target->second.target.indirect_target) { + case IndirectTarget::CIRCLE_PAD_UP: + circle_pad_up = false; + UpdateCirclePad(emu_window); + break; + case IndirectTarget::CIRCLE_PAD_DOWN: + circle_pad_down = false; + UpdateCirclePad(emu_window); + break; + case IndirectTarget::CIRCLE_PAD_LEFT: + circle_pad_left = false; + UpdateCirclePad(emu_window); + break; + case IndirectTarget::CIRCLE_PAD_RIGHT: + circle_pad_right = false; + UpdateCirclePad(emu_window); + break; + } + } } } diff --git a/src/common/key_map.h b/src/common/key_map.h index 68f7e2f99..0438a14e0 100644 --- a/src/common/key_map.h +++ b/src/common/key_map.h @@ -4,11 +4,42 @@ #pragma once +#include #include #include "core/hle/service/hid/hid.h" +class EmuWindow; + namespace KeyMap { +enum class IndirectTarget { + CIRCLE_PAD_UP, CIRCLE_PAD_DOWN, CIRCLE_PAD_LEFT, CIRCLE_PAD_RIGHT, +}; + +/** + * Represents a key mapping target. It can be a PadState that represents 3DS real buttons, + * or an IndirectTarget. + */ +struct KeyTarget { + bool direct; + union { + u32 direct_target_hex; + IndirectTarget indirect_target; + } target; + + KeyTarget() : direct(true) { + target.direct_target_hex = 0; + } + + KeyTarget(Service::HID::PadState pad) : direct(true) { + target.direct_target_hex = pad.hex; + } + + KeyTarget(IndirectTarget i) : direct(false) { + target.indirect_target = i; + } +}; + /** * Represents a key for a specific host device. */ @@ -27,19 +58,31 @@ struct HostDeviceKey { } }; +extern const std::array mapping_targets; + /** * Generates a new device id, which uniquely identifies a host device within KeyMap. */ int NewDeviceId(); /** - * Maps a device-specific key to a PadState. + * Maps a device-specific key to a target (a PadState or an IndirectTarget). */ -void SetKeyMapping(HostDeviceKey key, Service::HID::PadState padState); +void SetKeyMapping(HostDeviceKey key, KeyTarget target); /** - * Gets the PadState that's mapped to the provided device-specific key. + * Clears all key mappings belonging to one device. */ -Service::HID::PadState GetPadKey(HostDeviceKey key); +void ClearKeyMapping(int device_id); + +/** + * Maps a key press actions and call the corresponding function in EmuWindow + */ +void PressKey(EmuWindow& emu_window, HostDeviceKey key); + +/** + * Maps a key release actions and call the corresponding function in EmuWindow + */ +void ReleaseKey(EmuWindow& emu_window, HostDeviceKey key); } diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index d216cecb4..a48184495 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp @@ -19,8 +19,6 @@ namespace Service { namespace HID { -static const int MAX_CIRCLEPAD_POS = 0x9C; ///< Max value for a circle pad position - // Handle to shared memory region designated to HID_User service static Kernel::SharedPtr shared_mem; @@ -39,38 +37,48 @@ static u32 next_gyroscope_index; static int enable_accelerometer_count = 0; // positive means enabled static int enable_gyroscope_count = 0; // positive means enabled -const std::array pad_mapping = {{ - Service::HID::PAD_A, Service::HID::PAD_B, Service::HID::PAD_X, Service::HID::PAD_Y, - Service::HID::PAD_L, Service::HID::PAD_R, Service::HID::PAD_ZL, Service::HID::PAD_ZR, - Service::HID::PAD_START, Service::HID::PAD_SELECT, Service::HID::PAD_NONE, - Service::HID::PAD_UP, Service::HID::PAD_DOWN, Service::HID::PAD_LEFT, Service::HID::PAD_RIGHT, - Service::HID::PAD_CIRCLE_UP, Service::HID::PAD_CIRCLE_DOWN, Service::HID::PAD_CIRCLE_LEFT, Service::HID::PAD_CIRCLE_RIGHT, - Service::HID::PAD_C_UP, Service::HID::PAD_C_DOWN, Service::HID::PAD_C_LEFT, Service::HID::PAD_C_RIGHT -}}; +static PadState GetCirclePadDirectionState(s16 circle_pad_x, s16 circle_pad_y) { + constexpr float TAN30 = 0.577350269, TAN60 = 1 / TAN30; // 30 degree and 60 degree are angular thresholds for directions + constexpr int CIRCLE_PAD_THRESHOLD_SQUARE = 40 * 40; // a circle pad radius greater than 40 will trigger circle pad direction + PadState state; + state.hex = 0; + if (circle_pad_x * circle_pad_x + circle_pad_y * circle_pad_y > CIRCLE_PAD_THRESHOLD_SQUARE) { + float t = std::abs(static_cast(circle_pad_y) / circle_pad_x); -// TODO(peachum): -// Add a method for setting analog input from joystick device for the circle Pad. -// -// This method should: -// * Be called after both PadButton(). -// * Be called before PadUpdateComplete() -// * Set current PadEntry.circle_pad_ using analog data -// * Set PadData.raw_circle_pad_data -// * Set PadData.current_state.circle_right = 1 if current PadEntry.circle_pad_x >= 41 -// * Set PadData.current_state.circle_up = 1 if current PadEntry.circle_pad_y >= 41 -// * Set PadData.current_state.circle_left = 1 if current PadEntry.circle_pad_x <= -41 -// * Set PadData.current_state.circle_right = 1 if current PadEntry.circle_pad_y <= -41 + if (circle_pad_x != 0 && t < TAN60) { + if (circle_pad_x > 0) + state.circle_right.Assign(1); + else + state.circle_left.Assign(1); + } + + if (circle_pad_x == 0 || t > TAN30) { + if (circle_pad_y > 0) + state.circle_up.Assign(1); + else + state.circle_down.Assign(1); + } + } + + return state; +} void Update() { SharedMem* mem = reinterpret_cast(shared_mem->GetPointer()); - const PadState state = VideoCore::g_emu_window->GetPadState(); if (mem == nullptr) { LOG_DEBUG(Service_HID, "Cannot update HID prior to mapping shared memory!"); return; } + PadState state = VideoCore::g_emu_window->GetPadState(); + + // Get current circle pad positon and update circle pad direction + s16 circle_pad_x, circle_pad_y; + std::tie(circle_pad_x, circle_pad_y) = VideoCore::g_emu_window->GetCirclePadState(); + state.hex |= GetCirclePadDirectionState(circle_pad_x, circle_pad_y).hex; + mem->pad.current_state.hex = state.hex; mem->pad.index = next_pad_index; next_pad_index = (next_pad_index + 1) % mem->pad.entries.size(); @@ -88,13 +96,9 @@ void Update() { // Update entry properties pad_entry.current_state.hex = state.hex; pad_entry.delta_additions.hex = changed.hex & state.hex; - pad_entry.delta_removals.hex = changed.hex & old_state.hex;; - - // Set circle Pad - pad_entry.circle_pad_x = state.circle_left ? -MAX_CIRCLEPAD_POS : - state.circle_right ? MAX_CIRCLEPAD_POS : 0x0; - pad_entry.circle_pad_y = state.circle_down ? -MAX_CIRCLEPAD_POS : - state.circle_up ? MAX_CIRCLEPAD_POS : 0x0; + pad_entry.delta_removals.hex = changed.hex & old_state.hex; + pad_entry.circle_pad_x = circle_pad_x; + pad_entry.circle_pad_y = circle_pad_y; // If we just updated index 0, provide a new timestamp if (mem->pad.index == 0) { diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h index 170d19ea8..669b1f723 100644 --- a/src/core/hle/service/hid/hid.h +++ b/src/core/hle/service/hid/hid.h @@ -215,9 +215,6 @@ const PadState PAD_CIRCLE_LEFT = {{1u << 29}}; const PadState PAD_CIRCLE_UP = {{1u << 30}}; const PadState PAD_CIRCLE_DOWN = {{1u << 31}}; - -extern const std::array pad_mapping; - /** * HID::GetIPCHandles service function * Inputs: diff --git a/src/core/settings.h b/src/core/settings.h index ce2a31164..df5915442 100644 --- a/src/core/settings.h +++ b/src/core/settings.h @@ -13,29 +13,37 @@ namespace Settings { namespace NativeInput { enum Values { + // directly mapped keys A, B, X, Y, L, R, ZL, ZR, START, SELECT, HOME, DUP, DDOWN, DLEFT, DRIGHT, - SUP, SDOWN, SLEFT, SRIGHT, CUP, CDOWN, CLEFT, CRIGHT, + + // indirectly mapped keys + CIRCLE_UP, CIRCLE_DOWN, CIRCLE_LEFT, CIRCLE_RIGHT, + NUM_INPUTS }; + static const std::array Mapping = {{ + // directly mapped keys "pad_a", "pad_b", "pad_x", "pad_y", "pad_l", "pad_r", "pad_zl", "pad_zr", "pad_start", "pad_select", "pad_home", "pad_dup", "pad_ddown", "pad_dleft", "pad_dright", - "pad_sup", "pad_sdown", "pad_sleft", "pad_sright", - "pad_cup", "pad_cdown", "pad_cleft", "pad_cright" + "pad_cup", "pad_cdown", "pad_cleft", "pad_cright", + + // indirectly mapped keys + "pad_circle_up", "pad_circle_down", "pad_circle_left", "pad_circle_right" }}; static const std::array All = {{ A, B, X, Y, L, R, ZL, ZR, START, SELECT, HOME, DUP, DDOWN, DLEFT, DRIGHT, - SUP, SDOWN, SLEFT, SRIGHT, - CUP, CDOWN, CLEFT, CRIGHT + CUP, CDOWN, CLEFT, CRIGHT, + CIRCLE_UP, CIRCLE_DOWN, CIRCLE_LEFT, CIRCLE_RIGHT, }}; }