diff options
| author | 2017-03-17 14:59:39 -0400 | |
|---|---|---|
| committer | 2017-03-17 14:59:39 -0400 | |
| commit | 423ab5e2bcf5a522e5f412447c05f648df57a14c (patch) | |
| tree | 1e60eaeffa59229254a47f885d2fe2cbbdc1a5c0 /src | |
| parent | Merge pull request #2618 from wwylele/log-less-filename (diff) | |
| parent | qt/config_input: don't connect for null button (diff) | |
| download | yuzu-423ab5e2bcf5a522e5f412447c05f648df57a14c.tar.gz yuzu-423ab5e2bcf5a522e5f412447c05f648df57a14c.tar.xz yuzu-423ab5e2bcf5a522e5f412447c05f648df57a14c.zip | |
Merge pull request #2497 from wwylele/input-2
Refactor input emulation & add SDL gamepad support
Diffstat (limited to 'src')
40 files changed, 1244 insertions, 574 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 1e1245160..a45439481 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt | |||
| @@ -5,6 +5,7 @@ add_subdirectory(common) | |||
| 5 | add_subdirectory(core) | 5 | add_subdirectory(core) |
| 6 | add_subdirectory(video_core) | 6 | add_subdirectory(video_core) |
| 7 | add_subdirectory(audio_core) | 7 | add_subdirectory(audio_core) |
| 8 | add_subdirectory(input_common) | ||
| 8 | add_subdirectory(tests) | 9 | add_subdirectory(tests) |
| 9 | if (ENABLE_SDL2) | 10 | if (ENABLE_SDL2) |
| 10 | add_subdirectory(citra) | 11 | add_subdirectory(citra) |
diff --git a/src/citra/CMakeLists.txt b/src/citra/CMakeLists.txt index ecb5d2dfe..47231ba71 100644 --- a/src/citra/CMakeLists.txt +++ b/src/citra/CMakeLists.txt | |||
| @@ -18,7 +18,7 @@ create_directory_groups(${SRCS} ${HEADERS}) | |||
| 18 | include_directories(${SDL2_INCLUDE_DIR}) | 18 | include_directories(${SDL2_INCLUDE_DIR}) |
| 19 | 19 | ||
| 20 | add_executable(citra ${SRCS} ${HEADERS}) | 20 | add_executable(citra ${SRCS} ${HEADERS}) |
| 21 | target_link_libraries(citra core video_core audio_core common) | 21 | target_link_libraries(citra core video_core audio_core common input_common) |
| 22 | target_link_libraries(citra ${SDL2_LIBRARY} ${OPENGL_gl_LIBRARY} inih glad) | 22 | target_link_libraries(citra ${SDL2_LIBRARY} ${OPENGL_gl_LIBRARY} inih glad) |
| 23 | if (MSVC) | 23 | if (MSVC) |
| 24 | target_link_libraries(citra getopt) | 24 | target_link_libraries(citra getopt) |
diff --git a/src/citra/config.cpp b/src/citra/config.cpp index fac1c9a0e..ef1229912 100644 --- a/src/citra/config.cpp +++ b/src/citra/config.cpp | |||
| @@ -8,8 +8,10 @@ | |||
| 8 | #include "citra/default_ini.h" | 8 | #include "citra/default_ini.h" |
| 9 | #include "common/file_util.h" | 9 | #include "common/file_util.h" |
| 10 | #include "common/logging/log.h" | 10 | #include "common/logging/log.h" |
| 11 | #include "common/param_package.h" | ||
| 11 | #include "config.h" | 12 | #include "config.h" |
| 12 | #include "core/settings.h" | 13 | #include "core/settings.h" |
| 14 | #include "input_common/main.h" | ||
| 13 | 15 | ||
| 14 | Config::Config() { | 16 | Config::Config() { |
| 15 | // TODO: Don't hardcode the path; let the frontend decide where to put the config files. | 17 | // TODO: Don't hardcode the path; let the frontend decide where to put the config files. |
| @@ -37,25 +39,40 @@ bool Config::LoadINI(const std::string& default_contents, bool retry) { | |||
| 37 | return true; | 39 | return true; |
| 38 | } | 40 | } |
| 39 | 41 | ||
| 40 | static const std::array<int, Settings::NativeInput::NUM_INPUTS> defaults = { | 42 | static const std::array<int, Settings::NativeButton::NumButtons> default_buttons = { |
| 41 | // directly mapped keys | 43 | SDL_SCANCODE_A, SDL_SCANCODE_S, SDL_SCANCODE_Z, SDL_SCANCODE_X, SDL_SCANCODE_T, |
| 42 | SDL_SCANCODE_A, SDL_SCANCODE_S, SDL_SCANCODE_Z, SDL_SCANCODE_X, SDL_SCANCODE_Q, SDL_SCANCODE_W, | 44 | SDL_SCANCODE_G, SDL_SCANCODE_F, SDL_SCANCODE_H, SDL_SCANCODE_Q, SDL_SCANCODE_W, |
| 43 | SDL_SCANCODE_1, SDL_SCANCODE_2, SDL_SCANCODE_M, SDL_SCANCODE_N, SDL_SCANCODE_B, SDL_SCANCODE_T, | 45 | SDL_SCANCODE_M, SDL_SCANCODE_N, SDL_SCANCODE_1, SDL_SCANCODE_2, SDL_SCANCODE_B, |
| 44 | SDL_SCANCODE_G, SDL_SCANCODE_F, SDL_SCANCODE_H, SDL_SCANCODE_I, SDL_SCANCODE_K, SDL_SCANCODE_J, | ||
| 45 | SDL_SCANCODE_L, | ||
| 46 | |||
| 47 | // indirectly mapped keys | ||
| 48 | SDL_SCANCODE_UP, SDL_SCANCODE_DOWN, SDL_SCANCODE_LEFT, SDL_SCANCODE_RIGHT, SDL_SCANCODE_D, | ||
| 49 | }; | 46 | }; |
| 50 | 47 | ||
| 48 | static const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs> default_analogs{{ | ||
| 49 | { | ||
| 50 | SDL_SCANCODE_UP, SDL_SCANCODE_DOWN, SDL_SCANCODE_LEFT, SDL_SCANCODE_RIGHT, SDL_SCANCODE_D, | ||
| 51 | }, | ||
| 52 | { | ||
| 53 | SDL_SCANCODE_I, SDL_SCANCODE_K, SDL_SCANCODE_J, SDL_SCANCODE_L, SDL_SCANCODE_D, | ||
| 54 | }, | ||
| 55 | }}; | ||
| 56 | |||
| 51 | void Config::ReadValues() { | 57 | void Config::ReadValues() { |
| 52 | // Controls | 58 | // Controls |
| 53 | for (int i = 0; i < Settings::NativeInput::NUM_INPUTS; ++i) { | 59 | for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { |
| 54 | Settings::values.input_mappings[Settings::NativeInput::All[i]] = | 60 | std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); |
| 55 | sdl2_config->GetInteger("Controls", Settings::NativeInput::Mapping[i], defaults[i]); | 61 | Settings::values.buttons[i] = |
| 62 | sdl2_config->Get("Controls", Settings::NativeButton::mapping[i], default_param); | ||
| 63 | if (Settings::values.buttons[i].empty()) | ||
| 64 | Settings::values.buttons[i] = default_param; | ||
| 65 | } | ||
| 66 | |||
| 67 | for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { | ||
| 68 | std::string default_param = InputCommon::GenerateAnalogParamFromKeys( | ||
| 69 | default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], | ||
| 70 | default_analogs[i][3], default_analogs[i][4], 0.5f); | ||
| 71 | Settings::values.analogs[i] = | ||
| 72 | sdl2_config->Get("Controls", Settings::NativeAnalog::mapping[i], default_param); | ||
| 73 | if (Settings::values.analogs[i].empty()) | ||
| 74 | Settings::values.analogs[i] = default_param; | ||
| 56 | } | 75 | } |
| 57 | Settings::values.pad_circle_modifier_scale = | ||
| 58 | (float)sdl2_config->GetReal("Controls", "pad_circle_modifier_scale", 0.5); | ||
| 59 | 76 | ||
| 60 | // Core | 77 | // Core |
| 61 | Settings::values.use_cpu_jit = sdl2_config->GetBoolean("Core", "use_cpu_jit", true); | 78 | Settings::values.use_cpu_jit = sdl2_config->GetBoolean("Core", "use_cpu_jit", true); |
diff --git a/src/citra/default_ini.h b/src/citra/default_ini.h index 435ba6f00..af9f7aa2a 100644 --- a/src/citra/default_ini.h +++ b/src/citra/default_ini.h | |||
| @@ -8,34 +8,47 @@ namespace DefaultINI { | |||
| 8 | 8 | ||
| 9 | const char* sdl2_config_file = R"( | 9 | const char* sdl2_config_file = R"( |
| 10 | [Controls] | 10 | [Controls] |
| 11 | pad_start = | 11 | # The input devices and parameters for each 3DS native input |
| 12 | pad_select = | 12 | # It should be in the format of "engine:[engine_name],[param1]:[value1],[param2]:[value2]..." |
| 13 | pad_home = | 13 | # Escape characters $0 (for ':'), $1 (for ',') and $2 (for '$') can be used in values |
| 14 | pad_dup = | 14 | |
| 15 | pad_ddown = | 15 | # for button input, the following devices are avaible: |
| 16 | pad_dleft = | 16 | # - "keyboard" (default) for keyboard input. Required parameters: |
| 17 | pad_dright = | 17 | # - "code": the code of the key to bind |
| 18 | pad_a = | 18 | # - "sdl" for joystick input using SDL. Required parameters: |
| 19 | pad_b = | 19 | # - "joystick": the index of the joystick to bind |
| 20 | pad_x = | 20 | # - "button"(optional): the index of the button to bind |
| 21 | pad_y = | 21 | # - "hat"(optional): the index of the hat to bind as direction buttons |
| 22 | pad_l = | 22 | # - "direction"(only used for hat): the direction name of the hat to bind. Can be "up", "down", "left" or "right" |
| 23 | pad_r = | 23 | button_a= |
| 24 | pad_zl = | 24 | button_b= |
| 25 | pad_zr = | 25 | button_x= |
| 26 | pad_cup = | 26 | button_y= |
| 27 | pad_cdown = | 27 | button_up= |
| 28 | pad_cleft = | 28 | button_down= |
| 29 | pad_cright = | 29 | button_left= |
| 30 | pad_circle_up = | 30 | button_right= |
| 31 | pad_circle_down = | 31 | button_l= |
| 32 | pad_circle_left = | 32 | button_r= |
| 33 | pad_circle_right = | 33 | button_start= |
| 34 | pad_circle_modifier = | 34 | button_select= |
| 35 | 35 | button_zl= | |
| 36 | # The applied modifier scale to circle pad. | 36 | button_zr= |
| 37 | # Must be in range of 0.0-1.0. Defaults to 0.5 | 37 | button_home= |
| 38 | pad_circle_modifier_scale = | 38 | |
| 39 | # for analog input, the following devices are avaible: | ||
| 40 | # - "analog_from_button" (default) for emulating analog input from direction buttons. Required parameters: | ||
| 41 | # - "up", "down", "left", "right": sub-devices for each direction. | ||
| 42 | # Should be in the format as a button input devices using escape characters, for example, "engine$0keyboard$1code$00" | ||
| 43 | # - "modifier": sub-devices as a modifier. | ||
| 44 | # - "modifier_scale": a float number representing the applied modifier scale to the analog input. | ||
| 45 | # Must be in range of 0.0-1.0. Defaults to 0.5 | ||
| 46 | # - "sdl" for joystick input using SDL. Required parameters: | ||
| 47 | # - "joystick": the index of the joystick to bind | ||
| 48 | # - "axis_x": the index of the axis to bind as x-axis (default to 0) | ||
| 49 | # - "axis_y": the index of the axis to bind as y-axis (default to 1) | ||
| 50 | circle_pad= | ||
| 51 | c_stick= | ||
| 39 | 52 | ||
| 40 | [Core] | 53 | [Core] |
| 41 | # Whether to use the Just-In-Time (JIT) compiler for CPU emulation | 54 | # Whether to use the Just-In-Time (JIT) compiler for CPU emulation |
diff --git a/src/citra/emu_window/emu_window_sdl2.cpp b/src/citra/emu_window/emu_window_sdl2.cpp index 00d00905a..6bc0b0d00 100644 --- a/src/citra/emu_window/emu_window_sdl2.cpp +++ b/src/citra/emu_window/emu_window_sdl2.cpp | |||
| @@ -12,9 +12,9 @@ | |||
| 12 | #include "common/logging/log.h" | 12 | #include "common/logging/log.h" |
| 13 | #include "common/scm_rev.h" | 13 | #include "common/scm_rev.h" |
| 14 | #include "common/string_util.h" | 14 | #include "common/string_util.h" |
| 15 | #include "core/frontend/key_map.h" | ||
| 16 | #include "core/hle/service/hid/hid.h" | ||
| 17 | #include "core/settings.h" | 15 | #include "core/settings.h" |
| 16 | #include "input_common/keyboard.h" | ||
| 17 | #include "input_common/main.h" | ||
| 18 | #include "video_core/video_core.h" | 18 | #include "video_core/video_core.h" |
| 19 | 19 | ||
| 20 | void EmuWindow_SDL2::OnMouseMotion(s32 x, s32 y) { | 20 | void EmuWindow_SDL2::OnMouseMotion(s32 x, s32 y) { |
| @@ -40,9 +40,9 @@ void EmuWindow_SDL2::OnMouseButton(u32 button, u8 state, s32 x, s32 y) { | |||
| 40 | 40 | ||
| 41 | void EmuWindow_SDL2::OnKeyEvent(int key, u8 state) { | 41 | void EmuWindow_SDL2::OnKeyEvent(int key, u8 state) { |
| 42 | if (state == SDL_PRESSED) { | 42 | if (state == SDL_PRESSED) { |
| 43 | KeyMap::PressKey(*this, {key, keyboard_id}); | 43 | InputCommon::GetKeyboard()->PressKey(key); |
| 44 | } else if (state == SDL_RELEASED) { | 44 | } else if (state == SDL_RELEASED) { |
| 45 | KeyMap::ReleaseKey(*this, {key, keyboard_id}); | 45 | InputCommon::GetKeyboard()->ReleaseKey(key); |
| 46 | } | 46 | } |
| 47 | } | 47 | } |
| 48 | 48 | ||
| @@ -57,9 +57,8 @@ void EmuWindow_SDL2::OnResize() { | |||
| 57 | } | 57 | } |
| 58 | 58 | ||
| 59 | EmuWindow_SDL2::EmuWindow_SDL2() { | 59 | EmuWindow_SDL2::EmuWindow_SDL2() { |
| 60 | keyboard_id = KeyMap::NewDeviceId(); | 60 | InputCommon::Init(); |
| 61 | 61 | ||
| 62 | ReloadSetKeymaps(); | ||
| 63 | motion_emu = std::make_unique<Motion::MotionEmu>(*this); | 62 | motion_emu = std::make_unique<Motion::MotionEmu>(*this); |
| 64 | 63 | ||
| 65 | SDL_SetMainReady(); | 64 | SDL_SetMainReady(); |
| @@ -117,6 +116,7 @@ EmuWindow_SDL2::~EmuWindow_SDL2() { | |||
| 117 | SDL_GL_DeleteContext(gl_context); | 116 | SDL_GL_DeleteContext(gl_context); |
| 118 | SDL_Quit(); | 117 | SDL_Quit(); |
| 119 | motion_emu = nullptr; | 118 | motion_emu = nullptr; |
| 119 | InputCommon::Shutdown(); | ||
| 120 | } | 120 | } |
| 121 | 121 | ||
| 122 | void EmuWindow_SDL2::SwapBuffers() { | 122 | void EmuWindow_SDL2::SwapBuffers() { |
| @@ -169,15 +169,6 @@ void EmuWindow_SDL2::DoneCurrent() { | |||
| 169 | SDL_GL_MakeCurrent(render_window, nullptr); | 169 | SDL_GL_MakeCurrent(render_window, nullptr); |
| 170 | } | 170 | } |
| 171 | 171 | ||
| 172 | void EmuWindow_SDL2::ReloadSetKeymaps() { | ||
| 173 | KeyMap::ClearKeyMapping(keyboard_id); | ||
| 174 | for (int i = 0; i < Settings::NativeInput::NUM_INPUTS; ++i) { | ||
| 175 | KeyMap::SetKeyMapping( | ||
| 176 | {Settings::values.input_mappings[Settings::NativeInput::All[i]], keyboard_id}, | ||
| 177 | KeyMap::mapping_targets[i]); | ||
| 178 | } | ||
| 179 | } | ||
| 180 | |||
| 181 | void EmuWindow_SDL2::OnMinimalClientAreaChangeRequest( | 172 | void EmuWindow_SDL2::OnMinimalClientAreaChangeRequest( |
| 182 | const std::pair<unsigned, unsigned>& minimal_size) { | 173 | const std::pair<unsigned, unsigned>& minimal_size) { |
| 183 | 174 | ||
diff --git a/src/citra/emu_window/emu_window_sdl2.h b/src/citra/emu_window/emu_window_sdl2.h index b1cbf16d7..1ce2991f7 100644 --- a/src/citra/emu_window/emu_window_sdl2.h +++ b/src/citra/emu_window/emu_window_sdl2.h | |||
| @@ -31,9 +31,6 @@ public: | |||
| 31 | /// Whether the window is still open, and a close request hasn't yet been sent | 31 | /// Whether the window is still open, and a close request hasn't yet been sent |
| 32 | bool IsOpen() const; | 32 | bool IsOpen() const; |
| 33 | 33 | ||
| 34 | /// Load keymap from configuration | ||
| 35 | void ReloadSetKeymaps() override; | ||
| 36 | |||
| 37 | private: | 34 | private: |
| 38 | /// Called by PollEvents when a key is pressed or released. | 35 | /// Called by PollEvents when a key is pressed or released. |
| 39 | void OnKeyEvent(int key, u8 state); | 36 | void OnKeyEvent(int key, u8 state); |
| @@ -61,9 +58,6 @@ private: | |||
| 61 | /// The OpenGL context associated with the window | 58 | /// The OpenGL context associated with the window |
| 62 | SDL_GLContext gl_context; | 59 | SDL_GLContext gl_context; |
| 63 | 60 | ||
| 64 | /// Device id of keyboard for use with KeyMap | ||
| 65 | int keyboard_id; | ||
| 66 | |||
| 67 | /// Motion sensors emulation | 61 | /// Motion sensors emulation |
| 68 | std::unique_ptr<Motion::MotionEmu> motion_emu; | 62 | std::unique_ptr<Motion::MotionEmu> motion_emu; |
| 69 | }; | 63 | }; |
diff --git a/src/citra_qt/CMakeLists.txt b/src/citra_qt/CMakeLists.txt index 15a6ccf9a..2b1c59a92 100644 --- a/src/citra_qt/CMakeLists.txt +++ b/src/citra_qt/CMakeLists.txt | |||
| @@ -97,7 +97,7 @@ if (APPLE) | |||
| 97 | else() | 97 | else() |
| 98 | add_executable(citra-qt ${SRCS} ${HEADERS} ${UI_HDRS}) | 98 | add_executable(citra-qt ${SRCS} ${HEADERS} ${UI_HDRS}) |
| 99 | endif() | 99 | endif() |
| 100 | target_link_libraries(citra-qt core video_core audio_core common) | 100 | target_link_libraries(citra-qt core video_core audio_core common input_common) |
| 101 | target_link_libraries(citra-qt ${OPENGL_gl_LIBRARY} ${CITRA_QT_LIBS}) | 101 | target_link_libraries(citra-qt ${OPENGL_gl_LIBRARY} ${CITRA_QT_LIBS}) |
| 102 | target_link_libraries(citra-qt ${PLATFORM_LIBRARIES} Threads::Threads) | 102 | target_link_libraries(citra-qt ${PLATFORM_LIBRARIES} Threads::Threads) |
| 103 | 103 | ||
diff --git a/src/citra_qt/bootmanager.cpp b/src/citra_qt/bootmanager.cpp index 69d18cf0c..28264df9a 100644 --- a/src/citra_qt/bootmanager.cpp +++ b/src/citra_qt/bootmanager.cpp | |||
| @@ -13,7 +13,9 @@ | |||
| 13 | #include "common/scm_rev.h" | 13 | #include "common/scm_rev.h" |
| 14 | #include "common/string_util.h" | 14 | #include "common/string_util.h" |
| 15 | #include "core/core.h" | 15 | #include "core/core.h" |
| 16 | #include "core/frontend/key_map.h" | 16 | #include "core/settings.h" |
| 17 | #include "input_common/keyboard.h" | ||
| 18 | #include "input_common/main.h" | ||
| 17 | #include "video_core/debug_utils/debug_utils.h" | 19 | #include "video_core/debug_utils/debug_utils.h" |
| 18 | #include "video_core/video_core.h" | 20 | #include "video_core/video_core.h" |
| 19 | 21 | ||
| @@ -99,14 +101,17 @@ private: | |||
| 99 | }; | 101 | }; |
| 100 | 102 | ||
| 101 | GRenderWindow::GRenderWindow(QWidget* parent, EmuThread* emu_thread) | 103 | GRenderWindow::GRenderWindow(QWidget* parent, EmuThread* emu_thread) |
| 102 | : QWidget(parent), child(nullptr), keyboard_id(0), emu_thread(emu_thread) { | 104 | : QWidget(parent), child(nullptr), emu_thread(emu_thread) { |
| 103 | 105 | ||
| 104 | std::string window_title = Common::StringFromFormat("Citra %s| %s-%s", Common::g_build_name, | 106 | std::string window_title = Common::StringFromFormat("Citra %s| %s-%s", Common::g_build_name, |
| 105 | Common::g_scm_branch, Common::g_scm_desc); | 107 | Common::g_scm_branch, Common::g_scm_desc); |
| 106 | setWindowTitle(QString::fromStdString(window_title)); | 108 | setWindowTitle(QString::fromStdString(window_title)); |
| 107 | 109 | ||
| 108 | keyboard_id = KeyMap::NewDeviceId(); | 110 | InputCommon::Init(); |
| 109 | ReloadSetKeymaps(); | 111 | } |
| 112 | |||
| 113 | GRenderWindow::~GRenderWindow() { | ||
| 114 | InputCommon::Shutdown(); | ||
| 110 | } | 115 | } |
| 111 | 116 | ||
| 112 | void GRenderWindow::moveContext() { | 117 | void GRenderWindow::moveContext() { |
| @@ -197,11 +202,11 @@ void GRenderWindow::closeEvent(QCloseEvent* event) { | |||
| 197 | } | 202 | } |
| 198 | 203 | ||
| 199 | void GRenderWindow::keyPressEvent(QKeyEvent* event) { | 204 | void GRenderWindow::keyPressEvent(QKeyEvent* event) { |
| 200 | KeyMap::PressKey(*this, {event->key(), keyboard_id}); | 205 | InputCommon::GetKeyboard()->PressKey(event->key()); |
| 201 | } | 206 | } |
| 202 | 207 | ||
| 203 | void GRenderWindow::keyReleaseEvent(QKeyEvent* event) { | 208 | void GRenderWindow::keyReleaseEvent(QKeyEvent* event) { |
| 204 | KeyMap::ReleaseKey(*this, {event->key(), keyboard_id}); | 209 | InputCommon::GetKeyboard()->ReleaseKey(event->key()); |
| 205 | } | 210 | } |
| 206 | 211 | ||
| 207 | void GRenderWindow::mousePressEvent(QMouseEvent* event) { | 212 | void GRenderWindow::mousePressEvent(QMouseEvent* event) { |
| @@ -230,14 +235,7 @@ void GRenderWindow::mouseReleaseEvent(QMouseEvent* event) { | |||
| 230 | motion_emu->EndTilt(); | 235 | motion_emu->EndTilt(); |
| 231 | } | 236 | } |
| 232 | 237 | ||
| 233 | void GRenderWindow::ReloadSetKeymaps() { | 238 | void GRenderWindow::ReloadSetKeymaps() {} |
| 234 | KeyMap::ClearKeyMapping(keyboard_id); | ||
| 235 | for (int i = 0; i < Settings::NativeInput::NUM_INPUTS; ++i) { | ||
| 236 | KeyMap::SetKeyMapping( | ||
| 237 | {Settings::values.input_mappings[Settings::NativeInput::All[i]], keyboard_id}, | ||
| 238 | KeyMap::mapping_targets[i]); | ||
| 239 | } | ||
| 240 | } | ||
| 241 | 239 | ||
| 242 | void GRenderWindow::OnClientAreaResized(unsigned width, unsigned height) { | 240 | void GRenderWindow::OnClientAreaResized(unsigned width, unsigned height) { |
| 243 | NotifyClientAreaSizeChanged(std::make_pair(width, height)); | 241 | NotifyClientAreaSizeChanged(std::make_pair(width, height)); |
diff --git a/src/citra_qt/bootmanager.h b/src/citra_qt/bootmanager.h index 7dac1c480..923a5b456 100644 --- a/src/citra_qt/bootmanager.h +++ b/src/citra_qt/bootmanager.h | |||
| @@ -104,6 +104,7 @@ class GRenderWindow : public QWidget, public EmuWindow { | |||
| 104 | 104 | ||
| 105 | public: | 105 | public: |
| 106 | GRenderWindow(QWidget* parent, EmuThread* emu_thread); | 106 | GRenderWindow(QWidget* parent, EmuThread* emu_thread); |
| 107 | ~GRenderWindow(); | ||
| 107 | 108 | ||
| 108 | // EmuWindow implementation | 109 | // EmuWindow implementation |
| 109 | void SwapBuffers() override; | 110 | void SwapBuffers() override; |
| @@ -127,7 +128,7 @@ public: | |||
| 127 | void mouseMoveEvent(QMouseEvent* event) override; | 128 | void mouseMoveEvent(QMouseEvent* event) override; |
| 128 | void mouseReleaseEvent(QMouseEvent* event) override; | 129 | void mouseReleaseEvent(QMouseEvent* event) override; |
| 129 | 130 | ||
| 130 | void ReloadSetKeymaps() override; | 131 | void ReloadSetKeymaps(); |
| 131 | 132 | ||
| 132 | void OnClientAreaResized(unsigned width, unsigned height); | 133 | void OnClientAreaResized(unsigned width, unsigned height); |
| 133 | 134 | ||
| @@ -152,9 +153,6 @@ private: | |||
| 152 | 153 | ||
| 153 | QByteArray geometry; | 154 | QByteArray geometry; |
| 154 | 155 | ||
| 155 | /// Device id of keyboard for use with KeyMap | ||
| 156 | int keyboard_id; | ||
| 157 | |||
| 158 | EmuThread* emu_thread; | 156 | EmuThread* emu_thread; |
| 159 | 157 | ||
| 160 | /// Motion sensors emulation | 158 | /// Motion sensors emulation |
diff --git a/src/citra_qt/config.cpp b/src/citra_qt/config.cpp index 5fe57dfa2..6ccfa1577 100644 --- a/src/citra_qt/config.cpp +++ b/src/citra_qt/config.cpp | |||
| @@ -6,6 +6,7 @@ | |||
| 6 | #include "citra_qt/config.h" | 6 | #include "citra_qt/config.h" |
| 7 | #include "citra_qt/ui_settings.h" | 7 | #include "citra_qt/ui_settings.h" |
| 8 | #include "common/file_util.h" | 8 | #include "common/file_util.h" |
| 9 | #include "input_common/main.h" | ||
| 9 | 10 | ||
| 10 | Config::Config() { | 11 | Config::Config() { |
| 11 | // TODO: Don't hardcode the path; let the frontend decide where to put the config files. | 12 | // TODO: Don't hardcode the path; let the frontend decide where to put the config files. |
| @@ -16,25 +17,46 @@ Config::Config() { | |||
| 16 | Reload(); | 17 | Reload(); |
| 17 | } | 18 | } |
| 18 | 19 | ||
| 19 | const std::array<QVariant, Settings::NativeInput::NUM_INPUTS> Config::defaults = { | 20 | const std::array<int, Settings::NativeButton::NumButtons> Config::default_buttons = { |
| 20 | // directly mapped keys | 21 | Qt::Key_A, Qt::Key_S, Qt::Key_Z, Qt::Key_X, Qt::Key_T, Qt::Key_G, Qt::Key_F, Qt::Key_H, |
| 21 | Qt::Key_A, Qt::Key_S, Qt::Key_Z, Qt::Key_X, Qt::Key_Q, Qt::Key_W, Qt::Key_1, Qt::Key_2, | 22 | Qt::Key_Q, Qt::Key_W, Qt::Key_M, Qt::Key_N, Qt::Key_1, Qt::Key_2, Qt::Key_B, |
| 22 | Qt::Key_M, Qt::Key_N, Qt::Key_B, Qt::Key_T, Qt::Key_G, Qt::Key_F, Qt::Key_H, Qt::Key_I, | ||
| 23 | Qt::Key_K, Qt::Key_J, Qt::Key_L, | ||
| 24 | |||
| 25 | // indirectly mapped keys | ||
| 26 | Qt::Key_Up, Qt::Key_Down, Qt::Key_Left, Qt::Key_Right, Qt::Key_D, | ||
| 27 | }; | 23 | }; |
| 28 | 24 | ||
| 25 | const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs> Config::default_analogs{{ | ||
| 26 | { | ||
| 27 | Qt::Key_Up, Qt::Key_Down, Qt::Key_Left, Qt::Key_Right, Qt::Key_D, | ||
| 28 | }, | ||
| 29 | { | ||
| 30 | Qt::Key_I, Qt::Key_K, Qt::Key_J, Qt::Key_L, Qt::Key_D, | ||
| 31 | }, | ||
| 32 | }}; | ||
| 33 | |||
| 29 | void Config::ReadValues() { | 34 | void Config::ReadValues() { |
| 30 | qt_config->beginGroup("Controls"); | 35 | qt_config->beginGroup("Controls"); |
| 31 | for (int i = 0; i < Settings::NativeInput::NUM_INPUTS; ++i) { | 36 | for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { |
| 32 | Settings::values.input_mappings[Settings::NativeInput::All[i]] = | 37 | std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); |
| 33 | qt_config->value(QString::fromStdString(Settings::NativeInput::Mapping[i]), defaults[i]) | 38 | Settings::values.buttons[i] = |
| 34 | .toInt(); | 39 | qt_config |
| 40 | ->value(Settings::NativeButton::mapping[i], QString::fromStdString(default_param)) | ||
| 41 | .toString() | ||
| 42 | .toStdString(); | ||
| 43 | if (Settings::values.buttons[i].empty()) | ||
| 44 | Settings::values.buttons[i] = default_param; | ||
| 45 | } | ||
| 46 | |||
| 47 | for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { | ||
| 48 | std::string default_param = InputCommon::GenerateAnalogParamFromKeys( | ||
| 49 | default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], | ||
| 50 | default_analogs[i][3], default_analogs[i][4], 0.5f); | ||
| 51 | Settings::values.analogs[i] = | ||
| 52 | qt_config | ||
| 53 | ->value(Settings::NativeAnalog::mapping[i], QString::fromStdString(default_param)) | ||
| 54 | .toString() | ||
| 55 | .toStdString(); | ||
| 56 | if (Settings::values.analogs[i].empty()) | ||
| 57 | Settings::values.analogs[i] = default_param; | ||
| 35 | } | 58 | } |
| 36 | Settings::values.pad_circle_modifier_scale = | 59 | |
| 37 | qt_config->value("pad_circle_modifier_scale", 0.5).toFloat(); | ||
| 38 | qt_config->endGroup(); | 60 | qt_config->endGroup(); |
| 39 | 61 | ||
| 40 | qt_config->beginGroup("Core"); | 62 | qt_config->beginGroup("Core"); |
| @@ -155,12 +177,14 @@ void Config::ReadValues() { | |||
| 155 | 177 | ||
| 156 | void Config::SaveValues() { | 178 | void Config::SaveValues() { |
| 157 | qt_config->beginGroup("Controls"); | 179 | qt_config->beginGroup("Controls"); |
| 158 | for (int i = 0; i < Settings::NativeInput::NUM_INPUTS; ++i) { | 180 | for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { |
| 159 | qt_config->setValue(QString::fromStdString(Settings::NativeInput::Mapping[i]), | 181 | qt_config->setValue(QString::fromStdString(Settings::NativeButton::mapping[i]), |
| 160 | Settings::values.input_mappings[Settings::NativeInput::All[i]]); | 182 | QString::fromStdString(Settings::values.buttons[i])); |
| 183 | } | ||
| 184 | for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { | ||
| 185 | qt_config->setValue(QString::fromStdString(Settings::NativeAnalog::mapping[i]), | ||
| 186 | QString::fromStdString(Settings::values.analogs[i])); | ||
| 161 | } | 187 | } |
| 162 | qt_config->setValue("pad_circle_modifier_scale", | ||
| 163 | (double)Settings::values.pad_circle_modifier_scale); | ||
| 164 | qt_config->endGroup(); | 188 | qt_config->endGroup(); |
| 165 | 189 | ||
| 166 | qt_config->beginGroup("Core"); | 190 | qt_config->beginGroup("Core"); |
diff --git a/src/citra_qt/config.h b/src/citra_qt/config.h index 79c901804..cbf745ea2 100644 --- a/src/citra_qt/config.h +++ b/src/citra_qt/config.h | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <array> | ||
| 7 | #include <string> | 8 | #include <string> |
| 8 | #include <QVariant> | 9 | #include <QVariant> |
| 9 | #include "core/settings.h" | 10 | #include "core/settings.h" |
| @@ -23,5 +24,7 @@ public: | |||
| 23 | 24 | ||
| 24 | void Reload(); | 25 | void Reload(); |
| 25 | void Save(); | 26 | void Save(); |
| 26 | static const std::array<QVariant, Settings::NativeInput::NUM_INPUTS> defaults; | 27 | |
| 28 | static const std::array<int, Settings::NativeButton::NumButtons> default_buttons; | ||
| 29 | static const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs> default_analogs; | ||
| 27 | }; | 30 | }; |
diff --git a/src/citra_qt/configure_input.cpp b/src/citra_qt/configure_input.cpp index c29652f32..b59713e2c 100644 --- a/src/citra_qt/configure_input.cpp +++ b/src/citra_qt/configure_input.cpp | |||
| @@ -2,13 +2,21 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <algorithm> | ||
| 5 | #include <memory> | 6 | #include <memory> |
| 6 | #include <utility> | 7 | #include <utility> |
| 7 | #include <QTimer> | 8 | #include <QTimer> |
| 8 | #include "citra_qt/config.h" | 9 | #include "citra_qt/config.h" |
| 9 | #include "citra_qt/configure_input.h" | 10 | #include "citra_qt/configure_input.h" |
| 11 | #include "common/param_package.h" | ||
| 12 | #include "input_common/main.h" | ||
| 10 | 13 | ||
| 11 | static QString getKeyName(Qt::Key key_code) { | 14 | const std::array<std::string, ConfigureInput::ANALOG_SUB_BUTTONS_NUM> |
| 15 | ConfigureInput::analog_sub_buttons{{ | ||
| 16 | "up", "down", "left", "right", "modifier", | ||
| 17 | }}; | ||
| 18 | |||
| 19 | static QString getKeyName(int key_code) { | ||
| 12 | switch (key_code) { | 20 | switch (key_code) { |
| 13 | case Qt::Key_Shift: | 21 | case Qt::Key_Shift: |
| 14 | return QObject::tr("Shift"); | 22 | return QObject::tr("Shift"); |
| @@ -23,6 +31,20 @@ static QString getKeyName(Qt::Key key_code) { | |||
| 23 | } | 31 | } |
| 24 | } | 32 | } |
| 25 | 33 | ||
| 34 | static void SetButtonKey(int key, Common::ParamPackage& button_param) { | ||
| 35 | button_param = Common::ParamPackage{InputCommon::GenerateKeyboardParam(key)}; | ||
| 36 | } | ||
| 37 | |||
| 38 | static void SetAnalogKey(int key, Common::ParamPackage& analog_param, | ||
| 39 | const std::string& button_name) { | ||
| 40 | if (analog_param.Get("engine", "") != "analog_from_button") { | ||
| 41 | analog_param = { | ||
| 42 | {"engine", "analog_from_button"}, {"modifier_scale", "0.5"}, | ||
| 43 | }; | ||
| 44 | } | ||
| 45 | analog_param.Set(button_name, InputCommon::GenerateKeyboardParam(key)); | ||
| 46 | } | ||
| 47 | |||
| 26 | ConfigureInput::ConfigureInput(QWidget* parent) | 48 | ConfigureInput::ConfigureInput(QWidget* parent) |
| 27 | : QWidget(parent), ui(std::make_unique<Ui::ConfigureInput>()), | 49 | : QWidget(parent), ui(std::make_unique<Ui::ConfigureInput>()), |
| 28 | timer(std::make_unique<QTimer>()) { | 50 | timer(std::make_unique<QTimer>()) { |
| @@ -31,36 +53,41 @@ ConfigureInput::ConfigureInput(QWidget* parent) | |||
| 31 | setFocusPolicy(Qt::ClickFocus); | 53 | setFocusPolicy(Qt::ClickFocus); |
| 32 | 54 | ||
| 33 | button_map = { | 55 | button_map = { |
| 34 | {Settings::NativeInput::Values::A, ui->buttonA}, | 56 | ui->buttonA, ui->buttonB, ui->buttonX, ui->buttonY, ui->buttonDpadUp, |
| 35 | {Settings::NativeInput::Values::B, ui->buttonB}, | 57 | ui->buttonDpadDown, ui->buttonDpadLeft, ui->buttonDpadRight, ui->buttonL, ui->buttonR, |
| 36 | {Settings::NativeInput::Values::X, ui->buttonX}, | 58 | ui->buttonStart, ui->buttonSelect, ui->buttonZL, ui->buttonZR, ui->buttonHome, |
| 37 | {Settings::NativeInput::Values::Y, ui->buttonY}, | ||
| 38 | {Settings::NativeInput::Values::L, ui->buttonL}, | ||
| 39 | {Settings::NativeInput::Values::R, ui->buttonR}, | ||
| 40 | {Settings::NativeInput::Values::ZL, ui->buttonZL}, | ||
| 41 | {Settings::NativeInput::Values::ZR, ui->buttonZR}, | ||
| 42 | {Settings::NativeInput::Values::START, ui->buttonStart}, | ||
| 43 | {Settings::NativeInput::Values::SELECT, ui->buttonSelect}, | ||
| 44 | {Settings::NativeInput::Values::HOME, ui->buttonHome}, | ||
| 45 | {Settings::NativeInput::Values::DUP, ui->buttonDpadUp}, | ||
| 46 | {Settings::NativeInput::Values::DDOWN, ui->buttonDpadDown}, | ||
| 47 | {Settings::NativeInput::Values::DLEFT, ui->buttonDpadLeft}, | ||
| 48 | {Settings::NativeInput::Values::DRIGHT, ui->buttonDpadRight}, | ||
| 49 | {Settings::NativeInput::Values::CUP, ui->buttonCStickUp}, | ||
| 50 | {Settings::NativeInput::Values::CDOWN, ui->buttonCStickDown}, | ||
| 51 | {Settings::NativeInput::Values::CLEFT, ui->buttonCStickLeft}, | ||
| 52 | {Settings::NativeInput::Values::CRIGHT, ui->buttonCStickRight}, | ||
| 53 | {Settings::NativeInput::Values::CIRCLE_UP, ui->buttonCircleUp}, | ||
| 54 | {Settings::NativeInput::Values::CIRCLE_DOWN, ui->buttonCircleDown}, | ||
| 55 | {Settings::NativeInput::Values::CIRCLE_LEFT, ui->buttonCircleLeft}, | ||
| 56 | {Settings::NativeInput::Values::CIRCLE_RIGHT, ui->buttonCircleRight}, | ||
| 57 | {Settings::NativeInput::Values::CIRCLE_MODIFIER, ui->buttonCircleMod}, | ||
| 58 | }; | 59 | }; |
| 59 | 60 | ||
| 60 | for (const auto& entry : button_map) { | 61 | analog_map = {{ |
| 61 | const Settings::NativeInput::Values input_id = entry.first; | 62 | { |
| 62 | connect(entry.second, &QPushButton::released, | 63 | ui->buttonCircleUp, ui->buttonCircleDown, ui->buttonCircleLeft, ui->buttonCircleRight, |
| 63 | [this, input_id]() { handleClick(input_id); }); | 64 | ui->buttonCircleMod, |
| 65 | }, | ||
| 66 | { | ||
| 67 | ui->buttonCStickUp, ui->buttonCStickDown, ui->buttonCStickLeft, ui->buttonCStickRight, | ||
| 68 | nullptr, | ||
| 69 | }, | ||
| 70 | }}; | ||
| 71 | |||
| 72 | for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; button_id++) { | ||
| 73 | if (button_map[button_id]) | ||
| 74 | connect(button_map[button_id], &QPushButton::released, [=]() { | ||
| 75 | handleClick(button_map[button_id], | ||
| 76 | [=](int key) { SetButtonKey(key, buttons_param[button_id]); }); | ||
| 77 | }); | ||
| 78 | } | ||
| 79 | |||
| 80 | for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) { | ||
| 81 | for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; sub_button_id++) { | ||
| 82 | if (analog_map[analog_id][sub_button_id] != nullptr) { | ||
| 83 | connect(analog_map[analog_id][sub_button_id], &QPushButton::released, [=]() { | ||
| 84 | handleClick(analog_map[analog_id][sub_button_id], [=](int key) { | ||
| 85 | SetAnalogKey(key, analogs_param[analog_id], | ||
| 86 | analog_sub_buttons[sub_button_id]); | ||
| 87 | }); | ||
| 88 | }); | ||
| 89 | } | ||
| 90 | } | ||
| 64 | } | 91 | } |
| 65 | 92 | ||
| 66 | connect(ui->buttonRestoreDefaults, &QPushButton::released, [this]() { restoreDefaults(); }); | 93 | connect(ui->buttonRestoreDefaults, &QPushButton::released, [this]() { restoreDefaults(); }); |
| @@ -69,50 +96,93 @@ ConfigureInput::ConfigureInput(QWidget* parent) | |||
| 69 | connect(timer.get(), &QTimer::timeout, [this]() { | 96 | connect(timer.get(), &QTimer::timeout, [this]() { |
| 70 | releaseKeyboard(); | 97 | releaseKeyboard(); |
| 71 | releaseMouse(); | 98 | releaseMouse(); |
| 72 | current_input_id = boost::none; | 99 | key_setter = boost::none; |
| 73 | updateButtonLabels(); | 100 | updateButtonLabels(); |
| 74 | }); | 101 | }); |
| 75 | 102 | ||
| 76 | this->loadConfiguration(); | 103 | this->loadConfiguration(); |
| 104 | |||
| 105 | // TODO(wwylele): enable these when the input emulation for them is implemented | ||
| 106 | ui->buttonZL->setEnabled(false); | ||
| 107 | ui->buttonZR->setEnabled(false); | ||
| 108 | ui->buttonHome->setEnabled(false); | ||
| 109 | ui->buttonCStickUp->setEnabled(false); | ||
| 110 | ui->buttonCStickDown->setEnabled(false); | ||
| 111 | ui->buttonCStickLeft->setEnabled(false); | ||
| 112 | ui->buttonCStickRight->setEnabled(false); | ||
| 77 | } | 113 | } |
| 78 | 114 | ||
| 79 | void ConfigureInput::applyConfiguration() { | 115 | void ConfigureInput::applyConfiguration() { |
| 80 | for (const auto& input_id : Settings::NativeInput::All) { | 116 | std::transform(buttons_param.begin(), buttons_param.end(), Settings::values.buttons.begin(), |
| 81 | const size_t index = static_cast<size_t>(input_id); | 117 | [](const Common::ParamPackage& param) { return param.Serialize(); }); |
| 82 | Settings::values.input_mappings[index] = static_cast<int>(key_map[input_id]); | 118 | std::transform(analogs_param.begin(), analogs_param.end(), Settings::values.analogs.begin(), |
| 83 | } | 119 | [](const Common::ParamPackage& param) { return param.Serialize(); }); |
| 120 | |||
| 84 | Settings::Apply(); | 121 | Settings::Apply(); |
| 85 | } | 122 | } |
| 86 | 123 | ||
| 87 | void ConfigureInput::loadConfiguration() { | 124 | void ConfigureInput::loadConfiguration() { |
| 88 | for (const auto& input_id : Settings::NativeInput::All) { | 125 | std::transform(Settings::values.buttons.begin(), Settings::values.buttons.end(), |
| 89 | const size_t index = static_cast<size_t>(input_id); | 126 | buttons_param.begin(), |
| 90 | key_map[input_id] = static_cast<Qt::Key>(Settings::values.input_mappings[index]); | 127 | [](const std::string& str) { return Common::ParamPackage(str); }); |
| 91 | } | 128 | std::transform(Settings::values.analogs.begin(), Settings::values.analogs.end(), |
| 129 | analogs_param.begin(), | ||
| 130 | [](const std::string& str) { return Common::ParamPackage(str); }); | ||
| 92 | updateButtonLabels(); | 131 | updateButtonLabels(); |
| 93 | } | 132 | } |
| 94 | 133 | ||
| 95 | void ConfigureInput::restoreDefaults() { | 134 | void ConfigureInput::restoreDefaults() { |
| 96 | for (const auto& input_id : Settings::NativeInput::All) { | 135 | for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; button_id++) { |
| 97 | const size_t index = static_cast<size_t>(input_id); | 136 | SetButtonKey(Config::default_buttons[button_id], buttons_param[button_id]); |
| 98 | key_map[input_id] = static_cast<Qt::Key>(Config::defaults[index].toInt()); | 137 | } |
| 138 | |||
| 139 | for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) { | ||
| 140 | for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; sub_button_id++) { | ||
| 141 | SetAnalogKey(Config::default_analogs[analog_id][sub_button_id], | ||
| 142 | analogs_param[analog_id], analog_sub_buttons[sub_button_id]); | ||
| 143 | } | ||
| 99 | } | 144 | } |
| 100 | updateButtonLabels(); | 145 | updateButtonLabels(); |
| 101 | applyConfiguration(); | 146 | applyConfiguration(); |
| 102 | } | 147 | } |
| 103 | 148 | ||
| 104 | void ConfigureInput::updateButtonLabels() { | 149 | void ConfigureInput::updateButtonLabels() { |
| 105 | for (const auto& input_id : Settings::NativeInput::All) { | 150 | QString non_keyboard(tr("[non-keyboard]")); |
| 106 | button_map[input_id]->setText(getKeyName(key_map[input_id])); | 151 | |
| 152 | auto KeyToText = [&non_keyboard](const Common::ParamPackage& param) { | ||
| 153 | if (param.Get("engine", "") != "keyboard") { | ||
| 154 | return non_keyboard; | ||
| 155 | } else { | ||
| 156 | return getKeyName(param.Get("code", 0)); | ||
| 157 | } | ||
| 158 | }; | ||
| 159 | |||
| 160 | for (int button = 0; button < Settings::NativeButton::NumButtons; button++) { | ||
| 161 | button_map[button]->setText(KeyToText(buttons_param[button])); | ||
| 162 | } | ||
| 163 | |||
| 164 | for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) { | ||
| 165 | if (analogs_param[analog_id].Get("engine", "") != "analog_from_button") { | ||
| 166 | for (QPushButton* button : analog_map[analog_id]) { | ||
| 167 | if (button) | ||
| 168 | button->setText(non_keyboard); | ||
| 169 | } | ||
| 170 | } else { | ||
| 171 | for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; sub_button_id++) { | ||
| 172 | Common::ParamPackage param( | ||
| 173 | analogs_param[analog_id].Get(analog_sub_buttons[sub_button_id], "")); | ||
| 174 | if (analog_map[analog_id][sub_button_id]) | ||
| 175 | analog_map[analog_id][sub_button_id]->setText(KeyToText(param)); | ||
| 176 | } | ||
| 177 | } | ||
| 107 | } | 178 | } |
| 108 | } | 179 | } |
| 109 | 180 | ||
| 110 | void ConfigureInput::handleClick(Settings::NativeInput::Values input_id) { | 181 | void ConfigureInput::handleClick(QPushButton* button, std::function<void(int)> new_key_setter) { |
| 111 | QPushButton* button = button_map[input_id]; | ||
| 112 | button->setText(tr("[press key]")); | 182 | button->setText(tr("[press key]")); |
| 113 | button->setFocus(); | 183 | button->setFocus(); |
| 114 | 184 | ||
| 115 | current_input_id = input_id; | 185 | key_setter = new_key_setter; |
| 116 | 186 | ||
| 117 | grabKeyboard(); | 187 | grabKeyboard(); |
| 118 | grabMouse(); | 188 | grabMouse(); |
| @@ -123,23 +193,13 @@ void ConfigureInput::keyPressEvent(QKeyEvent* event) { | |||
| 123 | releaseKeyboard(); | 193 | releaseKeyboard(); |
| 124 | releaseMouse(); | 194 | releaseMouse(); |
| 125 | 195 | ||
| 126 | if (!current_input_id || !event) | 196 | if (!key_setter || !event) |
| 127 | return; | 197 | return; |
| 128 | 198 | ||
| 129 | if (event->key() != Qt::Key_Escape) | 199 | if (event->key() != Qt::Key_Escape) |
| 130 | setInput(*current_input_id, static_cast<Qt::Key>(event->key())); | 200 | (*key_setter)(event->key()); |
| 131 | 201 | ||
| 132 | updateButtonLabels(); | 202 | updateButtonLabels(); |
| 133 | current_input_id = boost::none; | 203 | key_setter = boost::none; |
| 134 | timer->stop(); | 204 | timer->stop(); |
| 135 | } | 205 | } |
| 136 | |||
| 137 | void ConfigureInput::setInput(Settings::NativeInput::Values input_id, Qt::Key key_pressed) { | ||
| 138 | // Remove duplicates | ||
| 139 | for (auto& pair : key_map) { | ||
| 140 | if (pair.second == key_pressed) | ||
| 141 | pair.second = Qt::Key_unknown; | ||
| 142 | } | ||
| 143 | |||
| 144 | key_map[input_id] = key_pressed; | ||
| 145 | } | ||
diff --git a/src/citra_qt/configure_input.h b/src/citra_qt/configure_input.h index bc343db83..c950fbcb4 100644 --- a/src/citra_qt/configure_input.h +++ b/src/citra_qt/configure_input.h | |||
| @@ -4,10 +4,14 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <array> | ||
| 8 | #include <functional> | ||
| 7 | #include <memory> | 9 | #include <memory> |
| 10 | #include <string> | ||
| 8 | #include <QKeyEvent> | 11 | #include <QKeyEvent> |
| 9 | #include <QWidget> | 12 | #include <QWidget> |
| 10 | #include <boost/optional.hpp> | 13 | #include <boost/optional.hpp> |
| 14 | #include "common/param_package.h" | ||
| 11 | #include "core/settings.h" | 15 | #include "core/settings.h" |
| 12 | #include "ui_configure_input.h" | 16 | #include "ui_configure_input.h" |
| 13 | 17 | ||
| @@ -31,15 +35,25 @@ public: | |||
| 31 | private: | 35 | private: |
| 32 | std::unique_ptr<Ui::ConfigureInput> ui; | 36 | std::unique_ptr<Ui::ConfigureInput> ui; |
| 33 | 37 | ||
| 34 | /// This input is currently awaiting configuration. | ||
| 35 | /// (i.e.: its corresponding QPushButton has been pressed.) | ||
| 36 | boost::optional<Settings::NativeInput::Values> current_input_id; | ||
| 37 | std::unique_ptr<QTimer> timer; | 38 | std::unique_ptr<QTimer> timer; |
| 38 | 39 | ||
| 39 | /// Each input is represented by a QPushButton. | 40 | /// This will be the the setting function when an input is awaiting configuration. |
| 40 | std::map<Settings::NativeInput::Values, QPushButton*> button_map; | 41 | boost::optional<std::function<void(int)>> key_setter; |
| 41 | /// Each input is configured to respond to the press of a Qt::Key. | 42 | |
| 42 | std::map<Settings::NativeInput::Values, Qt::Key> key_map; | 43 | std::array<Common::ParamPackage, Settings::NativeButton::NumButtons> buttons_param; |
| 44 | std::array<Common::ParamPackage, Settings::NativeAnalog::NumAnalogs> analogs_param; | ||
| 45 | |||
| 46 | static constexpr int ANALOG_SUB_BUTTONS_NUM = 5; | ||
| 47 | |||
| 48 | /// Each button input is represented by a QPushButton. | ||
| 49 | std::array<QPushButton*, Settings::NativeButton::NumButtons> button_map; | ||
| 50 | |||
| 51 | /// Each analog input is represented by five QPushButtons which represents up, down, left, right | ||
| 52 | /// and modifier | ||
| 53 | std::array<std::array<QPushButton*, ANALOG_SUB_BUTTONS_NUM>, Settings::NativeAnalog::NumAnalogs> | ||
| 54 | analog_map; | ||
| 55 | |||
| 56 | static const std::array<std::string, ANALOG_SUB_BUTTONS_NUM> analog_sub_buttons; | ||
| 43 | 57 | ||
| 44 | /// Load configuration settings. | 58 | /// Load configuration settings. |
| 45 | void loadConfiguration(); | 59 | void loadConfiguration(); |
| @@ -48,10 +62,8 @@ private: | |||
| 48 | /// Update UI to reflect current configuration. | 62 | /// Update UI to reflect current configuration. |
| 49 | void updateButtonLabels(); | 63 | void updateButtonLabels(); |
| 50 | 64 | ||
| 51 | /// Called when the button corresponding to input_id was pressed. | 65 | /// Called when the button was pressed. |
| 52 | void handleClick(Settings::NativeInput::Values input_id); | 66 | void handleClick(QPushButton* button, std::function<void(int)> new_key_setter); |
| 53 | /// Handle key press events. | 67 | /// Handle key press events. |
| 54 | void keyPressEvent(QKeyEvent* event) override; | 68 | void keyPressEvent(QKeyEvent* event) override; |
| 55 | /// Configure input input_id to respond to key key_pressed. | ||
| 56 | void setInput(Settings::NativeInput::Values input_id, Qt::Key key_pressed); | ||
| 57 | }; | 69 | }; |
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 8a6170257..13277a5c2 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt | |||
| @@ -35,6 +35,7 @@ set(SRCS | |||
| 35 | memory_util.cpp | 35 | memory_util.cpp |
| 36 | microprofile.cpp | 36 | microprofile.cpp |
| 37 | misc.cpp | 37 | misc.cpp |
| 38 | param_package.cpp | ||
| 38 | scm_rev.cpp | 39 | scm_rev.cpp |
| 39 | string_util.cpp | 40 | string_util.cpp |
| 40 | symbols.cpp | 41 | symbols.cpp |
| @@ -66,6 +67,7 @@ set(HEADERS | |||
| 66 | memory_util.h | 67 | memory_util.h |
| 67 | microprofile.h | 68 | microprofile.h |
| 68 | microprofileui.h | 69 | microprofileui.h |
| 70 | param_package.h | ||
| 69 | platform.h | 71 | platform.h |
| 70 | quaternion.h | 72 | quaternion.h |
| 71 | scm_rev.h | 73 | scm_rev.h |
diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp index 737e1d57f..42f6a9918 100644 --- a/src/common/logging/backend.cpp +++ b/src/common/logging/backend.cpp | |||
| @@ -71,6 +71,7 @@ namespace Log { | |||
| 71 | CLS(Audio) \ | 71 | CLS(Audio) \ |
| 72 | SUB(Audio, DSP) \ | 72 | SUB(Audio, DSP) \ |
| 73 | SUB(Audio, Sink) \ | 73 | SUB(Audio, Sink) \ |
| 74 | CLS(Input) \ | ||
| 74 | CLS(Loader) | 75 | CLS(Loader) |
| 75 | 76 | ||
| 76 | // GetClassName is a macro defined by Windows.h, grrr... | 77 | // GetClassName is a macro defined by Windows.h, grrr... |
diff --git a/src/common/logging/log.h b/src/common/logging/log.h index 4b0f8ff03..1b905f66c 100644 --- a/src/common/logging/log.h +++ b/src/common/logging/log.h | |||
| @@ -89,6 +89,7 @@ enum class Class : ClassType { | |||
| 89 | Audio_DSP, ///< The HLE implementation of the DSP | 89 | Audio_DSP, ///< The HLE implementation of the DSP |
| 90 | Audio_Sink, ///< Emulator audio output backend | 90 | Audio_Sink, ///< Emulator audio output backend |
| 91 | Loader, ///< ROM loader | 91 | Loader, ///< ROM loader |
| 92 | Input, ///< Input emulation | ||
| 92 | Count ///< Total number of logging classes | 93 | Count ///< Total number of logging classes |
| 93 | }; | 94 | }; |
| 94 | 95 | ||
diff --git a/src/common/param_package.cpp b/src/common/param_package.cpp new file mode 100644 index 000000000..3a6ef8c27 --- /dev/null +++ b/src/common/param_package.cpp | |||
| @@ -0,0 +1,120 @@ | |||
| 1 | // Copyright 2017 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <array> | ||
| 6 | #include <vector> | ||
| 7 | #include "common/logging/log.h" | ||
| 8 | #include "common/param_package.h" | ||
| 9 | #include "common/string_util.h" | ||
| 10 | |||
| 11 | namespace Common { | ||
| 12 | |||
| 13 | constexpr char KEY_VALUE_SEPARATOR = ':'; | ||
| 14 | constexpr char PARAM_SEPARATOR = ','; | ||
| 15 | constexpr char ESCAPE_CHARACTER = '$'; | ||
| 16 | const std::string KEY_VALUE_SEPARATOR_ESCAPE{ESCAPE_CHARACTER, '0'}; | ||
| 17 | const std::string PARAM_SEPARATOR_ESCAPE{ESCAPE_CHARACTER, '1'}; | ||
| 18 | const std::string ESCAPE_CHARACTER_ESCAPE{ESCAPE_CHARACTER, '2'}; | ||
| 19 | |||
| 20 | ParamPackage::ParamPackage(const std::string& serialized) { | ||
| 21 | std::vector<std::string> pairs; | ||
| 22 | Common::SplitString(serialized, PARAM_SEPARATOR, pairs); | ||
| 23 | |||
| 24 | for (const std::string& pair : pairs) { | ||
| 25 | std::vector<std::string> key_value; | ||
| 26 | Common::SplitString(pair, KEY_VALUE_SEPARATOR, key_value); | ||
| 27 | if (key_value.size() != 2) { | ||
| 28 | LOG_ERROR(Common, "invalid key pair %s", pair.c_str()); | ||
| 29 | continue; | ||
| 30 | } | ||
| 31 | |||
| 32 | for (std::string& part : key_value) { | ||
| 33 | part = Common::ReplaceAll(part, KEY_VALUE_SEPARATOR_ESCAPE, {KEY_VALUE_SEPARATOR}); | ||
| 34 | part = Common::ReplaceAll(part, PARAM_SEPARATOR_ESCAPE, {PARAM_SEPARATOR}); | ||
| 35 | part = Common::ReplaceAll(part, ESCAPE_CHARACTER_ESCAPE, {ESCAPE_CHARACTER}); | ||
| 36 | } | ||
| 37 | |||
| 38 | Set(key_value[0], key_value[1]); | ||
| 39 | } | ||
| 40 | } | ||
| 41 | |||
| 42 | ParamPackage::ParamPackage(std::initializer_list<DataType::value_type> list) : data(list) {} | ||
| 43 | |||
| 44 | std::string ParamPackage::Serialize() const { | ||
| 45 | if (data.empty()) | ||
| 46 | return ""; | ||
| 47 | |||
| 48 | std::string result; | ||
| 49 | |||
| 50 | for (const auto& pair : data) { | ||
| 51 | std::array<std::string, 2> key_value{{pair.first, pair.second}}; | ||
| 52 | for (std::string& part : key_value) { | ||
| 53 | part = Common::ReplaceAll(part, {ESCAPE_CHARACTER}, ESCAPE_CHARACTER_ESCAPE); | ||
| 54 | part = Common::ReplaceAll(part, {PARAM_SEPARATOR}, PARAM_SEPARATOR_ESCAPE); | ||
| 55 | part = Common::ReplaceAll(part, {KEY_VALUE_SEPARATOR}, KEY_VALUE_SEPARATOR_ESCAPE); | ||
| 56 | } | ||
| 57 | result += key_value[0] + KEY_VALUE_SEPARATOR + key_value[1] + PARAM_SEPARATOR; | ||
| 58 | } | ||
| 59 | |||
| 60 | result.pop_back(); // discard the trailing PARAM_SEPARATOR | ||
| 61 | return result; | ||
| 62 | } | ||
| 63 | |||
| 64 | std::string ParamPackage::Get(const std::string& key, const std::string& default_value) const { | ||
| 65 | auto pair = data.find(key); | ||
| 66 | if (pair == data.end()) { | ||
| 67 | LOG_DEBUG(Common, "key %s not found", key.c_str()); | ||
| 68 | return default_value; | ||
| 69 | } | ||
| 70 | |||
| 71 | return pair->second; | ||
| 72 | } | ||
| 73 | |||
| 74 | int ParamPackage::Get(const std::string& key, int default_value) const { | ||
| 75 | auto pair = data.find(key); | ||
| 76 | if (pair == data.end()) { | ||
| 77 | LOG_DEBUG(Common, "key %s not found", key.c_str()); | ||
| 78 | return default_value; | ||
| 79 | } | ||
| 80 | |||
| 81 | try { | ||
| 82 | return std::stoi(pair->second); | ||
| 83 | } catch (const std::logic_error&) { | ||
| 84 | LOG_ERROR(Common, "failed to convert %s to int", pair->second.c_str()); | ||
| 85 | return default_value; | ||
| 86 | } | ||
| 87 | } | ||
| 88 | |||
| 89 | float ParamPackage::Get(const std::string& key, float default_value) const { | ||
| 90 | auto pair = data.find(key); | ||
| 91 | if (pair == data.end()) { | ||
| 92 | LOG_DEBUG(Common, "key %s not found", key.c_str()); | ||
| 93 | return default_value; | ||
| 94 | } | ||
| 95 | |||
| 96 | try { | ||
| 97 | return std::stof(pair->second); | ||
| 98 | } catch (const std::logic_error&) { | ||
| 99 | LOG_ERROR(Common, "failed to convert %s to float", pair->second.c_str()); | ||
| 100 | return default_value; | ||
| 101 | } | ||
| 102 | } | ||
| 103 | |||
| 104 | void ParamPackage::Set(const std::string& key, const std::string& value) { | ||
| 105 | data[key] = value; | ||
| 106 | } | ||
| 107 | |||
| 108 | void ParamPackage::Set(const std::string& key, int value) { | ||
| 109 | data[key] = std::to_string(value); | ||
| 110 | } | ||
| 111 | |||
| 112 | void ParamPackage::Set(const std::string& key, float value) { | ||
| 113 | data[key] = std::to_string(value); | ||
| 114 | } | ||
| 115 | |||
| 116 | bool ParamPackage::Has(const std::string& key) const { | ||
| 117 | return data.find(key) != data.end(); | ||
| 118 | } | ||
| 119 | |||
| 120 | } // namespace Common | ||
diff --git a/src/common/param_package.h b/src/common/param_package.h new file mode 100644 index 000000000..c4c11b221 --- /dev/null +++ b/src/common/param_package.h | |||
| @@ -0,0 +1,40 @@ | |||
| 1 | // Copyright 2017 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <initializer_list> | ||
| 8 | #include <string> | ||
| 9 | #include <unordered_map> | ||
| 10 | |||
| 11 | namespace Common { | ||
| 12 | |||
| 13 | /// A string-based key-value container supporting serializing to and deserializing from a string | ||
| 14 | class ParamPackage { | ||
| 15 | public: | ||
| 16 | using DataType = std::unordered_map<std::string, std::string>; | ||
| 17 | |||
| 18 | ParamPackage() = default; | ||
| 19 | explicit ParamPackage(const std::string& serialized); | ||
| 20 | ParamPackage(std::initializer_list<DataType::value_type> list); | ||
| 21 | ParamPackage(const ParamPackage& other) = default; | ||
| 22 | ParamPackage(ParamPackage&& other) = default; | ||
| 23 | |||
| 24 | ParamPackage& operator=(const ParamPackage& other) = default; | ||
| 25 | ParamPackage& operator=(ParamPackage&& other) = default; | ||
| 26 | |||
| 27 | std::string Serialize() const; | ||
| 28 | std::string Get(const std::string& key, const std::string& default_value) const; | ||
| 29 | int Get(const std::string& key, int default_value) const; | ||
| 30 | float Get(const std::string& key, float default_value) const; | ||
| 31 | void Set(const std::string& key, const std::string& value); | ||
| 32 | void Set(const std::string& key, int value); | ||
| 33 | void Set(const std::string& key, float value); | ||
| 34 | bool Has(const std::string& key) const; | ||
| 35 | |||
| 36 | private: | ||
| 37 | DataType data; | ||
| 38 | }; | ||
| 39 | |||
| 40 | } // namespace Common | ||
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index ffd67f074..61a0b1cc3 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt | |||
| @@ -34,7 +34,6 @@ set(SRCS | |||
| 34 | frontend/camera/factory.cpp | 34 | frontend/camera/factory.cpp |
| 35 | frontend/camera/interface.cpp | 35 | frontend/camera/interface.cpp |
| 36 | frontend/emu_window.cpp | 36 | frontend/emu_window.cpp |
| 37 | frontend/key_map.cpp | ||
| 38 | frontend/motion_emu.cpp | 37 | frontend/motion_emu.cpp |
| 39 | gdbstub/gdbstub.cpp | 38 | gdbstub/gdbstub.cpp |
| 40 | hle/config_mem.cpp | 39 | hle/config_mem.cpp |
| @@ -218,7 +217,7 @@ set(HEADERS | |||
| 218 | frontend/camera/factory.h | 217 | frontend/camera/factory.h |
| 219 | frontend/camera/interface.h | 218 | frontend/camera/interface.h |
| 220 | frontend/emu_window.h | 219 | frontend/emu_window.h |
| 221 | frontend/key_map.h | 220 | frontend/input.h |
| 222 | frontend/motion_emu.h | 221 | frontend/motion_emu.h |
| 223 | gdbstub/gdbstub.h | 222 | gdbstub/gdbstub.h |
| 224 | hle/config_mem.h | 223 | hle/config_mem.h |
diff --git a/src/core/frontend/emu_window.cpp b/src/core/frontend/emu_window.cpp index a155b657d..73a44bfe7 100644 --- a/src/core/frontend/emu_window.cpp +++ b/src/core/frontend/emu_window.cpp | |||
| @@ -7,33 +7,9 @@ | |||
| 7 | #include "common/assert.h" | 7 | #include "common/assert.h" |
| 8 | #include "core/core.h" | 8 | #include "core/core.h" |
| 9 | #include "core/frontend/emu_window.h" | 9 | #include "core/frontend/emu_window.h" |
| 10 | #include "core/frontend/key_map.h" | 10 | #include "core/settings.h" |
| 11 | #include "video_core/video_core.h" | 11 | #include "video_core/video_core.h" |
| 12 | 12 | ||
| 13 | void EmuWindow::ButtonPressed(Service::HID::PadState pad) { | ||
| 14 | pad_state.hex |= pad.hex; | ||
| 15 | } | ||
| 16 | |||
| 17 | void EmuWindow::ButtonReleased(Service::HID::PadState pad) { | ||
| 18 | pad_state.hex &= ~pad.hex; | ||
| 19 | } | ||
| 20 | |||
| 21 | void EmuWindow::CirclePadUpdated(float x, float y) { | ||
| 22 | constexpr int MAX_CIRCLEPAD_POS = 0x9C; // Max value for a circle pad position | ||
| 23 | |||
| 24 | // Make sure the coordinates are in the unit circle, | ||
| 25 | // otherwise normalize it. | ||
| 26 | float r = x * x + y * y; | ||
| 27 | if (r > 1) { | ||
| 28 | r = std::sqrt(r); | ||
| 29 | x /= r; | ||
| 30 | y /= r; | ||
| 31 | } | ||
| 32 | |||
| 33 | circle_pad_x = static_cast<s16>(x * MAX_CIRCLEPAD_POS); | ||
| 34 | circle_pad_y = static_cast<s16>(y * MAX_CIRCLEPAD_POS); | ||
| 35 | } | ||
| 36 | |||
| 37 | /** | 13 | /** |
| 38 | * Check if the given x/y coordinates are within the touchpad specified by the framebuffer layout | 14 | * Check if the given x/y coordinates are within the touchpad specified by the framebuffer layout |
| 39 | * @param layout FramebufferLayout object describing the framebuffer size and screen positions | 15 | * @param layout FramebufferLayout object describing the framebuffer size and screen positions |
diff --git a/src/core/frontend/emu_window.h b/src/core/frontend/emu_window.h index 1ba64c92b..36f2667fa 100644 --- a/src/core/frontend/emu_window.h +++ b/src/core/frontend/emu_window.h | |||
| @@ -10,7 +10,6 @@ | |||
| 10 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
| 11 | #include "common/framebuffer_layout.h" | 11 | #include "common/framebuffer_layout.h" |
| 12 | #include "common/math_util.h" | 12 | #include "common/math_util.h" |
| 13 | #include "core/hle/service/hid/hid.h" | ||
| 14 | 13 | ||
| 15 | /** | 14 | /** |
| 16 | * Abstraction class used to provide an interface between emulation code and the frontend | 15 | * Abstraction class used to provide an interface between emulation code and the frontend |
| @@ -52,30 +51,6 @@ public: | |||
| 52 | /// Releases (dunno if this is the "right" word) the GLFW context from the caller thread | 51 | /// Releases (dunno if this is the "right" word) the GLFW context from the caller thread |
| 53 | virtual void DoneCurrent() = 0; | 52 | virtual void DoneCurrent() = 0; |
| 54 | 53 | ||
| 55 | virtual void ReloadSetKeymaps() = 0; | ||
| 56 | |||
| 57 | /** | ||
| 58 | * Signals a button press action to the HID module. | ||
| 59 | * @param pad_state indicates which button to press | ||
| 60 | * @note only handles real buttons (A/B/X/Y/...), excluding analog inputs like the circle pad. | ||
| 61 | */ | ||
| 62 | void ButtonPressed(Service::HID::PadState pad_state); | ||
| 63 | |||
| 64 | /** | ||
| 65 | * Signals a button release action to the HID module. | ||
| 66 | * @param pad_state indicates which button to press | ||
| 67 | * @note only handles real buttons (A/B/X/Y/...), excluding analog inputs like the circle pad. | ||
| 68 | */ | ||
| 69 | void ButtonReleased(Service::HID::PadState pad_state); | ||
| 70 | |||
| 71 | /** | ||
| 72 | * Signals a circle pad change action to the HID module. | ||
| 73 | * @param x new x-coordinate of the circle pad, in the range [-1.0, 1.0] | ||
| 74 | * @param y new y-coordinate of the circle pad, in the range [-1.0, 1.0] | ||
| 75 | * @note the coordinates will be normalized if the radius is larger than 1 | ||
| 76 | */ | ||
| 77 | void CirclePadUpdated(float x, float y); | ||
| 78 | |||
| 79 | /** | 54 | /** |
| 80 | * Signal that a touch pressed event has occurred (e.g. mouse click pressed) | 55 | * Signal that a touch pressed event has occurred (e.g. mouse click pressed) |
| 81 | * @param framebuffer_x Framebuffer x-coordinate that was pressed | 56 | * @param framebuffer_x Framebuffer x-coordinate that was pressed |
| @@ -115,27 +90,6 @@ public: | |||
| 115 | void GyroscopeChanged(float x, float y, float z); | 90 | void GyroscopeChanged(float x, float y, float z); |
| 116 | 91 | ||
| 117 | /** | 92 | /** |
| 118 | * Gets the current pad state (which buttons are pressed). | ||
| 119 | * @note This should be called by the core emu thread to get a state set by the window thread. | ||
| 120 | * @note This doesn't include analog input like circle pad direction | ||
| 121 | * @todo Fix this function to be thread-safe. | ||
| 122 | * @return PadState object indicating the current pad state | ||
| 123 | */ | ||
| 124 | Service::HID::PadState GetPadState() const { | ||
| 125 | return pad_state; | ||
| 126 | } | ||
| 127 | |||
| 128 | /** | ||
| 129 | * Gets the current circle pad state. | ||
| 130 | * @note This should be called by the core emu thread to get a state set by the window thread. | ||
| 131 | * @todo Fix this function to be thread-safe. | ||
| 132 | * @return std::tuple of (x, y), where `x` and `y` are the circle pad coordinates | ||
| 133 | */ | ||
| 134 | std::tuple<s16, s16> GetCirclePadState() const { | ||
| 135 | return std::make_tuple(circle_pad_x, circle_pad_y); | ||
| 136 | } | ||
| 137 | |||
| 138 | /** | ||
| 139 | * Gets the current touch screen state (touch X/Y coordinates and whether or not it is pressed). | 93 | * Gets the current touch screen state (touch X/Y coordinates and whether or not it is pressed). |
| 140 | * @note This should be called by the core emu thread to get a state set by the window thread. | 94 | * @note This should be called by the core emu thread to get a state set by the window thread. |
| 141 | * @todo Fix this function to be thread-safe. | 95 | * @todo Fix this function to be thread-safe. |
| @@ -230,11 +184,8 @@ protected: | |||
| 230 | // TODO: Find a better place to set this. | 184 | // TODO: Find a better place to set this. |
| 231 | config.min_client_area_size = std::make_pair(400u, 480u); | 185 | config.min_client_area_size = std::make_pair(400u, 480u); |
| 232 | active_config = config; | 186 | active_config = config; |
| 233 | pad_state.hex = 0; | ||
| 234 | touch_x = 0; | 187 | touch_x = 0; |
| 235 | touch_y = 0; | 188 | touch_y = 0; |
| 236 | circle_pad_x = 0; | ||
| 237 | circle_pad_y = 0; | ||
| 238 | touch_pressed = false; | 189 | touch_pressed = false; |
| 239 | accel_x = 0; | 190 | accel_x = 0; |
| 240 | accel_y = -512; | 191 | accel_y = -512; |
| @@ -304,9 +255,6 @@ private: | |||
| 304 | u16 touch_x; ///< Touchpad X-position in native 3DS pixel coordinates (0-320) | 255 | u16 touch_x; ///< Touchpad X-position in native 3DS pixel coordinates (0-320) |
| 305 | u16 touch_y; ///< Touchpad Y-position in native 3DS pixel coordinates (0-240) | 256 | u16 touch_y; ///< Touchpad Y-position in native 3DS pixel coordinates (0-240) |
| 306 | 257 | ||
| 307 | s16 circle_pad_x; ///< Circle pad X-position in native 3DS pixel coordinates (-156 - 156) | ||
| 308 | s16 circle_pad_y; ///< Circle pad Y-position in native 3DS pixel coordinates (-156 - 156) | ||
| 309 | |||
| 310 | std::mutex accel_mutex; | 258 | std::mutex accel_mutex; |
| 311 | s16 accel_x; ///< Accelerometer X-axis value in native 3DS units | 259 | s16 accel_x; ///< Accelerometer X-axis value in native 3DS units |
| 312 | s16 accel_y; ///< Accelerometer Y-axis value in native 3DS units | 260 | s16 accel_y; ///< Accelerometer Y-axis value in native 3DS units |
| @@ -321,6 +269,4 @@ private: | |||
| 321 | * Clip the provided coordinates to be inside the touchscreen area. | 269 | * Clip the provided coordinates to be inside the touchscreen area. |
| 322 | */ | 270 | */ |
| 323 | std::tuple<unsigned, unsigned> ClipToTouchScreen(unsigned new_x, unsigned new_y); | 271 | std::tuple<unsigned, unsigned> ClipToTouchScreen(unsigned new_x, unsigned new_y); |
| 324 | |||
| 325 | Service::HID::PadState pad_state; | ||
| 326 | }; | 272 | }; |
diff --git a/src/core/frontend/input.h b/src/core/frontend/input.h new file mode 100644 index 000000000..0a5713dc0 --- /dev/null +++ b/src/core/frontend/input.h | |||
| @@ -0,0 +1,110 @@ | |||
| 1 | // Copyright 2017 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <memory> | ||
| 8 | #include <string> | ||
| 9 | #include <tuple> | ||
| 10 | #include <unordered_map> | ||
| 11 | #include <utility> | ||
| 12 | #include "common/logging/log.h" | ||
| 13 | #include "common/param_package.h" | ||
| 14 | |||
| 15 | namespace Input { | ||
| 16 | |||
| 17 | /// An abstract class template for an input device (a button, an analog input, etc.). | ||
| 18 | template <typename StatusType> | ||
| 19 | class InputDevice { | ||
| 20 | public: | ||
| 21 | virtual ~InputDevice() = default; | ||
| 22 | virtual StatusType GetStatus() const { | ||
| 23 | return {}; | ||
| 24 | } | ||
| 25 | }; | ||
| 26 | |||
| 27 | /// An abstract class template for a factory that can create input devices. | ||
| 28 | template <typename InputDeviceType> | ||
| 29 | class Factory { | ||
| 30 | public: | ||
| 31 | virtual ~Factory() = default; | ||
| 32 | virtual std::unique_ptr<InputDeviceType> Create(const Common::ParamPackage&) = 0; | ||
| 33 | }; | ||
| 34 | |||
| 35 | namespace Impl { | ||
| 36 | |||
| 37 | template <typename InputDeviceType> | ||
| 38 | using FactoryListType = std::unordered_map<std::string, std::shared_ptr<Factory<InputDeviceType>>>; | ||
| 39 | |||
| 40 | template <typename InputDeviceType> | ||
| 41 | struct FactoryList { | ||
| 42 | static FactoryListType<InputDeviceType> list; | ||
| 43 | }; | ||
| 44 | |||
| 45 | template <typename InputDeviceType> | ||
| 46 | FactoryListType<InputDeviceType> FactoryList<InputDeviceType>::list; | ||
| 47 | |||
| 48 | } // namespace Impl | ||
| 49 | |||
| 50 | /** | ||
| 51 | * Registers an input device factory. | ||
| 52 | * @tparam InputDeviceType the type of input devices the factory can create | ||
| 53 | * @param name the name of the factory. Will be used to match the "engine" parameter when creating | ||
| 54 | * a device | ||
| 55 | * @param factory the factory object to register | ||
| 56 | */ | ||
| 57 | template <typename InputDeviceType> | ||
| 58 | void RegisterFactory(const std::string& name, std::shared_ptr<Factory<InputDeviceType>> factory) { | ||
| 59 | auto pair = std::make_pair(name, std::move(factory)); | ||
| 60 | if (!Impl::FactoryList<InputDeviceType>::list.insert(std::move(pair)).second) { | ||
| 61 | LOG_ERROR(Input, "Factory %s already registered", name.c_str()); | ||
| 62 | } | ||
| 63 | } | ||
| 64 | |||
| 65 | /** | ||
| 66 | * Unregisters an input device factory. | ||
| 67 | * @tparam InputDeviceType the type of input devices the factory can create | ||
| 68 | * @param name the name of the factory to unregister | ||
| 69 | */ | ||
| 70 | template <typename InputDeviceType> | ||
| 71 | void UnregisterFactory(const std::string& name) { | ||
| 72 | if (Impl::FactoryList<InputDeviceType>::list.erase(name) == 0) { | ||
| 73 | LOG_ERROR(Input, "Factory %s not registered", name.c_str()); | ||
| 74 | } | ||
| 75 | } | ||
| 76 | |||
| 77 | /** | ||
| 78 | * Create an input device from given paramters. | ||
| 79 | * @tparam InputDeviceType the type of input devices to create | ||
| 80 | * @param params a serialized ParamPackage string contains all parameters for creating the device | ||
| 81 | */ | ||
| 82 | template <typename InputDeviceType> | ||
| 83 | std::unique_ptr<InputDeviceType> CreateDevice(const std::string& params) { | ||
| 84 | const Common::ParamPackage package(params); | ||
| 85 | const std::string engine = package.Get("engine", "null"); | ||
| 86 | const auto& factory_list = Impl::FactoryList<InputDeviceType>::list; | ||
| 87 | const auto pair = factory_list.find(engine); | ||
| 88 | if (pair == factory_list.end()) { | ||
| 89 | if (engine != "null") { | ||
| 90 | LOG_ERROR(Input, "Unknown engine name: %s", engine.c_str()); | ||
| 91 | } | ||
| 92 | return std::make_unique<InputDeviceType>(); | ||
| 93 | } | ||
| 94 | return pair->second->Create(package); | ||
| 95 | } | ||
| 96 | |||
| 97 | /** | ||
| 98 | * A button device is an input device that returns bool as status. | ||
| 99 | * true for pressed; false for released. | ||
| 100 | */ | ||
| 101 | using ButtonDevice = InputDevice<bool>; | ||
| 102 | |||
| 103 | /** | ||
| 104 | * An analog device is an input device that returns a tuple of x and y coordinates as status. The | ||
| 105 | * coordinates are within the unit circle. x+ is defined as right direction, and y+ is defined as up | ||
| 106 | * direction | ||
| 107 | */ | ||
| 108 | using AnalogDevice = InputDevice<std::tuple<float, float>>; | ||
| 109 | |||
| 110 | } // namespace Input | ||
diff --git a/src/core/frontend/key_map.cpp b/src/core/frontend/key_map.cpp deleted file mode 100644 index 15f0e079c..000000000 --- a/src/core/frontend/key_map.cpp +++ /dev/null | |||
| @@ -1,152 +0,0 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <map> | ||
| 6 | #include "core/frontend/emu_window.h" | ||
| 7 | #include "core/frontend/key_map.h" | ||
| 8 | |||
| 9 | namespace KeyMap { | ||
| 10 | |||
| 11 | // TODO (wwylele): currently we treat c-stick as four direction buttons | ||
| 12 | // and map it directly to EmuWindow::ButtonPressed. | ||
| 13 | // It should go the analog input way like circle pad does. | ||
| 14 | const std::array<KeyTarget, Settings::NativeInput::NUM_INPUTS> mapping_targets = {{ | ||
| 15 | Service::HID::PAD_A, | ||
| 16 | Service::HID::PAD_B, | ||
| 17 | Service::HID::PAD_X, | ||
| 18 | Service::HID::PAD_Y, | ||
| 19 | Service::HID::PAD_L, | ||
| 20 | Service::HID::PAD_R, | ||
| 21 | Service::HID::PAD_ZL, | ||
| 22 | Service::HID::PAD_ZR, | ||
| 23 | Service::HID::PAD_START, | ||
| 24 | Service::HID::PAD_SELECT, | ||
| 25 | Service::HID::PAD_NONE, | ||
| 26 | Service::HID::PAD_UP, | ||
| 27 | Service::HID::PAD_DOWN, | ||
| 28 | Service::HID::PAD_LEFT, | ||
| 29 | Service::HID::PAD_RIGHT, | ||
| 30 | Service::HID::PAD_C_UP, | ||
| 31 | Service::HID::PAD_C_DOWN, | ||
| 32 | Service::HID::PAD_C_LEFT, | ||
| 33 | Service::HID::PAD_C_RIGHT, | ||
| 34 | |||
| 35 | IndirectTarget::CirclePadUp, | ||
| 36 | IndirectTarget::CirclePadDown, | ||
| 37 | IndirectTarget::CirclePadLeft, | ||
| 38 | IndirectTarget::CirclePadRight, | ||
| 39 | IndirectTarget::CirclePadModifier, | ||
| 40 | }}; | ||
| 41 | |||
| 42 | static std::map<HostDeviceKey, KeyTarget> key_map; | ||
| 43 | static int next_device_id = 0; | ||
| 44 | |||
| 45 | static bool circle_pad_up = false; | ||
| 46 | static bool circle_pad_down = false; | ||
| 47 | static bool circle_pad_left = false; | ||
| 48 | static bool circle_pad_right = false; | ||
| 49 | static bool circle_pad_modifier = false; | ||
| 50 | |||
| 51 | static void UpdateCirclePad(EmuWindow& emu_window) { | ||
| 52 | constexpr float SQRT_HALF = 0.707106781f; | ||
| 53 | int x = 0, y = 0; | ||
| 54 | |||
| 55 | if (circle_pad_right) | ||
| 56 | ++x; | ||
| 57 | if (circle_pad_left) | ||
| 58 | --x; | ||
| 59 | if (circle_pad_up) | ||
| 60 | ++y; | ||
| 61 | if (circle_pad_down) | ||
| 62 | --y; | ||
| 63 | |||
| 64 | float modifier = circle_pad_modifier ? Settings::values.pad_circle_modifier_scale : 1.0f; | ||
| 65 | emu_window.CirclePadUpdated(x * modifier * (y == 0 ? 1.0f : SQRT_HALF), | ||
| 66 | y * modifier * (x == 0 ? 1.0f : SQRT_HALF)); | ||
| 67 | } | ||
| 68 | |||
| 69 | int NewDeviceId() { | ||
| 70 | return next_device_id++; | ||
| 71 | } | ||
| 72 | |||
| 73 | void SetKeyMapping(HostDeviceKey key, KeyTarget target) { | ||
| 74 | key_map[key] = target; | ||
| 75 | } | ||
| 76 | |||
| 77 | void ClearKeyMapping(int device_id) { | ||
| 78 | auto iter = key_map.begin(); | ||
| 79 | while (iter != key_map.end()) { | ||
| 80 | if (iter->first.device_id == device_id) | ||
| 81 | key_map.erase(iter++); | ||
| 82 | else | ||
| 83 | ++iter; | ||
| 84 | } | ||
| 85 | } | ||
| 86 | |||
| 87 | void PressKey(EmuWindow& emu_window, HostDeviceKey key) { | ||
| 88 | auto target = key_map.find(key); | ||
| 89 | if (target == key_map.end()) | ||
| 90 | return; | ||
| 91 | |||
| 92 | if (target->second.direct) { | ||
| 93 | emu_window.ButtonPressed({{target->second.target.direct_target_hex}}); | ||
| 94 | } else { | ||
| 95 | switch (target->second.target.indirect_target) { | ||
| 96 | case IndirectTarget::CirclePadUp: | ||
| 97 | circle_pad_up = true; | ||
| 98 | UpdateCirclePad(emu_window); | ||
| 99 | break; | ||
| 100 | case IndirectTarget::CirclePadDown: | ||
| 101 | circle_pad_down = true; | ||
| 102 | UpdateCirclePad(emu_window); | ||
| 103 | break; | ||
| 104 | case IndirectTarget::CirclePadLeft: | ||
| 105 | circle_pad_left = true; | ||
| 106 | UpdateCirclePad(emu_window); | ||
| 107 | break; | ||
| 108 | case IndirectTarget::CirclePadRight: | ||
| 109 | circle_pad_right = true; | ||
| 110 | UpdateCirclePad(emu_window); | ||
| 111 | break; | ||
| 112 | case IndirectTarget::CirclePadModifier: | ||
| 113 | circle_pad_modifier = true; | ||
| 114 | UpdateCirclePad(emu_window); | ||
| 115 | break; | ||
| 116 | } | ||
| 117 | } | ||
| 118 | } | ||
| 119 | |||
| 120 | void ReleaseKey(EmuWindow& emu_window, HostDeviceKey key) { | ||
| 121 | auto target = key_map.find(key); | ||
| 122 | if (target == key_map.end()) | ||
| 123 | return; | ||
| 124 | |||
| 125 | if (target->second.direct) { | ||
| 126 | emu_window.ButtonReleased({{target->second.target.direct_target_hex}}); | ||
| 127 | } else { | ||
| 128 | switch (target->second.target.indirect_target) { | ||
| 129 | case IndirectTarget::CirclePadUp: | ||
| 130 | circle_pad_up = false; | ||
| 131 | UpdateCirclePad(emu_window); | ||
| 132 | break; | ||
| 133 | case IndirectTarget::CirclePadDown: | ||
| 134 | circle_pad_down = false; | ||
| 135 | UpdateCirclePad(emu_window); | ||
| 136 | break; | ||
| 137 | case IndirectTarget::CirclePadLeft: | ||
| 138 | circle_pad_left = false; | ||
| 139 | UpdateCirclePad(emu_window); | ||
| 140 | break; | ||
| 141 | case IndirectTarget::CirclePadRight: | ||
| 142 | circle_pad_right = false; | ||
| 143 | UpdateCirclePad(emu_window); | ||
| 144 | break; | ||
| 145 | case IndirectTarget::CirclePadModifier: | ||
| 146 | circle_pad_modifier = false; | ||
| 147 | UpdateCirclePad(emu_window); | ||
| 148 | break; | ||
| 149 | } | ||
| 150 | } | ||
| 151 | } | ||
| 152 | } | ||
diff --git a/src/core/frontend/key_map.h b/src/core/frontend/key_map.h deleted file mode 100644 index 040794578..000000000 --- a/src/core/frontend/key_map.h +++ /dev/null | |||
| @@ -1,93 +0,0 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <array> | ||
| 8 | #include <tuple> | ||
| 9 | #include "core/hle/service/hid/hid.h" | ||
| 10 | |||
| 11 | class EmuWindow; | ||
| 12 | |||
| 13 | namespace KeyMap { | ||
| 14 | |||
| 15 | /** | ||
| 16 | * Represents key mapping targets that are not real 3DS buttons. | ||
| 17 | * They will be handled by KeyMap and translated to 3DS input. | ||
| 18 | */ | ||
| 19 | enum class IndirectTarget { | ||
| 20 | CirclePadUp, | ||
| 21 | CirclePadDown, | ||
| 22 | CirclePadLeft, | ||
| 23 | CirclePadRight, | ||
| 24 | CirclePadModifier, | ||
| 25 | }; | ||
| 26 | |||
| 27 | /** | ||
| 28 | * Represents a key mapping target. It can be a PadState that represents real 3DS buttons, | ||
| 29 | * or an IndirectTarget. | ||
| 30 | */ | ||
| 31 | struct KeyTarget { | ||
| 32 | bool direct; | ||
| 33 | union { | ||
| 34 | u32 direct_target_hex; | ||
| 35 | IndirectTarget indirect_target; | ||
| 36 | } target; | ||
| 37 | |||
| 38 | KeyTarget() : direct(true) { | ||
| 39 | target.direct_target_hex = 0; | ||
| 40 | } | ||
| 41 | |||
| 42 | KeyTarget(Service::HID::PadState pad) : direct(true) { | ||
| 43 | target.direct_target_hex = pad.hex; | ||
| 44 | } | ||
| 45 | |||
| 46 | KeyTarget(IndirectTarget i) : direct(false) { | ||
| 47 | target.indirect_target = i; | ||
| 48 | } | ||
| 49 | }; | ||
| 50 | |||
| 51 | /** | ||
| 52 | * Represents a key for a specific host device. | ||
| 53 | */ | ||
| 54 | struct HostDeviceKey { | ||
| 55 | int key_code; | ||
| 56 | int device_id; ///< Uniquely identifies a host device | ||
| 57 | |||
| 58 | bool operator<(const HostDeviceKey& other) const { | ||
| 59 | return std::tie(key_code, device_id) < std::tie(other.key_code, other.device_id); | ||
| 60 | } | ||
| 61 | |||
| 62 | bool operator==(const HostDeviceKey& other) const { | ||
| 63 | return std::tie(key_code, device_id) == std::tie(other.key_code, other.device_id); | ||
| 64 | } | ||
| 65 | }; | ||
| 66 | |||
| 67 | extern const std::array<KeyTarget, Settings::NativeInput::NUM_INPUTS> mapping_targets; | ||
| 68 | |||
| 69 | /** | ||
| 70 | * Generates a new device id, which uniquely identifies a host device within KeyMap. | ||
| 71 | */ | ||
| 72 | int NewDeviceId(); | ||
| 73 | |||
| 74 | /** | ||
| 75 | * Maps a device-specific key to a target (a PadState or an IndirectTarget). | ||
| 76 | */ | ||
| 77 | void SetKeyMapping(HostDeviceKey key, KeyTarget target); | ||
| 78 | |||
| 79 | /** | ||
| 80 | * Clears all key mappings belonging to one device. | ||
| 81 | */ | ||
| 82 | void ClearKeyMapping(int device_id); | ||
| 83 | |||
| 84 | /** | ||
| 85 | * Maps a key press action and call the corresponding function in EmuWindow | ||
| 86 | */ | ||
| 87 | void PressKey(EmuWindow& emu_window, HostDeviceKey key); | ||
| 88 | |||
| 89 | /** | ||
| 90 | * Maps a key release action and call the corresponding function in EmuWindow | ||
| 91 | */ | ||
| 92 | void ReleaseKey(EmuWindow& emu_window, HostDeviceKey key); | ||
| 93 | } | ||
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index fb3acb507..b19e831fe 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp | |||
| @@ -2,10 +2,14 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <algorithm> | ||
| 6 | #include <atomic> | ||
| 5 | #include <cmath> | 7 | #include <cmath> |
| 8 | #include <memory> | ||
| 6 | #include "common/logging/log.h" | 9 | #include "common/logging/log.h" |
| 7 | #include "core/core_timing.h" | 10 | #include "core/core_timing.h" |
| 8 | #include "core/frontend/emu_window.h" | 11 | #include "core/frontend/emu_window.h" |
| 12 | #include "core/frontend/input.h" | ||
| 9 | #include "core/hle/kernel/event.h" | 13 | #include "core/hle/kernel/event.h" |
| 10 | #include "core/hle/kernel/shared_memory.h" | 14 | #include "core/hle/kernel/shared_memory.h" |
| 11 | #include "core/hle/service/hid/hid.h" | 15 | #include "core/hle/service/hid/hid.h" |
| @@ -44,6 +48,11 @@ constexpr u64 pad_update_ticks = BASE_CLOCK_RATE_ARM11 / 234; | |||
| 44 | constexpr u64 accelerometer_update_ticks = BASE_CLOCK_RATE_ARM11 / 104; | 48 | constexpr u64 accelerometer_update_ticks = BASE_CLOCK_RATE_ARM11 / 104; |
| 45 | constexpr u64 gyroscope_update_ticks = BASE_CLOCK_RATE_ARM11 / 101; | 49 | constexpr u64 gyroscope_update_ticks = BASE_CLOCK_RATE_ARM11 / 101; |
| 46 | 50 | ||
| 51 | static std::atomic<bool> is_device_reload_pending; | ||
| 52 | static std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeButton::NUM_BUTTONS_HID> | ||
| 53 | buttons; | ||
| 54 | static std::unique_ptr<Input::AnalogDevice> circle_pad; | ||
| 55 | |||
| 47 | static PadState GetCirclePadDirectionState(s16 circle_pad_x, s16 circle_pad_y) { | 56 | static PadState GetCirclePadDirectionState(s16 circle_pad_x, s16 circle_pad_y) { |
| 48 | // 30 degree and 60 degree are angular thresholds for directions | 57 | // 30 degree and 60 degree are angular thresholds for directions |
| 49 | constexpr float TAN30 = 0.577350269f; | 58 | constexpr float TAN30 = 0.577350269f; |
| @@ -74,14 +83,48 @@ static PadState GetCirclePadDirectionState(s16 circle_pad_x, s16 circle_pad_y) { | |||
| 74 | return state; | 83 | return state; |
| 75 | } | 84 | } |
| 76 | 85 | ||
| 86 | static void LoadInputDevices() { | ||
| 87 | std::transform(Settings::values.buttons.begin() + Settings::NativeButton::BUTTON_HID_BEGIN, | ||
| 88 | Settings::values.buttons.begin() + Settings::NativeButton::BUTTON_HID_END, | ||
| 89 | buttons.begin(), Input::CreateDevice<Input::ButtonDevice>); | ||
| 90 | circle_pad = Input::CreateDevice<Input::AnalogDevice>( | ||
| 91 | Settings::values.analogs[Settings::NativeAnalog::CirclePad]); | ||
| 92 | } | ||
| 93 | |||
| 94 | static void UnloadInputDevices() { | ||
| 95 | for (auto& button : buttons) { | ||
| 96 | button.reset(); | ||
| 97 | } | ||
| 98 | circle_pad.reset(); | ||
| 99 | } | ||
| 100 | |||
| 77 | static void UpdatePadCallback(u64 userdata, int cycles_late) { | 101 | static void UpdatePadCallback(u64 userdata, int cycles_late) { |
| 78 | SharedMem* mem = reinterpret_cast<SharedMem*>(shared_mem->GetPointer()); | 102 | SharedMem* mem = reinterpret_cast<SharedMem*>(shared_mem->GetPointer()); |
| 79 | 103 | ||
| 80 | PadState state = VideoCore::g_emu_window->GetPadState(); | 104 | if (is_device_reload_pending.exchange(false)) |
| 105 | LoadInputDevices(); | ||
| 106 | |||
| 107 | PadState state; | ||
| 108 | using namespace Settings::NativeButton; | ||
| 109 | state.a.Assign(buttons[A - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 110 | state.b.Assign(buttons[B - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 111 | state.x.Assign(buttons[X - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 112 | state.y.Assign(buttons[Y - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 113 | state.right.Assign(buttons[Right - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 114 | state.left.Assign(buttons[Left - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 115 | state.up.Assign(buttons[Up - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 116 | state.down.Assign(buttons[Down - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 117 | state.l.Assign(buttons[L - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 118 | state.r.Assign(buttons[R - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 119 | state.start.Assign(buttons[Start - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 120 | state.select.Assign(buttons[Select - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 81 | 121 | ||
| 82 | // Get current circle pad position and update circle pad direction | 122 | // Get current circle pad position and update circle pad direction |
| 83 | s16 circle_pad_x, circle_pad_y; | 123 | float circle_pad_x_f, circle_pad_y_f; |
| 84 | std::tie(circle_pad_x, circle_pad_y) = VideoCore::g_emu_window->GetCirclePadState(); | 124 | std::tie(circle_pad_x_f, circle_pad_y_f) = circle_pad->GetStatus(); |
| 125 | constexpr int MAX_CIRCLEPAD_POS = 0x9C; // Max value for a circle pad position | ||
| 126 | s16 circle_pad_x = static_cast<s16>(circle_pad_x_f * MAX_CIRCLEPAD_POS); | ||
| 127 | s16 circle_pad_y = static_cast<s16>(circle_pad_y_f * MAX_CIRCLEPAD_POS); | ||
| 85 | state.hex |= GetCirclePadDirectionState(circle_pad_x, circle_pad_y).hex; | 128 | state.hex |= GetCirclePadDirectionState(circle_pad_x, circle_pad_y).hex; |
| 86 | 129 | ||
| 87 | mem->pad.current_state.hex = state.hex; | 130 | mem->pad.current_state.hex = state.hex; |
| @@ -313,6 +356,8 @@ void Init() { | |||
| 313 | AddService(new HID_U_Interface); | 356 | AddService(new HID_U_Interface); |
| 314 | AddService(new HID_SPVR_Interface); | 357 | AddService(new HID_SPVR_Interface); |
| 315 | 358 | ||
| 359 | is_device_reload_pending.store(true); | ||
| 360 | |||
| 316 | using Kernel::MemoryPermission; | 361 | using Kernel::MemoryPermission; |
| 317 | shared_mem = | 362 | shared_mem = |
| 318 | SharedMemory::Create(nullptr, 0x1000, MemoryPermission::ReadWrite, MemoryPermission::Read, | 363 | SharedMemory::Create(nullptr, 0x1000, MemoryPermission::ReadWrite, MemoryPermission::Read, |
| @@ -350,6 +395,11 @@ void Shutdown() { | |||
| 350 | event_accelerometer = nullptr; | 395 | event_accelerometer = nullptr; |
| 351 | event_gyroscope = nullptr; | 396 | event_gyroscope = nullptr; |
| 352 | event_debug_pad = nullptr; | 397 | event_debug_pad = nullptr; |
| 398 | UnloadInputDevices(); | ||
| 399 | } | ||
| 400 | |||
| 401 | void ReloadInputDevices() { | ||
| 402 | is_device_reload_pending.store(true); | ||
| 353 | } | 403 | } |
| 354 | 404 | ||
| 355 | } // namespace HID | 405 | } // namespace HID |
diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h index c7f4ee138..b505cdcd5 100644 --- a/src/core/hle/service/hid/hid.h +++ b/src/core/hle/service/hid/hid.h | |||
| @@ -39,13 +39,6 @@ struct PadState { | |||
| 39 | BitField<10, 1, u32> x; | 39 | BitField<10, 1, u32> x; |
| 40 | BitField<11, 1, u32> y; | 40 | BitField<11, 1, u32> y; |
| 41 | 41 | ||
| 42 | BitField<14, 1, u32> zl; | ||
| 43 | BitField<15, 1, u32> zr; | ||
| 44 | |||
| 45 | BitField<24, 1, u32> c_right; | ||
| 46 | BitField<25, 1, u32> c_left; | ||
| 47 | BitField<26, 1, u32> c_up; | ||
| 48 | BitField<27, 1, u32> c_down; | ||
| 49 | BitField<28, 1, u32> circle_right; | 42 | BitField<28, 1, u32> circle_right; |
| 50 | BitField<29, 1, u32> circle_left; | 43 | BitField<29, 1, u32> circle_left; |
| 51 | BitField<30, 1, u32> circle_up; | 44 | BitField<30, 1, u32> circle_up; |
| @@ -183,33 +176,6 @@ ASSERT_REG_POSITION(touch.index_reset_ticks, 0x2A); | |||
| 183 | #undef ASSERT_REG_POSITION | 176 | #undef ASSERT_REG_POSITION |
| 184 | #endif // !defined(_MSC_VER) | 177 | #endif // !defined(_MSC_VER) |
| 185 | 178 | ||
| 186 | // Pre-defined PadStates for single button presses | ||
| 187 | const PadState PAD_NONE = {{0}}; | ||
| 188 | const PadState PAD_A = {{1u << 0}}; | ||
| 189 | const PadState PAD_B = {{1u << 1}}; | ||
| 190 | const PadState PAD_SELECT = {{1u << 2}}; | ||
| 191 | const PadState PAD_START = {{1u << 3}}; | ||
| 192 | const PadState PAD_RIGHT = {{1u << 4}}; | ||
| 193 | const PadState PAD_LEFT = {{1u << 5}}; | ||
| 194 | const PadState PAD_UP = {{1u << 6}}; | ||
| 195 | const PadState PAD_DOWN = {{1u << 7}}; | ||
| 196 | const PadState PAD_R = {{1u << 8}}; | ||
| 197 | const PadState PAD_L = {{1u << 9}}; | ||
| 198 | const PadState PAD_X = {{1u << 10}}; | ||
| 199 | const PadState PAD_Y = {{1u << 11}}; | ||
| 200 | |||
| 201 | const PadState PAD_ZL = {{1u << 14}}; | ||
| 202 | const PadState PAD_ZR = {{1u << 15}}; | ||
| 203 | |||
| 204 | const PadState PAD_C_RIGHT = {{1u << 24}}; | ||
| 205 | const PadState PAD_C_LEFT = {{1u << 25}}; | ||
| 206 | const PadState PAD_C_UP = {{1u << 26}}; | ||
| 207 | const PadState PAD_C_DOWN = {{1u << 27}}; | ||
| 208 | const PadState PAD_CIRCLE_RIGHT = {{1u << 28}}; | ||
| 209 | const PadState PAD_CIRCLE_LEFT = {{1u << 29}}; | ||
| 210 | const PadState PAD_CIRCLE_UP = {{1u << 30}}; | ||
| 211 | const PadState PAD_CIRCLE_DOWN = {{1u << 31}}; | ||
| 212 | |||
| 213 | /** | 179 | /** |
| 214 | * HID::GetIPCHandles service function | 180 | * HID::GetIPCHandles service function |
| 215 | * Inputs: | 181 | * Inputs: |
| @@ -297,5 +263,8 @@ void Init(); | |||
| 297 | 263 | ||
| 298 | /// Shutdown HID service | 264 | /// Shutdown HID service |
| 299 | void Shutdown(); | 265 | void Shutdown(); |
| 266 | |||
| 267 | /// Reload input devices. Used when input configuration changed | ||
| 268 | void ReloadInputDevices(); | ||
| 300 | } | 269 | } |
| 301 | } | 270 | } |
diff --git a/src/core/settings.cpp b/src/core/settings.cpp index 3a32b70aa..a598f9f2f 100644 --- a/src/core/settings.cpp +++ b/src/core/settings.cpp | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | 4 | ||
| 5 | #include "audio_core/audio_core.h" | 5 | #include "audio_core/audio_core.h" |
| 6 | #include "core/gdbstub/gdbstub.h" | 6 | #include "core/gdbstub/gdbstub.h" |
| 7 | #include "core/hle/service/hid/hid.h" | ||
| 7 | #include "settings.h" | 8 | #include "settings.h" |
| 8 | #include "video_core/video_core.h" | 9 | #include "video_core/video_core.h" |
| 9 | 10 | ||
| @@ -29,6 +30,8 @@ void Apply() { | |||
| 29 | 30 | ||
| 30 | AudioCore::SelectSink(values.sink_id); | 31 | AudioCore::SelectSink(values.sink_id); |
| 31 | AudioCore::EnableStretching(values.enable_audio_stretching); | 32 | AudioCore::EnableStretching(values.enable_audio_stretching); |
| 33 | |||
| 34 | Service::HID::ReloadInputDevices(); | ||
| 32 | } | 35 | } |
| 33 | 36 | ||
| 34 | } // namespace | 37 | } // namespace |
diff --git a/src/core/settings.h b/src/core/settings.h index b6c75531f..d1a9f0da8 100644 --- a/src/core/settings.h +++ b/src/core/settings.h | |||
| @@ -18,64 +18,68 @@ enum class LayoutOption { | |||
| 18 | Custom, | 18 | Custom, |
| 19 | }; | 19 | }; |
| 20 | 20 | ||
| 21 | namespace NativeInput { | 21 | namespace NativeButton { |
| 22 | |||
| 23 | enum Values { | 22 | enum Values { |
| 24 | // directly mapped keys | ||
| 25 | A, | 23 | A, |
| 26 | B, | 24 | B, |
| 27 | X, | 25 | X, |
| 28 | Y, | 26 | Y, |
| 27 | Up, | ||
| 28 | Down, | ||
| 29 | Left, | ||
| 30 | Right, | ||
| 29 | L, | 31 | L, |
| 30 | R, | 32 | R, |
| 33 | Start, | ||
| 34 | Select, | ||
| 35 | |||
| 31 | ZL, | 36 | ZL, |
| 32 | ZR, | 37 | ZR, |
| 33 | START, | 38 | |
| 34 | SELECT, | 39 | Home, |
| 35 | HOME, | 40 | |
| 36 | DUP, | 41 | NumButtons, |
| 37 | DDOWN, | ||
| 38 | DLEFT, | ||
| 39 | DRIGHT, | ||
| 40 | CUP, | ||
| 41 | CDOWN, | ||
| 42 | CLEFT, | ||
| 43 | CRIGHT, | ||
| 44 | |||
| 45 | // indirectly mapped keys | ||
| 46 | CIRCLE_UP, | ||
| 47 | CIRCLE_DOWN, | ||
| 48 | CIRCLE_LEFT, | ||
| 49 | CIRCLE_RIGHT, | ||
| 50 | CIRCLE_MODIFIER, | ||
| 51 | |||
| 52 | NUM_INPUTS | ||
| 53 | }; | 42 | }; |
| 54 | 43 | ||
| 55 | static const std::array<const char*, NUM_INPUTS> Mapping = {{ | 44 | constexpr int BUTTON_HID_BEGIN = A; |
| 56 | // directly mapped keys | 45 | constexpr int BUTTON_IR_BEGIN = ZL; |
| 57 | "pad_a", "pad_b", "pad_x", "pad_y", "pad_l", "pad_r", "pad_zl", "pad_zr", "pad_start", | 46 | constexpr int BUTTON_NS_BEGIN = Home; |
| 58 | "pad_select", "pad_home", "pad_dup", "pad_ddown", "pad_dleft", "pad_dright", "pad_cup", | 47 | |
| 59 | "pad_cdown", "pad_cleft", "pad_cright", | 48 | constexpr int BUTTON_HID_END = BUTTON_IR_BEGIN; |
| 49 | constexpr int BUTTON_IR_END = BUTTON_NS_BEGIN; | ||
| 50 | constexpr int BUTTON_NS_END = NumButtons; | ||
| 51 | |||
| 52 | constexpr int NUM_BUTTONS_HID = BUTTON_HID_END - BUTTON_HID_BEGIN; | ||
| 53 | constexpr int NUM_BUTTONS_IR = BUTTON_IR_END - BUTTON_IR_BEGIN; | ||
| 54 | constexpr int NUM_BUTTONS_NS = BUTTON_NS_END - BUTTON_NS_BEGIN; | ||
| 60 | 55 | ||
| 61 | // indirectly mapped keys | 56 | static const std::array<const char*, NumButtons> mapping = {{ |
| 62 | "pad_circle_up", "pad_circle_down", "pad_circle_left", "pad_circle_right", | 57 | "button_a", "button_b", "button_x", "button_y", "button_up", "button_down", "button_left", |
| 63 | "pad_circle_modifier", | 58 | "button_right", "button_l", "button_r", "button_start", "button_select", "button_zl", |
| 59 | "button_zr", "button_home", | ||
| 64 | }}; | 60 | }}; |
| 65 | static const std::array<Values, NUM_INPUTS> All = {{ | 61 | } // namespace NativeButton |
| 66 | A, B, X, Y, L, R, ZL, ZR, | 62 | |
| 67 | START, SELECT, HOME, DUP, DDOWN, DLEFT, DRIGHT, CUP, | 63 | namespace NativeAnalog { |
| 68 | CDOWN, CLEFT, CRIGHT, CIRCLE_UP, CIRCLE_DOWN, CIRCLE_LEFT, CIRCLE_RIGHT, CIRCLE_MODIFIER, | 64 | enum Values { |
| 65 | CirclePad, | ||
| 66 | CStick, | ||
| 67 | |||
| 68 | NumAnalogs, | ||
| 69 | }; | ||
| 70 | |||
| 71 | static const std::array<const char*, NumAnalogs> mapping = {{ | ||
| 72 | "circle_pad", "c_stick", | ||
| 69 | }}; | 73 | }}; |
| 70 | } | 74 | } // namespace NumAnalog |
| 71 | 75 | ||
| 72 | struct Values { | 76 | struct Values { |
| 73 | // CheckNew3DS | 77 | // CheckNew3DS |
| 74 | bool is_new_3ds; | 78 | bool is_new_3ds; |
| 75 | 79 | ||
| 76 | // Controls | 80 | // Controls |
| 77 | std::array<int, NativeInput::NUM_INPUTS> input_mappings; | 81 | std::array<std::string, NativeButton::NumButtons> buttons; |
| 78 | float pad_circle_modifier_scale; | 82 | std::array<std::string, NativeAnalog::NumAnalogs> analogs; |
| 79 | 83 | ||
| 80 | // Core | 84 | // Core |
| 81 | bool use_cpu_jit; | 85 | bool use_cpu_jit; |
diff --git a/src/input_common/CMakeLists.txt b/src/input_common/CMakeLists.txt new file mode 100644 index 000000000..cfe5caaa3 --- /dev/null +++ b/src/input_common/CMakeLists.txt | |||
| @@ -0,0 +1,27 @@ | |||
| 1 | set(SRCS | ||
| 2 | analog_from_button.cpp | ||
| 3 | keyboard.cpp | ||
| 4 | main.cpp | ||
| 5 | ) | ||
| 6 | |||
| 7 | set(HEADERS | ||
| 8 | analog_from_button.h | ||
| 9 | keyboard.h | ||
| 10 | main.h | ||
| 11 | ) | ||
| 12 | |||
| 13 | if(SDL2_FOUND) | ||
| 14 | set(SRCS ${SRCS} sdl/sdl.cpp) | ||
| 15 | set(HEADERS ${HEADERS} sdl/sdl.h) | ||
| 16 | include_directories(${SDL2_INCLUDE_DIR}) | ||
| 17 | endif() | ||
| 18 | |||
| 19 | create_directory_groups(${SRCS} ${HEADERS}) | ||
| 20 | |||
| 21 | add_library(input_common STATIC ${SRCS} ${HEADERS}) | ||
| 22 | target_link_libraries(input_common common core) | ||
| 23 | |||
| 24 | if(SDL2_FOUND) | ||
| 25 | target_link_libraries(input_common ${SDL2_LIBRARY}) | ||
| 26 | set_property(TARGET input_common APPEND PROPERTY COMPILE_DEFINITIONS HAVE_SDL2) | ||
| 27 | endif() | ||
diff --git a/src/input_common/analog_from_button.cpp b/src/input_common/analog_from_button.cpp new file mode 100755 index 000000000..e1a260762 --- /dev/null +++ b/src/input_common/analog_from_button.cpp | |||
| @@ -0,0 +1,58 @@ | |||
| 1 | // Copyright 2017 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "input_common/analog_from_button.h" | ||
| 6 | |||
| 7 | namespace InputCommon { | ||
| 8 | |||
| 9 | class Analog final : public Input::AnalogDevice { | ||
| 10 | public: | ||
| 11 | using Button = std::unique_ptr<Input::ButtonDevice>; | ||
| 12 | |||
| 13 | Analog(Button up_, Button down_, Button left_, Button right_, Button modifier_, | ||
| 14 | float modifier_scale_) | ||
| 15 | : up(std::move(up_)), down(std::move(down_)), left(std::move(left_)), | ||
| 16 | right(std::move(right_)), modifier(std::move(modifier_)), | ||
| 17 | modifier_scale(modifier_scale_) {} | ||
| 18 | |||
| 19 | std::tuple<float, float> GetStatus() const override { | ||
| 20 | constexpr float SQRT_HALF = 0.707106781f; | ||
| 21 | int x = 0, y = 0; | ||
| 22 | |||
| 23 | if (right->GetStatus()) | ||
| 24 | ++x; | ||
| 25 | if (left->GetStatus()) | ||
| 26 | --x; | ||
| 27 | if (up->GetStatus()) | ||
| 28 | ++y; | ||
| 29 | if (down->GetStatus()) | ||
| 30 | --y; | ||
| 31 | |||
| 32 | float coef = modifier->GetStatus() ? modifier_scale : 1.0f; | ||
| 33 | return std::make_tuple(x * coef * (y == 0 ? 1.0f : SQRT_HALF), | ||
| 34 | y * coef * (x == 0 ? 1.0f : SQRT_HALF)); | ||
| 35 | } | ||
| 36 | |||
| 37 | private: | ||
| 38 | Button up; | ||
| 39 | Button down; | ||
| 40 | Button left; | ||
| 41 | Button right; | ||
| 42 | Button modifier; | ||
| 43 | float modifier_scale; | ||
| 44 | }; | ||
| 45 | |||
| 46 | std::unique_ptr<Input::AnalogDevice> AnalogFromButton::Create(const Common::ParamPackage& params) { | ||
| 47 | const std::string null_engine = Common::ParamPackage{{"engine", "null"}}.Serialize(); | ||
| 48 | auto up = Input::CreateDevice<Input::ButtonDevice>(params.Get("up", null_engine)); | ||
| 49 | auto down = Input::CreateDevice<Input::ButtonDevice>(params.Get("down", null_engine)); | ||
| 50 | auto left = Input::CreateDevice<Input::ButtonDevice>(params.Get("left", null_engine)); | ||
| 51 | auto right = Input::CreateDevice<Input::ButtonDevice>(params.Get("right", null_engine)); | ||
| 52 | auto modifier = Input::CreateDevice<Input::ButtonDevice>(params.Get("modifier", null_engine)); | ||
| 53 | auto modifier_scale = params.Get("modifier_scale", 0.5f); | ||
| 54 | return std::make_unique<Analog>(std::move(up), std::move(down), std::move(left), | ||
| 55 | std::move(right), std::move(modifier), modifier_scale); | ||
| 56 | } | ||
| 57 | |||
| 58 | } // namespace InputCommon | ||
diff --git a/src/input_common/analog_from_button.h b/src/input_common/analog_from_button.h new file mode 100755 index 000000000..bbd583dd9 --- /dev/null +++ b/src/input_common/analog_from_button.h | |||
| @@ -0,0 +1,31 @@ | |||
| 1 | // Copyright 2017 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <memory> | ||
| 8 | #include "core/frontend/input.h" | ||
| 9 | |||
| 10 | namespace InputCommon { | ||
| 11 | |||
| 12 | /** | ||
| 13 | * An analog device factory that takes direction button devices and combines them into a analog | ||
| 14 | * device. | ||
| 15 | */ | ||
| 16 | class AnalogFromButton final : public Input::Factory<Input::AnalogDevice> { | ||
| 17 | public: | ||
| 18 | /** | ||
| 19 | * Creates an analog device from direction button devices | ||
| 20 | * @param params contains parameters for creating the device: | ||
| 21 | * - "up": a serialized ParamPackage for creating a button device for up direction | ||
| 22 | * - "down": a serialized ParamPackage for creating a button device for down direction | ||
| 23 | * - "left": a serialized ParamPackage for creating a button device for left direction | ||
| 24 | * - "right": a serialized ParamPackage for creating a button device for right direction | ||
| 25 | * - "modifier": a serialized ParamPackage for creating a button device as the modifier | ||
| 26 | * - "modifier_scale": a float for the multiplier the modifier gives to the position | ||
| 27 | */ | ||
| 28 | std::unique_ptr<Input::AnalogDevice> Create(const Common::ParamPackage& params) override; | ||
| 29 | }; | ||
| 30 | |||
| 31 | } // namespace InputCommon | ||
diff --git a/src/input_common/keyboard.cpp b/src/input_common/keyboard.cpp new file mode 100644 index 000000000..a8fc01f2e --- /dev/null +++ b/src/input_common/keyboard.cpp | |||
| @@ -0,0 +1,82 @@ | |||
| 1 | // Copyright 2017 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <atomic> | ||
| 6 | #include <list> | ||
| 7 | #include <mutex> | ||
| 8 | #include "input_common/keyboard.h" | ||
| 9 | |||
| 10 | namespace InputCommon { | ||
| 11 | |||
| 12 | class KeyButton final : public Input::ButtonDevice { | ||
| 13 | public: | ||
| 14 | explicit KeyButton(std::shared_ptr<KeyButtonList> key_button_list_) | ||
| 15 | : key_button_list(key_button_list_) {} | ||
| 16 | |||
| 17 | ~KeyButton(); | ||
| 18 | |||
| 19 | bool GetStatus() const override { | ||
| 20 | return status.load(); | ||
| 21 | } | ||
| 22 | |||
| 23 | friend class KeyButtonList; | ||
| 24 | |||
| 25 | private: | ||
| 26 | std::shared_ptr<KeyButtonList> key_button_list; | ||
| 27 | std::atomic<bool> status{false}; | ||
| 28 | }; | ||
| 29 | |||
| 30 | struct KeyButtonPair { | ||
| 31 | int key_code; | ||
| 32 | KeyButton* key_button; | ||
| 33 | }; | ||
| 34 | |||
| 35 | class KeyButtonList { | ||
| 36 | public: | ||
| 37 | void AddKeyButton(int key_code, KeyButton* key_button) { | ||
| 38 | std::lock_guard<std::mutex> guard(mutex); | ||
| 39 | list.push_back(KeyButtonPair{key_code, key_button}); | ||
| 40 | } | ||
| 41 | |||
| 42 | void RemoveKeyButton(const KeyButton* key_button) { | ||
| 43 | std::lock_guard<std::mutex> guard(mutex); | ||
| 44 | list.remove_if( | ||
| 45 | [key_button](const KeyButtonPair& pair) { return pair.key_button == key_button; }); | ||
| 46 | } | ||
| 47 | |||
| 48 | void ChangeKeyStatus(int key_code, bool pressed) { | ||
| 49 | std::lock_guard<std::mutex> guard(mutex); | ||
| 50 | for (const KeyButtonPair& pair : list) { | ||
| 51 | if (pair.key_code == key_code) | ||
| 52 | pair.key_button->status.store(pressed); | ||
| 53 | } | ||
| 54 | } | ||
| 55 | |||
| 56 | private: | ||
| 57 | std::mutex mutex; | ||
| 58 | std::list<KeyButtonPair> list; | ||
| 59 | }; | ||
| 60 | |||
| 61 | Keyboard::Keyboard() : key_button_list{std::make_shared<KeyButtonList>()} {} | ||
| 62 | |||
| 63 | KeyButton::~KeyButton() { | ||
| 64 | key_button_list->RemoveKeyButton(this); | ||
| 65 | } | ||
| 66 | |||
| 67 | std::unique_ptr<Input::ButtonDevice> Keyboard::Create(const Common::ParamPackage& params) { | ||
| 68 | int key_code = params.Get("code", 0); | ||
| 69 | std::unique_ptr<KeyButton> button = std::make_unique<KeyButton>(key_button_list); | ||
| 70 | key_button_list->AddKeyButton(key_code, button.get()); | ||
| 71 | return std::move(button); | ||
| 72 | } | ||
| 73 | |||
| 74 | void Keyboard::PressKey(int key_code) { | ||
| 75 | key_button_list->ChangeKeyStatus(key_code, true); | ||
| 76 | } | ||
| 77 | |||
| 78 | void Keyboard::ReleaseKey(int key_code) { | ||
| 79 | key_button_list->ChangeKeyStatus(key_code, false); | ||
| 80 | } | ||
| 81 | |||
| 82 | } // namespace InputCommon | ||
diff --git a/src/input_common/keyboard.h b/src/input_common/keyboard.h new file mode 100644 index 000000000..76359aa30 --- /dev/null +++ b/src/input_common/keyboard.h | |||
| @@ -0,0 +1,45 @@ | |||
| 1 | // Copyright 2017 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <memory> | ||
| 8 | #include "core/frontend/input.h" | ||
| 9 | |||
| 10 | namespace InputCommon { | ||
| 11 | |||
| 12 | class KeyButtonList; | ||
| 13 | |||
| 14 | /** | ||
| 15 | * A button device factory representing a keyboard. It receives keyboard events and forward them | ||
| 16 | * to all button devices it created. | ||
| 17 | */ | ||
| 18 | class Keyboard final : public Input::Factory<Input::ButtonDevice> { | ||
| 19 | public: | ||
| 20 | Keyboard(); | ||
| 21 | |||
| 22 | /** | ||
| 23 | * Creates a button device from a keyboard key | ||
| 24 | * @param params contains parameters for creating the device: | ||
| 25 | * - "code": the code of the key to bind with the button | ||
| 26 | */ | ||
| 27 | std::unique_ptr<Input::ButtonDevice> Create(const Common::ParamPackage& params) override; | ||
| 28 | |||
| 29 | /** | ||
| 30 | * Sets the status of all buttons bound with the key to pressed | ||
| 31 | * @param key_code the code of the key to press | ||
| 32 | */ | ||
| 33 | void PressKey(int key_code); | ||
| 34 | |||
| 35 | /** | ||
| 36 | * Sets the status of all buttons bound with the key to released | ||
| 37 | * @param key_code the code of the key to release | ||
| 38 | */ | ||
| 39 | void ReleaseKey(int key_code); | ||
| 40 | |||
| 41 | private: | ||
| 42 | std::shared_ptr<KeyButtonList> key_button_list; | ||
| 43 | }; | ||
| 44 | |||
| 45 | } // namespace InputCommon | ||
diff --git a/src/input_common/main.cpp b/src/input_common/main.cpp new file mode 100644 index 000000000..699f41e6b --- /dev/null +++ b/src/input_common/main.cpp | |||
| @@ -0,0 +1,63 @@ | |||
| 1 | // Copyright 2017 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <memory> | ||
| 6 | #include "common/param_package.h" | ||
| 7 | #include "input_common/analog_from_button.h" | ||
| 8 | #include "input_common/keyboard.h" | ||
| 9 | #include "input_common/main.h" | ||
| 10 | #ifdef HAVE_SDL2 | ||
| 11 | #include "input_common/sdl/sdl.h" | ||
| 12 | #endif | ||
| 13 | |||
| 14 | namespace InputCommon { | ||
| 15 | |||
| 16 | static std::shared_ptr<Keyboard> keyboard; | ||
| 17 | |||
| 18 | void Init() { | ||
| 19 | keyboard = std::make_shared<InputCommon::Keyboard>(); | ||
| 20 | Input::RegisterFactory<Input::ButtonDevice>("keyboard", keyboard); | ||
| 21 | Input::RegisterFactory<Input::AnalogDevice>("analog_from_button", | ||
| 22 | std::make_shared<InputCommon::AnalogFromButton>()); | ||
| 23 | #ifdef HAVE_SDL2 | ||
| 24 | SDL::Init(); | ||
| 25 | #endif | ||
| 26 | } | ||
| 27 | |||
| 28 | void Shutdown() { | ||
| 29 | Input::UnregisterFactory<Input::ButtonDevice>("keyboard"); | ||
| 30 | keyboard.reset(); | ||
| 31 | Input::UnregisterFactory<Input::AnalogDevice>("analog_from_button"); | ||
| 32 | |||
| 33 | #ifdef HAVE_SDL2 | ||
| 34 | SDL::Shutdown(); | ||
| 35 | #endif | ||
| 36 | } | ||
| 37 | |||
| 38 | Keyboard* GetKeyboard() { | ||
| 39 | return keyboard.get(); | ||
| 40 | } | ||
| 41 | |||
| 42 | std::string GenerateKeyboardParam(int key_code) { | ||
| 43 | Common::ParamPackage param{ | ||
| 44 | {"engine", "keyboard"}, {"code", std::to_string(key_code)}, | ||
| 45 | }; | ||
| 46 | return param.Serialize(); | ||
| 47 | } | ||
| 48 | |||
| 49 | std::string GenerateAnalogParamFromKeys(int key_up, int key_down, int key_left, int key_right, | ||
| 50 | int key_modifier, float modifier_scale) { | ||
| 51 | Common::ParamPackage circle_pad_param{ | ||
| 52 | {"engine", "analog_from_button"}, | ||
| 53 | {"up", GenerateKeyboardParam(key_up)}, | ||
| 54 | {"down", GenerateKeyboardParam(key_down)}, | ||
| 55 | {"left", GenerateKeyboardParam(key_left)}, | ||
| 56 | {"right", GenerateKeyboardParam(key_right)}, | ||
| 57 | {"modifier", GenerateKeyboardParam(key_modifier)}, | ||
| 58 | {"modifier_scale", std::to_string(modifier_scale)}, | ||
| 59 | }; | ||
| 60 | return circle_pad_param.Serialize(); | ||
| 61 | } | ||
| 62 | |||
| 63 | } // namespace InputCommon | ||
diff --git a/src/input_common/main.h b/src/input_common/main.h new file mode 100644 index 000000000..140bbd014 --- /dev/null +++ b/src/input_common/main.h | |||
| @@ -0,0 +1,29 @@ | |||
| 1 | // Copyright 2017 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <string> | ||
| 8 | |||
| 9 | namespace InputCommon { | ||
| 10 | |||
| 11 | /// Initializes and registers all built-in input device factories. | ||
| 12 | void Init(); | ||
| 13 | |||
| 14 | /// Unresisters all build-in input device factories and shut them down. | ||
| 15 | void Shutdown(); | ||
| 16 | |||
| 17 | class Keyboard; | ||
| 18 | |||
| 19 | /// Gets the keyboard button device factory. | ||
| 20 | Keyboard* GetKeyboard(); | ||
| 21 | |||
| 22 | /// Generates a serialized param package for creating a keyboard button device | ||
| 23 | std::string GenerateKeyboardParam(int key_code); | ||
| 24 | |||
| 25 | /// Generates a serialized param package for creating an analog device taking input from keyboard | ||
| 26 | std::string GenerateAnalogParamFromKeys(int key_up, int key_down, int key_left, int key_right, | ||
| 27 | int key_modifier, float modifier_scale); | ||
| 28 | |||
| 29 | } // namespace InputCommon | ||
diff --git a/src/input_common/sdl/sdl.cpp b/src/input_common/sdl/sdl.cpp new file mode 100644 index 000000000..ae0206909 --- /dev/null +++ b/src/input_common/sdl/sdl.cpp | |||
| @@ -0,0 +1,202 @@ | |||
| 1 | // Copyright 2017 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <cmath> | ||
| 6 | #include <memory> | ||
| 7 | #include <string> | ||
| 8 | #include <tuple> | ||
| 9 | #include <unordered_map> | ||
| 10 | #include <SDL.h> | ||
| 11 | #include "common/math_util.h" | ||
| 12 | #include "input_common/sdl/sdl.h" | ||
| 13 | |||
| 14 | namespace InputCommon { | ||
| 15 | |||
| 16 | namespace SDL { | ||
| 17 | |||
| 18 | class SDLJoystick; | ||
| 19 | class SDLButtonFactory; | ||
| 20 | class SDLAnalogFactory; | ||
| 21 | static std::unordered_map<int, std::weak_ptr<SDLJoystick>> joystick_list; | ||
| 22 | static std::shared_ptr<SDLButtonFactory> button_factory; | ||
| 23 | static std::shared_ptr<SDLAnalogFactory> analog_factory; | ||
| 24 | |||
| 25 | static bool initialized = false; | ||
| 26 | |||
| 27 | class SDLJoystick { | ||
| 28 | public: | ||
| 29 | explicit SDLJoystick(int joystick_index) | ||
| 30 | : joystick{SDL_JoystickOpen(joystick_index), SDL_JoystickClose} { | ||
| 31 | if (!joystick) { | ||
| 32 | LOG_ERROR(Input, "failed to open joystick %d", joystick_index); | ||
| 33 | } | ||
| 34 | } | ||
| 35 | |||
| 36 | bool GetButton(int button) const { | ||
| 37 | if (!joystick) | ||
| 38 | return {}; | ||
| 39 | SDL_JoystickUpdate(); | ||
| 40 | return SDL_JoystickGetButton(joystick.get(), button) == 1; | ||
| 41 | } | ||
| 42 | |||
| 43 | std::tuple<float, float> GetAnalog(int axis_x, int axis_y) const { | ||
| 44 | if (!joystick) | ||
| 45 | return {}; | ||
| 46 | SDL_JoystickUpdate(); | ||
| 47 | float x = SDL_JoystickGetAxis(joystick.get(), axis_x) / 32767.0f; | ||
| 48 | float y = SDL_JoystickGetAxis(joystick.get(), axis_y) / 32767.0f; | ||
| 49 | y = -y; // 3DS uses an y-axis inverse from SDL | ||
| 50 | |||
| 51 | // Make sure the coordinates are in the unit circle, | ||
| 52 | // otherwise normalize it. | ||
| 53 | float r = x * x + y * y; | ||
| 54 | if (r > 1.0f) { | ||
| 55 | r = std::sqrt(r); | ||
| 56 | x /= r; | ||
| 57 | y /= r; | ||
| 58 | } | ||
| 59 | |||
| 60 | return std::make_tuple(x, y); | ||
| 61 | } | ||
| 62 | |||
| 63 | bool GetHatDirection(int hat, Uint8 direction) const { | ||
| 64 | return (SDL_JoystickGetHat(joystick.get(), hat) & direction) != 0; | ||
| 65 | } | ||
| 66 | |||
| 67 | private: | ||
| 68 | std::unique_ptr<SDL_Joystick, decltype(&SDL_JoystickClose)> joystick; | ||
| 69 | }; | ||
| 70 | |||
| 71 | class SDLButton final : public Input::ButtonDevice { | ||
| 72 | public: | ||
| 73 | explicit SDLButton(std::shared_ptr<SDLJoystick> joystick_, int button_) | ||
| 74 | : joystick(joystick_), button(button_) {} | ||
| 75 | |||
| 76 | bool GetStatus() const override { | ||
| 77 | return joystick->GetButton(button); | ||
| 78 | } | ||
| 79 | |||
| 80 | private: | ||
| 81 | std::shared_ptr<SDLJoystick> joystick; | ||
| 82 | int button; | ||
| 83 | }; | ||
| 84 | |||
| 85 | class SDLDirectionButton final : public Input::ButtonDevice { | ||
| 86 | public: | ||
| 87 | explicit SDLDirectionButton(std::shared_ptr<SDLJoystick> joystick_, int hat_, Uint8 direction_) | ||
| 88 | : joystick(joystick_), hat(hat_), direction(direction_) {} | ||
| 89 | |||
| 90 | bool GetStatus() const override { | ||
| 91 | return joystick->GetHatDirection(hat, direction); | ||
| 92 | } | ||
| 93 | |||
| 94 | private: | ||
| 95 | std::shared_ptr<SDLJoystick> joystick; | ||
| 96 | int hat; | ||
| 97 | Uint8 direction; | ||
| 98 | }; | ||
| 99 | |||
| 100 | class SDLAnalog final : public Input::AnalogDevice { | ||
| 101 | public: | ||
| 102 | SDLAnalog(std::shared_ptr<SDLJoystick> joystick_, int axis_x_, int axis_y_) | ||
| 103 | : joystick(joystick_), axis_x(axis_x_), axis_y(axis_y_) {} | ||
| 104 | |||
| 105 | std::tuple<float, float> GetStatus() const override { | ||
| 106 | return joystick->GetAnalog(axis_x, axis_y); | ||
| 107 | } | ||
| 108 | |||
| 109 | private: | ||
| 110 | std::shared_ptr<SDLJoystick> joystick; | ||
| 111 | int axis_x; | ||
| 112 | int axis_y; | ||
| 113 | }; | ||
| 114 | |||
| 115 | static std::shared_ptr<SDLJoystick> GetJoystick(int joystick_index) { | ||
| 116 | std::shared_ptr<SDLJoystick> joystick = joystick_list[joystick_index].lock(); | ||
| 117 | if (!joystick) { | ||
| 118 | joystick = std::make_shared<SDLJoystick>(joystick_index); | ||
| 119 | joystick_list[joystick_index] = joystick; | ||
| 120 | } | ||
| 121 | return joystick; | ||
| 122 | } | ||
| 123 | |||
| 124 | /// A button device factory that creates button devices from SDL joystick | ||
| 125 | class SDLButtonFactory final : public Input::Factory<Input::ButtonDevice> { | ||
| 126 | public: | ||
| 127 | /** | ||
| 128 | * Creates a button device from a joystick button | ||
| 129 | * @param params contains parameters for creating the device: | ||
| 130 | * - "joystick": the index of the joystick to bind | ||
| 131 | * - "button"(optional): the index of the button to bind | ||
| 132 | * - "hat"(optional): the index of the hat to bind as direction buttons | ||
| 133 | * - "direction"(only used for hat): the direction name of the hat to bind. Can be "up", | ||
| 134 | * "down", "left" or "right" | ||
| 135 | */ | ||
| 136 | std::unique_ptr<Input::ButtonDevice> Create(const Common::ParamPackage& params) override { | ||
| 137 | const int joystick_index = params.Get("joystick", 0); | ||
| 138 | |||
| 139 | if (params.Has("hat")) { | ||
| 140 | const int hat = params.Get("hat", 0); | ||
| 141 | const std::string direction_name = params.Get("direction", ""); | ||
| 142 | Uint8 direction; | ||
| 143 | if (direction_name == "up") { | ||
| 144 | direction = SDL_HAT_UP; | ||
| 145 | } else if (direction_name == "down") { | ||
| 146 | direction = SDL_HAT_DOWN; | ||
| 147 | } else if (direction_name == "left") { | ||
| 148 | direction = SDL_HAT_LEFT; | ||
| 149 | } else if (direction_name == "right") { | ||
| 150 | direction = SDL_HAT_RIGHT; | ||
| 151 | } else { | ||
| 152 | direction = 0; | ||
| 153 | } | ||
| 154 | return std::make_unique<SDLDirectionButton>(GetJoystick(joystick_index), hat, | ||
| 155 | direction); | ||
| 156 | } | ||
| 157 | |||
| 158 | const int button = params.Get("button", 0); | ||
| 159 | return std::make_unique<SDLButton>(GetJoystick(joystick_index), button); | ||
| 160 | } | ||
| 161 | }; | ||
| 162 | |||
| 163 | /// An analog device factory that creates analog devices from SDL joystick | ||
| 164 | class SDLAnalogFactory final : public Input::Factory<Input::AnalogDevice> { | ||
| 165 | public: | ||
| 166 | /** | ||
| 167 | * Creates analog device from joystick axes | ||
| 168 | * @param params contains parameters for creating the device: | ||
| 169 | * - "joystick": the index of the joystick to bind | ||
| 170 | * - "axis_x": the index of the axis to be bind as x-axis | ||
| 171 | * - "axis_y": the index of the axis to be bind as y-axis | ||
| 172 | */ | ||
| 173 | std::unique_ptr<Input::AnalogDevice> Create(const Common::ParamPackage& params) override { | ||
| 174 | const int joystick_index = params.Get("joystick", 0); | ||
| 175 | const int axis_x = params.Get("axis_x", 0); | ||
| 176 | const int axis_y = params.Get("axis_y", 1); | ||
| 177 | return std::make_unique<SDLAnalog>(GetJoystick(joystick_index), axis_x, axis_y); | ||
| 178 | } | ||
| 179 | }; | ||
| 180 | |||
| 181 | void Init() { | ||
| 182 | if (SDL_Init(SDL_INIT_JOYSTICK) < 0) { | ||
| 183 | LOG_CRITICAL(Input, "SDL_Init(SDL_INIT_JOYSTICK) failed with: %s", SDL_GetError()); | ||
| 184 | } else { | ||
| 185 | using namespace Input; | ||
| 186 | RegisterFactory<ButtonDevice>("sdl", std::make_shared<SDLButtonFactory>()); | ||
| 187 | RegisterFactory<AnalogDevice>("sdl", std::make_shared<SDLAnalogFactory>()); | ||
| 188 | initialized = true; | ||
| 189 | } | ||
| 190 | } | ||
| 191 | |||
| 192 | void Shutdown() { | ||
| 193 | if (initialized) { | ||
| 194 | using namespace Input; | ||
| 195 | UnregisterFactory<ButtonDevice>("sdl"); | ||
| 196 | UnregisterFactory<AnalogDevice>("sdl"); | ||
| 197 | SDL_QuitSubSystem(SDL_INIT_JOYSTICK); | ||
| 198 | } | ||
| 199 | } | ||
| 200 | |||
| 201 | } // namespace SDL | ||
| 202 | } // namespace InputCommon | ||
diff --git a/src/input_common/sdl/sdl.h b/src/input_common/sdl/sdl.h new file mode 100644 index 000000000..3e72debcc --- /dev/null +++ b/src/input_common/sdl/sdl.h | |||
| @@ -0,0 +1,19 @@ | |||
| 1 | // Copyright 2017 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include "core/frontend/input.h" | ||
| 8 | |||
| 9 | namespace InputCommon { | ||
| 10 | namespace SDL { | ||
| 11 | |||
| 12 | /// Initializes and registers SDL device factories | ||
| 13 | void Init(); | ||
| 14 | |||
| 15 | /// Unresisters SDL device factories and shut them down. | ||
| 16 | void Shutdown(); | ||
| 17 | |||
| 18 | } // namespace SDL | ||
| 19 | } // namespace InputCommon | ||
diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt index b47156ca4..d1144ba77 100644 --- a/src/tests/CMakeLists.txt +++ b/src/tests/CMakeLists.txt | |||
| @@ -1,6 +1,7 @@ | |||
| 1 | set(SRCS | 1 | set(SRCS |
| 2 | glad.cpp | 2 | glad.cpp |
| 3 | tests.cpp | 3 | tests.cpp |
| 4 | common/param_package.cpp | ||
| 4 | core/file_sys/path_parser.cpp | 5 | core/file_sys/path_parser.cpp |
| 5 | ) | 6 | ) |
| 6 | 7 | ||
diff --git a/src/tests/common/param_package.cpp b/src/tests/common/param_package.cpp new file mode 100644 index 000000000..efec2cc86 --- /dev/null +++ b/src/tests/common/param_package.cpp | |||
| @@ -0,0 +1,25 @@ | |||
| 1 | // Copyright 2017 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <catch.hpp> | ||
| 6 | #include <math.h> | ||
| 7 | #include "common/param_package.h" | ||
| 8 | |||
| 9 | namespace Common { | ||
| 10 | |||
| 11 | TEST_CASE("ParamPackage", "[common]") { | ||
| 12 | ParamPackage original{ | ||
| 13 | {"abc", "xyz"}, {"def", "42"}, {"jkl", "$$:1:$2$,3"}, | ||
| 14 | }; | ||
| 15 | original.Set("ghi", 3.14f); | ||
| 16 | ParamPackage copy(original.Serialize()); | ||
| 17 | REQUIRE(copy.Get("abc", "") == "xyz"); | ||
| 18 | REQUIRE(copy.Get("def", 0) == 42); | ||
| 19 | REQUIRE(std::abs(copy.Get("ghi", 0.0f) - 3.14f) < 0.01f); | ||
| 20 | REQUIRE(copy.Get("jkl", "") == "$$:1:$2$,3"); | ||
| 21 | REQUIRE(copy.Get("mno", "uvw") == "uvw"); | ||
| 22 | REQUIRE(copy.Get("abc", 42) == 42); | ||
| 23 | } | ||
| 24 | |||
| 25 | } // namespace Common | ||
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp index 0818a87b3..456443e86 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp | |||
| @@ -17,6 +17,7 @@ | |||
| 17 | #include "common/vector_math.h" | 17 | #include "common/vector_math.h" |
| 18 | #include "core/frontend/emu_window.h" | 18 | #include "core/frontend/emu_window.h" |
| 19 | #include "core/memory.h" | 19 | #include "core/memory.h" |
| 20 | #include "core/settings.h" | ||
| 20 | #include "video_core/pica_state.h" | 21 | #include "video_core/pica_state.h" |
| 21 | #include "video_core/renderer_opengl/gl_rasterizer_cache.h" | 22 | #include "video_core/renderer_opengl/gl_rasterizer_cache.h" |
| 22 | #include "video_core/renderer_opengl/gl_state.h" | 23 | #include "video_core/renderer_opengl/gl_state.h" |