summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar bunnei2016-06-10 22:28:58 -0400
committerGravatar GitHub2016-06-10 22:28:58 -0400
commitf99961581ee129c44625dbd8890fab349253271a (patch)
tree9a2610d391d795b533054a91c22763e869bdd62b /src
parentMerge pull request #1896 from citra-emu/revert-1893-interpreter-split (diff)
parentfixup! fixup! Refactor input system (diff)
downloadyuzu-f99961581ee129c44625dbd8890fab349253271a.tar.gz
yuzu-f99961581ee129c44625dbd8890fab349253271a.tar.xz
yuzu-f99961581ee129c44625dbd8890fab349253271a.zip
Merge pull request #1789 from wwylele/input-refactor
Refactor input mapping & implement circle pad modifier
Diffstat (limited to 'src')
-rw-r--r--src/citra/config.cpp7
-rw-r--r--src/citra/default_ini.h13
-rw-r--r--src/citra/emu_window/emu_window_sdl2.cpp7
-rw-r--r--src/citra_qt/bootmanager.cpp7
-rw-r--r--src/citra_qt/config.cpp8
-rw-r--r--src/common/emu_window.cpp24
-rw-r--r--src/common/emu_window.h46
-rw-r--r--src/common/key_map.cpp126
-rw-r--r--src/common/key_map.h59
-rw-r--r--src/core/hle/service/hid/hid.cpp68
-rw-r--r--src/core/hle/service/hid/hid.h3
-rw-r--r--src/core/settings.h22
12 files changed, 315 insertions, 75 deletions
diff --git a/src/citra/config.cpp b/src/citra/config.cpp
index c64de8e22..22cb51ea8 100644
--- a/src/citra/config.cpp
+++ b/src/citra/config.cpp
@@ -44,12 +44,16 @@ bool Config::LoadINI(const std::string& default_contents, bool retry) {
44} 44}
45 45
46static const std::array<int, Settings::NativeInput::NUM_INPUTS> defaults = { 46static const std::array<int, Settings::NativeInput::NUM_INPUTS> defaults = {
47 // directly mapped keys
47 SDL_SCANCODE_A, SDL_SCANCODE_S, SDL_SCANCODE_Z, SDL_SCANCODE_X, 48 SDL_SCANCODE_A, SDL_SCANCODE_S, SDL_SCANCODE_Z, SDL_SCANCODE_X,
48 SDL_SCANCODE_Q, SDL_SCANCODE_W, SDL_SCANCODE_1, SDL_SCANCODE_2, 49 SDL_SCANCODE_Q, SDL_SCANCODE_W, SDL_SCANCODE_1, SDL_SCANCODE_2,
49 SDL_SCANCODE_M, SDL_SCANCODE_N, SDL_SCANCODE_B, 50 SDL_SCANCODE_M, SDL_SCANCODE_N, SDL_SCANCODE_B,
50 SDL_SCANCODE_T, SDL_SCANCODE_G, SDL_SCANCODE_F, SDL_SCANCODE_H, 51 SDL_SCANCODE_T, SDL_SCANCODE_G, SDL_SCANCODE_F, SDL_SCANCODE_H,
52 SDL_SCANCODE_I, SDL_SCANCODE_K, SDL_SCANCODE_J, SDL_SCANCODE_L,
53
54 // indirectly mapped keys
51 SDL_SCANCODE_UP, SDL_SCANCODE_DOWN, SDL_SCANCODE_LEFT, SDL_SCANCODE_RIGHT, 55 SDL_SCANCODE_UP, SDL_SCANCODE_DOWN, SDL_SCANCODE_LEFT, SDL_SCANCODE_RIGHT,
52 SDL_SCANCODE_I, SDL_SCANCODE_K, SDL_SCANCODE_J, SDL_SCANCODE_L 56 SDL_SCANCODE_D,
53}; 57};
54 58
55void Config::ReadValues() { 59void Config::ReadValues() {
@@ -58,6 +62,7 @@ void Config::ReadValues() {
58 Settings::values.input_mappings[Settings::NativeInput::All[i]] = 62 Settings::values.input_mappings[Settings::NativeInput::All[i]] =
59 sdl2_config->GetInteger("Controls", Settings::NativeInput::Mapping[i], defaults[i]); 63 sdl2_config->GetInteger("Controls", Settings::NativeInput::Mapping[i], defaults[i]);
60 } 64 }
65 Settings::values.pad_circle_modifier_scale = (float)sdl2_config->GetReal("Controls", "pad_circle_modifier_scale", 0.5);
61 66
62 // Core 67 // Core
63 Settings::values.frame_skip = sdl2_config->GetInteger("Core", "frame_skip", 0); 68 Settings::values.frame_skip = sdl2_config->GetInteger("Core", "frame_skip", 0);
diff --git a/src/citra/default_ini.h b/src/citra/default_ini.h
index 49126356f..a9017dcb3 100644
--- a/src/citra/default_ini.h
+++ b/src/citra/default_ini.h
@@ -23,14 +23,19 @@ pad_l =
23pad_r = 23pad_r =
24pad_zl = 24pad_zl =
25pad_zr = 25pad_zr =
26pad_sup =
27pad_sdown =
28pad_sleft =
29pad_sright =
30pad_cup = 26pad_cup =
31pad_cdown = 27pad_cdown =
32pad_cleft = 28pad_cleft =
33pad_cright = 29pad_cright =
30pad_circle_up =
31pad_circle_down =
32pad_circle_left =
33pad_circle_right =
34pad_circle_modifier =
35
36# The applied modifier scale to circle pad.
37# Must be in range of 0.0-1.0. Defaults to 0.5
38pad_circle_modifier_scale =
34 39
35[Core] 40[Core]
36# The applied frameskip amount. Must be a power of two. 41# The applied frameskip amount. Must be a power of two.
diff --git a/src/citra/emu_window/emu_window_sdl2.cpp b/src/citra/emu_window/emu_window_sdl2.cpp
index 12cdd9d95..591f68aa4 100644
--- a/src/citra/emu_window/emu_window_sdl2.cpp
+++ b/src/citra/emu_window/emu_window_sdl2.cpp
@@ -40,9 +40,9 @@ void EmuWindow_SDL2::OnMouseButton(u32 button, u8 state, s32 x, s32 y) {
40 40
41void EmuWindow_SDL2::OnKeyEvent(int key, u8 state) { 41void EmuWindow_SDL2::OnKeyEvent(int key, u8 state) {
42 if (state == SDL_PRESSED) { 42 if (state == SDL_PRESSED) {
43 KeyPressed({ key, keyboard_id }); 43 KeyMap::PressKey(*this, { key, keyboard_id });
44 } else if (state == SDL_RELEASED) { 44 } else if (state == SDL_RELEASED) {
45 KeyReleased({ key, keyboard_id }); 45 KeyMap::ReleaseKey(*this, { key, keyboard_id });
46 } 46 }
47} 47}
48 48
@@ -168,8 +168,9 @@ void EmuWindow_SDL2::DoneCurrent() {
168} 168}
169 169
170void EmuWindow_SDL2::ReloadSetKeymaps() { 170void EmuWindow_SDL2::ReloadSetKeymaps() {
171 KeyMap::ClearKeyMapping(keyboard_id);
171 for (int i = 0; i < Settings::NativeInput::NUM_INPUTS; ++i) { 172 for (int i = 0; i < Settings::NativeInput::NUM_INPUTS; ++i) {
172 KeyMap::SetKeyMapping({ Settings::values.input_mappings[Settings::NativeInput::All[i]], keyboard_id }, Service::HID::pad_mapping[i]); 173 KeyMap::SetKeyMapping({ Settings::values.input_mappings[Settings::NativeInput::All[i]], keyboard_id }, KeyMap::mapping_targets[i]);
173 } 174 }
174} 175}
175 176
diff --git a/src/citra_qt/bootmanager.cpp b/src/citra_qt/bootmanager.cpp
index 01b81c11c..414b2f8af 100644
--- a/src/citra_qt/bootmanager.cpp
+++ b/src/citra_qt/bootmanager.cpp
@@ -235,12 +235,12 @@ void GRenderWindow::closeEvent(QCloseEvent* event) {
235 235
236void GRenderWindow::keyPressEvent(QKeyEvent* event) 236void GRenderWindow::keyPressEvent(QKeyEvent* event)
237{ 237{
238 this->KeyPressed({event->key(), keyboard_id}); 238 KeyMap::PressKey(*this, { event->key(), keyboard_id });
239} 239}
240 240
241void GRenderWindow::keyReleaseEvent(QKeyEvent* event) 241void GRenderWindow::keyReleaseEvent(QKeyEvent* event)
242{ 242{
243 this->KeyReleased({event->key(), keyboard_id}); 243 KeyMap::ReleaseKey(*this, { event->key(), keyboard_id });
244} 244}
245 245
246void GRenderWindow::mousePressEvent(QMouseEvent *event) 246void GRenderWindow::mousePressEvent(QMouseEvent *event)
@@ -270,8 +270,9 @@ void GRenderWindow::mouseReleaseEvent(QMouseEvent *event)
270 270
271void GRenderWindow::ReloadSetKeymaps() 271void GRenderWindow::ReloadSetKeymaps()
272{ 272{
273 KeyMap::ClearKeyMapping(keyboard_id);
273 for (int i = 0; i < Settings::NativeInput::NUM_INPUTS; ++i) { 274 for (int i = 0; i < Settings::NativeInput::NUM_INPUTS; ++i) {
274 KeyMap::SetKeyMapping({Settings::values.input_mappings[Settings::NativeInput::All[i]], keyboard_id}, Service::HID::pad_mapping[i]); 275 KeyMap::SetKeyMapping({ Settings::values.input_mappings[Settings::NativeInput::All[i]], keyboard_id }, KeyMap::mapping_targets[i]);
275 } 276 }
276} 277}
277 278
diff --git a/src/citra_qt/config.cpp b/src/citra_qt/config.cpp
index 6e4ba3907..ba7edaff9 100644
--- a/src/citra_qt/config.cpp
+++ b/src/citra_qt/config.cpp
@@ -22,12 +22,16 @@ Config::Config() {
22} 22}
23 23
24static const std::array<QVariant, Settings::NativeInput::NUM_INPUTS> defaults = { 24static const std::array<QVariant, Settings::NativeInput::NUM_INPUTS> defaults = {
25 // directly mapped keys
25 Qt::Key_A, Qt::Key_S, Qt::Key_Z, Qt::Key_X, 26 Qt::Key_A, Qt::Key_S, Qt::Key_Z, Qt::Key_X,
26 Qt::Key_Q, Qt::Key_W, Qt::Key_1, Qt::Key_2, 27 Qt::Key_Q, Qt::Key_W, Qt::Key_1, Qt::Key_2,
27 Qt::Key_M, Qt::Key_N, Qt::Key_B, 28 Qt::Key_M, Qt::Key_N, Qt::Key_B,
28 Qt::Key_T, Qt::Key_G, Qt::Key_F, Qt::Key_H, 29 Qt::Key_T, Qt::Key_G, Qt::Key_F, Qt::Key_H,
30 Qt::Key_I, Qt::Key_K, Qt::Key_J, Qt::Key_L,
31
32 // indirectly mapped keys
29 Qt::Key_Up, Qt::Key_Down, Qt::Key_Left, Qt::Key_Right, 33 Qt::Key_Up, Qt::Key_Down, Qt::Key_Left, Qt::Key_Right,
30 Qt::Key_I, Qt::Key_K, Qt::Key_J, Qt::Key_L 34 Qt::Key_D,
31}; 35};
32 36
33void Config::ReadValues() { 37void Config::ReadValues() {
@@ -36,6 +40,7 @@ void Config::ReadValues() {
36 Settings::values.input_mappings[Settings::NativeInput::All[i]] = 40 Settings::values.input_mappings[Settings::NativeInput::All[i]] =
37 qt_config->value(QString::fromStdString(Settings::NativeInput::Mapping[i]), defaults[i]).toInt(); 41 qt_config->value(QString::fromStdString(Settings::NativeInput::Mapping[i]), defaults[i]).toInt();
38 } 42 }
43 Settings::values.pad_circle_modifier_scale = qt_config->value("pad_circle_modifier_scale", 0.5).toFloat();
39 qt_config->endGroup(); 44 qt_config->endGroup();
40 45
41 qt_config->beginGroup("Core"); 46 qt_config->beginGroup("Core");
@@ -126,6 +131,7 @@ void Config::SaveValues() {
126 qt_config->setValue(QString::fromStdString(Settings::NativeInput::Mapping[i]), 131 qt_config->setValue(QString::fromStdString(Settings::NativeInput::Mapping[i]),
127 Settings::values.input_mappings[Settings::NativeInput::All[i]]); 132 Settings::values.input_mappings[Settings::NativeInput::All[i]]);
128 } 133 }
134 qt_config->setValue("pad_circle_modifier_scale", (double)Settings::values.pad_circle_modifier_scale);
129 qt_config->endGroup(); 135 qt_config->endGroup();
130 136
131 qt_config->beginGroup("Core"); 137 qt_config->beginGroup("Core");
diff --git a/src/common/emu_window.cpp b/src/common/emu_window.cpp
index b2807354a..08270dd88 100644
--- a/src/common/emu_window.cpp
+++ b/src/common/emu_window.cpp
@@ -11,12 +11,28 @@
11#include "emu_window.h" 11#include "emu_window.h"
12#include "video_core/video_core.h" 12#include "video_core/video_core.h"
13 13
14void EmuWindow::KeyPressed(KeyMap::HostDeviceKey key) { 14void EmuWindow::ButtonPressed(Service::HID::PadState pad) {
15 pad_state.hex |= KeyMap::GetPadKey(key).hex; 15 pad_state.hex |= pad.hex;
16} 16}
17 17
18void EmuWindow::KeyReleased(KeyMap::HostDeviceKey key) { 18void EmuWindow::ButtonReleased(Service::HID::PadState pad) {
19 pad_state.hex &= ~KeyMap::GetPadKey(key).hex; 19 pad_state.hex &= ~pad.hex;
20}
21
22void EmuWindow::CirclePadUpdated(float x, float y) {
23 constexpr int MAX_CIRCLEPAD_POS = 0x9C; // Max value for a circle pad position
24
25 // Make sure the coordinates are in the unit circle,
26 // otherwise normalize it.
27 float r = x * x + y * y;
28 if (r > 1) {
29 r = std::sqrt(r);
30 x /= r;
31 y /= r;
32 }
33
34 circle_pad_x = static_cast<s16>(x * MAX_CIRCLEPAD_POS);
35 circle_pad_y = static_cast<s16>(y * MAX_CIRCLEPAD_POS);
20} 36}
21 37
22/** 38/**
diff --git a/src/common/emu_window.h b/src/common/emu_window.h
index 7c3486dea..57e303b6d 100644
--- a/src/common/emu_window.h
+++ b/src/common/emu_window.h
@@ -12,10 +12,6 @@
12 12
13#include "core/hle/service/hid/hid.h" 13#include "core/hle/service/hid/hid.h"
14 14
15namespace KeyMap {
16struct HostDeviceKey;
17}
18
19/** 15/**
20 * Abstraction class used to provide an interface between emulation code and the frontend 16 * Abstraction class used to provide an interface between emulation code and the frontend
21 * (e.g. SDL, QGLWidget, GLFW, etc...). 17 * (e.g. SDL, QGLWidget, GLFW, etc...).
@@ -76,11 +72,27 @@ public:
76 72
77 virtual void ReloadSetKeymaps() = 0; 73 virtual void ReloadSetKeymaps() = 0;
78 74
79 /// Signals a key press action to the HID module 75 /**
80 void KeyPressed(KeyMap::HostDeviceKey key); 76 * Signals a button press action to the HID module.
77 * @param pad_state indicates which button to press
78 * @note only handles real buttons (A/B/X/Y/...), excluding analog inputs like the circle pad.
79 */
80 void ButtonPressed(Service::HID::PadState pad_state);
81
82 /**
83 * Signals a button release action to the HID module.
84 * @param pad_state indicates which button to press
85 * @note only handles real buttons (A/B/X/Y/...), excluding analog inputs like the circle pad.
86 */
87 void ButtonReleased(Service::HID::PadState pad_state);
81 88
82 /// Signals a key release action to the HID module 89 /**
83 void KeyReleased(KeyMap::HostDeviceKey key); 90 * Signals a circle pad change action to the HID module.
91 * @param x new x-coordinate of the circle pad, in the range [-1.0, 1.0]
92 * @param y new y-coordinate of the circle pad, in the range [-1.0, 1.0]
93 * @note the coordinates will be normalized if the radius is larger than 1
94 */
95 void CirclePadUpdated(float x, float y);
84 96
85 /** 97 /**
86 * Signal that a touch pressed event has occurred (e.g. mouse click pressed) 98 * Signal that a touch pressed event has occurred (e.g. mouse click pressed)
@@ -100,8 +112,9 @@ public:
100 void TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y); 112 void TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y);
101 113
102 /** 114 /**
103 * Gets the current pad state (which buttons are pressed and the circle pad direction). 115 * Gets the current pad state (which buttons are pressed).
104 * @note This should be called by the core emu thread to get a state set by the window thread. 116 * @note This should be called by the core emu thread to get a state set by the window thread.
117 * @note This doesn't include analog input like circle pad direction
105 * @todo Fix this function to be thread-safe. 118 * @todo Fix this function to be thread-safe.
106 * @return PadState object indicating the current pad state 119 * @return PadState object indicating the current pad state
107 */ 120 */
@@ -110,6 +123,16 @@ public:
110 } 123 }
111 124
112 /** 125 /**
126 * Gets the current circle pad state.
127 * @note This should be called by the core emu thread to get a state set by the window thread.
128 * @todo Fix this function to be thread-safe.
129 * @return std::tuple of (x, y), where `x` and `y` are the circle pad coordinates
130 */
131 std::tuple<s16, s16> GetCirclePadState() const {
132 return std::make_tuple(circle_pad_x, circle_pad_y);
133 }
134
135 /**
113 * Gets the current touch screen state (touch X/Y coordinates and whether or not it is pressed). 136 * Gets the current touch screen state (touch X/Y coordinates and whether or not it is pressed).
114 * @note This should be called by the core emu thread to get a state set by the window thread. 137 * @note This should be called by the core emu thread to get a state set by the window thread.
115 * @todo Fix this function to be thread-safe. 138 * @todo Fix this function to be thread-safe.
@@ -200,6 +223,8 @@ protected:
200 pad_state.hex = 0; 223 pad_state.hex = 0;
201 touch_x = 0; 224 touch_x = 0;
202 touch_y = 0; 225 touch_y = 0;
226 circle_pad_x = 0;
227 circle_pad_y = 0;
203 touch_pressed = false; 228 touch_pressed = false;
204 } 229 }
205 virtual ~EmuWindow() {} 230 virtual ~EmuWindow() {}
@@ -260,6 +285,9 @@ private:
260 u16 touch_x; ///< Touchpad X-position in native 3DS pixel coordinates (0-320) 285 u16 touch_x; ///< Touchpad X-position in native 3DS pixel coordinates (0-320)
261 u16 touch_y; ///< Touchpad Y-position in native 3DS pixel coordinates (0-240) 286 u16 touch_y; ///< Touchpad Y-position in native 3DS pixel coordinates (0-240)
262 287
288 s16 circle_pad_x; ///< Circle pad X-position in native 3DS pixel coordinates (-156 - 156)
289 s16 circle_pad_y; ///< Circle pad Y-position in native 3DS pixel coordinates (-156 - 156)
290
263 /** 291 /**
264 * Clip the provided coordinates to be inside the touchscreen area. 292 * Clip the provided coordinates to be inside the touchscreen area.
265 */ 293 */
diff --git a/src/common/key_map.cpp b/src/common/key_map.cpp
index 844d5df68..ad311d66b 100644
--- a/src/common/key_map.cpp
+++ b/src/common/key_map.cpp
@@ -2,24 +2,138 @@
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 "key_map.h"
6#include <map> 5#include <map>
7 6
7#include "common/emu_window.h"
8#include "common/key_map.h"
9
8namespace KeyMap { 10namespace KeyMap {
9 11
10static std::map<HostDeviceKey, Service::HID::PadState> key_map; 12// TODO (wwylele): currently we treat c-stick as four direction buttons
13// and map it directly to EmuWindow::ButtonPressed.
14// It should go the analog input way like circle pad does.
15const std::array<KeyTarget, Settings::NativeInput::NUM_INPUTS> mapping_targets = {{
16 Service::HID::PAD_A, Service::HID::PAD_B, Service::HID::PAD_X, Service::HID::PAD_Y,
17 Service::HID::PAD_L, Service::HID::PAD_R, Service::HID::PAD_ZL, Service::HID::PAD_ZR,
18 Service::HID::PAD_START, Service::HID::PAD_SELECT, Service::HID::PAD_NONE,
19 Service::HID::PAD_UP, Service::HID::PAD_DOWN, Service::HID::PAD_LEFT, Service::HID::PAD_RIGHT,
20 Service::HID::PAD_C_UP, Service::HID::PAD_C_DOWN, Service::HID::PAD_C_LEFT, Service::HID::PAD_C_RIGHT,
21
22 IndirectTarget::CirclePadUp,
23 IndirectTarget::CirclePadDown,
24 IndirectTarget::CirclePadLeft,
25 IndirectTarget::CirclePadRight,
26 IndirectTarget::CirclePadModifier,
27}};
28
29static std::map<HostDeviceKey, KeyTarget> key_map;
11static int next_device_id = 0; 30static int next_device_id = 0;
12 31
32static bool circle_pad_up = false;
33static bool circle_pad_down = false;
34static bool circle_pad_left = false;
35static bool circle_pad_right = false;
36static bool circle_pad_modifier = false;
37
38static void UpdateCirclePad(EmuWindow& emu_window) {
39 constexpr float SQRT_HALF = 0.707106781;
40 int x = 0, y = 0;
41
42 if (circle_pad_right)
43 ++x;
44 if (circle_pad_left)
45 --x;
46 if (circle_pad_up)
47 ++y;
48 if (circle_pad_down)
49 --y;
50
51 float modifier = circle_pad_modifier ? Settings::values.pad_circle_modifier_scale : 1.0;
52 emu_window.CirclePadUpdated(x * modifier * (y == 0 ? 1.0 : SQRT_HALF), y * modifier * (x == 0 ? 1.0 : SQRT_HALF));
53}
54
13int NewDeviceId() { 55int NewDeviceId() {
14 return next_device_id++; 56 return next_device_id++;
15} 57}
16 58
17void SetKeyMapping(HostDeviceKey key, Service::HID::PadState padState) { 59void SetKeyMapping(HostDeviceKey key, KeyTarget target) {
18 key_map[key].hex = padState.hex; 60 key_map[key] = target;
61}
62
63void ClearKeyMapping(int device_id) {
64 auto iter = key_map.begin();
65 while (iter != key_map.end()) {
66 if (iter->first.device_id == device_id)
67 key_map.erase(iter++);
68 else
69 ++iter;
70 }
19} 71}
20 72
21Service::HID::PadState GetPadKey(HostDeviceKey key) { 73void PressKey(EmuWindow& emu_window, HostDeviceKey key) {
22 return key_map[key]; 74 auto target = key_map.find(key);
75 if (target == key_map.end())
76 return;
77
78 if (target->second.direct) {
79 emu_window.ButtonPressed({{target->second.target.direct_target_hex}});
80 } else {
81 switch (target->second.target.indirect_target) {
82 case IndirectTarget::CirclePadUp:
83 circle_pad_up = true;
84 UpdateCirclePad(emu_window);
85 break;
86 case IndirectTarget::CirclePadDown:
87 circle_pad_down = true;
88 UpdateCirclePad(emu_window);
89 break;
90 case IndirectTarget::CirclePadLeft:
91 circle_pad_left = true;
92 UpdateCirclePad(emu_window);
93 break;
94 case IndirectTarget::CirclePadRight:
95 circle_pad_right = true;
96 UpdateCirclePad(emu_window);
97 break;
98 case IndirectTarget::CirclePadModifier:
99 circle_pad_modifier = true;
100 UpdateCirclePad(emu_window);
101 break;
102 }
103 }
104}
105
106void ReleaseKey(EmuWindow& emu_window,HostDeviceKey key) {
107 auto target = key_map.find(key);
108 if (target == key_map.end())
109 return;
110
111 if (target->second.direct) {
112 emu_window.ButtonReleased({{target->second.target.direct_target_hex}});
113 } else {
114 switch (target->second.target.indirect_target) {
115 case IndirectTarget::CirclePadUp:
116 circle_pad_up = false;
117 UpdateCirclePad(emu_window);
118 break;
119 case IndirectTarget::CirclePadDown:
120 circle_pad_down = false;
121 UpdateCirclePad(emu_window);
122 break;
123 case IndirectTarget::CirclePadLeft:
124 circle_pad_left = false;
125 UpdateCirclePad(emu_window);
126 break;
127 case IndirectTarget::CirclePadRight:
128 circle_pad_right = false;
129 UpdateCirclePad(emu_window);
130 break;
131 case IndirectTarget::CirclePadModifier:
132 circle_pad_modifier = false;
133 UpdateCirclePad(emu_window);
134 break;
135 }
136 }
23} 137}
24 138
25} 139}
diff --git a/src/common/key_map.h b/src/common/key_map.h
index 68f7e2f99..b62f017c6 100644
--- a/src/common/key_map.h
+++ b/src/common/key_map.h
@@ -4,12 +4,51 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <array>
7#include <tuple> 8#include <tuple>
8#include "core/hle/service/hid/hid.h" 9#include "core/hle/service/hid/hid.h"
9 10
11class EmuWindow;
12
10namespace KeyMap { 13namespace KeyMap {
11 14
12/** 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 */
19enum 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 */
31struct 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/**
13 * Represents a key for a specific host device. 52 * Represents a key for a specific host device.
14 */ 53 */
15struct HostDeviceKey { 54struct HostDeviceKey {
@@ -27,19 +66,31 @@ struct HostDeviceKey {
27 } 66 }
28}; 67};
29 68
69extern const std::array<KeyTarget, Settings::NativeInput::NUM_INPUTS> mapping_targets;
70
30/** 71/**
31 * Generates a new device id, which uniquely identifies a host device within KeyMap. 72 * Generates a new device id, which uniquely identifies a host device within KeyMap.
32 */ 73 */
33int NewDeviceId(); 74int NewDeviceId();
34 75
35/** 76/**
36 * Maps a device-specific key to a PadState. 77 * Maps a device-specific key to a target (a PadState or an IndirectTarget).
78 */
79void SetKeyMapping(HostDeviceKey key, KeyTarget target);
80
81/**
82 * Clears all key mappings belonging to one device.
83 */
84void ClearKeyMapping(int device_id);
85
86/**
87 * Maps a key press action and call the corresponding function in EmuWindow
37 */ 88 */
38void SetKeyMapping(HostDeviceKey key, Service::HID::PadState padState); 89void PressKey(EmuWindow& emu_window, HostDeviceKey key);
39 90
40/** 91/**
41 * Gets the PadState that's mapped to the provided device-specific key. 92 * Maps a key release action and call the corresponding function in EmuWindow
42 */ 93 */
43Service::HID::PadState GetPadKey(HostDeviceKey key); 94void ReleaseKey(EmuWindow& emu_window, HostDeviceKey key);
44 95
45} 96}
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index d216cecb4..c975433f4 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -19,8 +19,6 @@
19namespace Service { 19namespace Service {
20namespace HID { 20namespace HID {
21 21
22static const int MAX_CIRCLEPAD_POS = 0x9C; ///< Max value for a circle pad position
23
24// Handle to shared memory region designated to HID_User service 22// Handle to shared memory region designated to HID_User service
25static Kernel::SharedPtr<Kernel::SharedMemory> shared_mem; 23static Kernel::SharedPtr<Kernel::SharedMemory> shared_mem;
26 24
@@ -39,38 +37,48 @@ static u32 next_gyroscope_index;
39static int enable_accelerometer_count = 0; // positive means enabled 37static int enable_accelerometer_count = 0; // positive means enabled
40static int enable_gyroscope_count = 0; // positive means enabled 38static int enable_gyroscope_count = 0; // positive means enabled
41 39
42const std::array<Service::HID::PadState, Settings::NativeInput::NUM_INPUTS> pad_mapping = {{ 40static PadState GetCirclePadDirectionState(s16 circle_pad_x, s16 circle_pad_y) {
43 Service::HID::PAD_A, Service::HID::PAD_B, Service::HID::PAD_X, Service::HID::PAD_Y, 41 constexpr float TAN30 = 0.577350269, TAN60 = 1 / TAN30; // 30 degree and 60 degree are angular thresholds for directions
44 Service::HID::PAD_L, Service::HID::PAD_R, Service::HID::PAD_ZL, Service::HID::PAD_ZR, 42 constexpr int CIRCLE_PAD_THRESHOLD_SQUARE = 40 * 40; // a circle pad radius greater than 40 will trigger circle pad direction
45 Service::HID::PAD_START, Service::HID::PAD_SELECT, Service::HID::PAD_NONE, 43 PadState state;
46 Service::HID::PAD_UP, Service::HID::PAD_DOWN, Service::HID::PAD_LEFT, Service::HID::PAD_RIGHT, 44 state.hex = 0;
47 Service::HID::PAD_CIRCLE_UP, Service::HID::PAD_CIRCLE_DOWN, Service::HID::PAD_CIRCLE_LEFT, Service::HID::PAD_CIRCLE_RIGHT, 45
48 Service::HID::PAD_C_UP, Service::HID::PAD_C_DOWN, Service::HID::PAD_C_LEFT, Service::HID::PAD_C_RIGHT 46 if (circle_pad_x * circle_pad_x + circle_pad_y * circle_pad_y > CIRCLE_PAD_THRESHOLD_SQUARE) {
49}}; 47 float t = std::abs(static_cast<float>(circle_pad_y) / circle_pad_x);
50 48
51 49 if (circle_pad_x != 0 && t < TAN60) {
52// TODO(peachum): 50 if (circle_pad_x > 0)
53// Add a method for setting analog input from joystick device for the circle Pad. 51 state.circle_right.Assign(1);
54// 52 else
55// This method should: 53 state.circle_left.Assign(1);
56// * Be called after both PadButton<Press, Release>(). 54 }
57// * Be called before PadUpdateComplete() 55
58// * Set current PadEntry.circle_pad_<axis> using analog data 56 if (circle_pad_x == 0 || t > TAN30) {
59// * Set PadData.raw_circle_pad_data 57 if (circle_pad_y > 0)
60// * Set PadData.current_state.circle_right = 1 if current PadEntry.circle_pad_x >= 41 58 state.circle_up.Assign(1);
61// * Set PadData.current_state.circle_up = 1 if current PadEntry.circle_pad_y >= 41 59 else
62// * Set PadData.current_state.circle_left = 1 if current PadEntry.circle_pad_x <= -41 60 state.circle_down.Assign(1);
63// * Set PadData.current_state.circle_right = 1 if current PadEntry.circle_pad_y <= -41 61 }
62 }
63
64 return state;
65}
64 66
65void Update() { 67void Update() {
66 SharedMem* mem = reinterpret_cast<SharedMem*>(shared_mem->GetPointer()); 68 SharedMem* mem = reinterpret_cast<SharedMem*>(shared_mem->GetPointer());
67 const PadState state = VideoCore::g_emu_window->GetPadState();
68 69
69 if (mem == nullptr) { 70 if (mem == nullptr) {
70 LOG_DEBUG(Service_HID, "Cannot update HID prior to mapping shared memory!"); 71 LOG_DEBUG(Service_HID, "Cannot update HID prior to mapping shared memory!");
71 return; 72 return;
72 } 73 }
73 74
75 PadState state = VideoCore::g_emu_window->GetPadState();
76
77 // Get current circle pad position and update circle pad direction
78 s16 circle_pad_x, circle_pad_y;
79 std::tie(circle_pad_x, circle_pad_y) = VideoCore::g_emu_window->GetCirclePadState();
80 state.hex |= GetCirclePadDirectionState(circle_pad_x, circle_pad_y).hex;
81
74 mem->pad.current_state.hex = state.hex; 82 mem->pad.current_state.hex = state.hex;
75 mem->pad.index = next_pad_index; 83 mem->pad.index = next_pad_index;
76 next_pad_index = (next_pad_index + 1) % mem->pad.entries.size(); 84 next_pad_index = (next_pad_index + 1) % mem->pad.entries.size();
@@ -88,13 +96,9 @@ void Update() {
88 // Update entry properties 96 // Update entry properties
89 pad_entry.current_state.hex = state.hex; 97 pad_entry.current_state.hex = state.hex;
90 pad_entry.delta_additions.hex = changed.hex & state.hex; 98 pad_entry.delta_additions.hex = changed.hex & state.hex;
91 pad_entry.delta_removals.hex = changed.hex & old_state.hex;; 99 pad_entry.delta_removals.hex = changed.hex & old_state.hex;
92 100 pad_entry.circle_pad_x = circle_pad_x;
93 // Set circle Pad 101 pad_entry.circle_pad_y = circle_pad_y;
94 pad_entry.circle_pad_x = state.circle_left ? -MAX_CIRCLEPAD_POS :
95 state.circle_right ? MAX_CIRCLEPAD_POS : 0x0;
96 pad_entry.circle_pad_y = state.circle_down ? -MAX_CIRCLEPAD_POS :
97 state.circle_up ? MAX_CIRCLEPAD_POS : 0x0;
98 102
99 // If we just updated index 0, provide a new timestamp 103 // If we just updated index 0, provide a new timestamp
100 if (mem->pad.index == 0) { 104 if (mem->pad.index == 0) {
diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h
index 170d19ea8..669b1f723 100644
--- a/src/core/hle/service/hid/hid.h
+++ b/src/core/hle/service/hid/hid.h
@@ -215,9 +215,6 @@ const PadState PAD_CIRCLE_LEFT = {{1u << 29}};
215const PadState PAD_CIRCLE_UP = {{1u << 30}}; 215const PadState PAD_CIRCLE_UP = {{1u << 30}};
216const PadState PAD_CIRCLE_DOWN = {{1u << 31}}; 216const PadState PAD_CIRCLE_DOWN = {{1u << 31}};
217 217
218
219extern const std::array<Service::HID::PadState, Settings::NativeInput::NUM_INPUTS> pad_mapping;
220
221/** 218/**
222 * HID::GetIPCHandles service function 219 * HID::GetIPCHandles service function
223 * Inputs: 220 * Inputs:
diff --git a/src/core/settings.h b/src/core/settings.h
index ea72f4d9c..f95e62390 100644
--- a/src/core/settings.h
+++ b/src/core/settings.h
@@ -13,29 +13,40 @@ namespace Settings {
13 13
14namespace NativeInput { 14namespace NativeInput {
15enum Values { 15enum Values {
16 // directly mapped keys
16 A, B, X, Y, 17 A, B, X, Y,
17 L, R, ZL, ZR, 18 L, R, ZL, ZR,
18 START, SELECT, HOME, 19 START, SELECT, HOME,
19 DUP, DDOWN, DLEFT, DRIGHT, 20 DUP, DDOWN, DLEFT, DRIGHT,
20 SUP, SDOWN, SLEFT, SRIGHT,
21 CUP, CDOWN, CLEFT, CRIGHT, 21 CUP, CDOWN, CLEFT, CRIGHT,
22
23 // indirectly mapped keys
24 CIRCLE_UP, CIRCLE_DOWN, CIRCLE_LEFT, CIRCLE_RIGHT,
25 CIRCLE_MODIFIER,
26
22 NUM_INPUTS 27 NUM_INPUTS
23}; 28};
29
24static const std::array<const char*, NUM_INPUTS> Mapping = {{ 30static const std::array<const char*, NUM_INPUTS> Mapping = {{
31 // directly mapped keys
25 "pad_a", "pad_b", "pad_x", "pad_y", 32 "pad_a", "pad_b", "pad_x", "pad_y",
26 "pad_l", "pad_r", "pad_zl", "pad_zr", 33 "pad_l", "pad_r", "pad_zl", "pad_zr",
27 "pad_start", "pad_select", "pad_home", 34 "pad_start", "pad_select", "pad_home",
28 "pad_dup", "pad_ddown", "pad_dleft", "pad_dright", 35 "pad_dup", "pad_ddown", "pad_dleft", "pad_dright",
29 "pad_sup", "pad_sdown", "pad_sleft", "pad_sright", 36 "pad_cup", "pad_cdown", "pad_cleft", "pad_cright",
30 "pad_cup", "pad_cdown", "pad_cleft", "pad_cright" 37
38 // indirectly mapped keys
39 "pad_circle_up", "pad_circle_down", "pad_circle_left", "pad_circle_right",
40 "pad_circle_modifier",
31}}; 41}};
32static const std::array<Values, NUM_INPUTS> All = {{ 42static const std::array<Values, NUM_INPUTS> All = {{
33 A, B, X, Y, 43 A, B, X, Y,
34 L, R, ZL, ZR, 44 L, R, ZL, ZR,
35 START, SELECT, HOME, 45 START, SELECT, HOME,
36 DUP, DDOWN, DLEFT, DRIGHT, 46 DUP, DDOWN, DLEFT, DRIGHT,
37 SUP, SDOWN, SLEFT, SRIGHT, 47 CUP, CDOWN, CLEFT, CRIGHT,
38 CUP, CDOWN, CLEFT, CRIGHT 48 CIRCLE_UP, CIRCLE_DOWN, CIRCLE_LEFT, CIRCLE_RIGHT,
49 CIRCLE_MODIFIER,
39}}; 50}};
40} 51}
41 52
@@ -46,6 +57,7 @@ struct Values {
46 57
47 // Controls 58 // Controls
48 std::array<int, NativeInput::NUM_INPUTS> input_mappings; 59 std::array<int, NativeInput::NUM_INPUTS> input_mappings;
60 float pad_circle_modifier_scale;
49 61
50 // Core 62 // Core
51 int frame_skip; 63 int frame_skip;