summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/citra/config.cpp3
-rw-r--r--src/citra/default_ini.h8
-rw-r--r--src/citra/emu_window/emu_window_sdl2.cpp10
-rw-r--r--src/citra/emu_window/emu_window_sdl2.h4
-rw-r--r--src/citra_qt/bootmanager.cpp10
-rw-r--r--src/citra_qt/bootmanager.h4
-rw-r--r--src/citra_qt/configuration/config.cpp6
-rw-r--r--src/core/CMakeLists.txt2
-rw-r--r--src/core/frontend/emu_window.cpp23
-rw-r--r--src/core/frontend/emu_window.h83
-rw-r--r--src/core/frontend/input.h19
-rw-r--r--src/core/frontend/motion_emu.cpp89
-rw-r--r--src/core/frontend/motion_emu.h52
-rw-r--r--src/core/hle/service/hid/hid.cpp32
-rw-r--r--src/core/settings.h1
-rw-r--r--src/input_common/CMakeLists.txt2
-rw-r--r--src/input_common/main.cpp15
-rw-r--r--src/input_common/main.h5
-rw-r--r--src/input_common/motion_emu.cpp165
-rw-r--r--src/input_common/motion_emu.h46
20 files changed, 302 insertions, 277 deletions
diff --git a/src/citra/config.cpp b/src/citra/config.cpp
index 69247b166..73846ed91 100644
--- a/src/citra/config.cpp
+++ b/src/citra/config.cpp
@@ -76,6 +76,9 @@ void Config::ReadValues() {
76 Settings::values.analogs[i] = default_param; 76 Settings::values.analogs[i] = default_param;
77 } 77 }
78 78
79 Settings::values.motion_device = sdl2_config->Get(
80 "Controls", "motion_device", "engine:motion_emu,update_period:100,sensitivity:0.01");
81
79 // Core 82 // Core
80 Settings::values.use_cpu_jit = sdl2_config->GetBoolean("Core", "use_cpu_jit", true); 83 Settings::values.use_cpu_jit = sdl2_config->GetBoolean("Core", "use_cpu_jit", true);
81 84
diff --git a/src/citra/default_ini.h b/src/citra/default_ini.h
index b0a0ebd3b..9ea779dd8 100644
--- a/src/citra/default_ini.h
+++ b/src/citra/default_ini.h
@@ -43,7 +43,7 @@ button_zr=
43button_home= 43button_home=
44 44
45# for analog input, the following devices are available: 45# for analog input, the following devices are available:
46# - "analog_from_button" (default) for emulating analog input from direction buttons. Required parameters: 46# - "analog_from_button" (default) for emulating analog input from direction buttons. Required parameters:
47# - "up", "down", "left", "right": sub-devices for each direction. 47# - "up", "down", "left", "right": sub-devices for each direction.
48# Should be in the format as a button input devices using escape characters, for example, "engine$0keyboard$1code$00" 48# Should be in the format as a button input devices using escape characters, for example, "engine$0keyboard$1code$00"
49# - "modifier": sub-devices as a modifier. 49# - "modifier": sub-devices as a modifier.
@@ -56,6 +56,12 @@ button_home=
56circle_pad= 56circle_pad=
57c_stick= 57c_stick=
58 58
59# for motion input, the following devices are available:
60# - "motion_emu" (default) for emulating motion input from mouse input. Required parameters:
61# - "update_period": update period in milliseconds (default to 100)
62# - "sensitivity": the coefficient converting mouse movement to tilting angle (default to 0.01)
63motion_device=
64
59[Core] 65[Core]
60# Whether to use the Just-In-Time (JIT) compiler for CPU emulation 66# Whether to use the Just-In-Time (JIT) compiler for CPU emulation
61# 0: Interpreter (slow), 1 (default): JIT (fast) 67# 0: Interpreter (slow), 1 (default): JIT (fast)
diff --git a/src/citra/emu_window/emu_window_sdl2.cpp b/src/citra/emu_window/emu_window_sdl2.cpp
index b0f808399..25643715a 100644
--- a/src/citra/emu_window/emu_window_sdl2.cpp
+++ b/src/citra/emu_window/emu_window_sdl2.cpp
@@ -16,11 +16,12 @@
16#include "core/settings.h" 16#include "core/settings.h"
17#include "input_common/keyboard.h" 17#include "input_common/keyboard.h"
18#include "input_common/main.h" 18#include "input_common/main.h"
19#include "input_common/motion_emu.h"
19#include "network/network.h" 20#include "network/network.h"
20 21
21void EmuWindow_SDL2::OnMouseMotion(s32 x, s32 y) { 22void EmuWindow_SDL2::OnMouseMotion(s32 x, s32 y) {
22 TouchMoved((unsigned)std::max(x, 0), (unsigned)std::max(y, 0)); 23 TouchMoved((unsigned)std::max(x, 0), (unsigned)std::max(y, 0));
23 motion_emu->Tilt(x, y); 24 InputCommon::GetMotionEmu()->Tilt(x, y);
24} 25}
25 26
26void EmuWindow_SDL2::OnMouseButton(u32 button, u8 state, s32 x, s32 y) { 27void EmuWindow_SDL2::OnMouseButton(u32 button, u8 state, s32 x, s32 y) {
@@ -32,9 +33,9 @@ void EmuWindow_SDL2::OnMouseButton(u32 button, u8 state, s32 x, s32 y) {
32 } 33 }
33 } else if (button == SDL_BUTTON_RIGHT) { 34 } else if (button == SDL_BUTTON_RIGHT) {
34 if (state == SDL_PRESSED) { 35 if (state == SDL_PRESSED) {
35 motion_emu->BeginTilt(x, y); 36 InputCommon::GetMotionEmu()->BeginTilt(x, y);
36 } else { 37 } else {
37 motion_emu->EndTilt(); 38 InputCommon::GetMotionEmu()->EndTilt();
38 } 39 }
39 } 40 }
40} 41}
@@ -61,8 +62,6 @@ EmuWindow_SDL2::EmuWindow_SDL2() {
61 InputCommon::Init(); 62 InputCommon::Init();
62 Network::Init(); 63 Network::Init();
63 64
64 motion_emu = std::make_unique<Motion::MotionEmu>(*this);
65
66 SDL_SetMainReady(); 65 SDL_SetMainReady();
67 66
68 // Initialize the window 67 // Initialize the window
@@ -117,7 +116,6 @@ EmuWindow_SDL2::EmuWindow_SDL2() {
117EmuWindow_SDL2::~EmuWindow_SDL2() { 116EmuWindow_SDL2::~EmuWindow_SDL2() {
118 SDL_GL_DeleteContext(gl_context); 117 SDL_GL_DeleteContext(gl_context);
119 SDL_Quit(); 118 SDL_Quit();
120 motion_emu = nullptr;
121 119
122 Network::Shutdown(); 120 Network::Shutdown();
123 InputCommon::Shutdown(); 121 InputCommon::Shutdown();
diff --git a/src/citra/emu_window/emu_window_sdl2.h b/src/citra/emu_window/emu_window_sdl2.h
index 1ce2991f7..3664d2fbe 100644
--- a/src/citra/emu_window/emu_window_sdl2.h
+++ b/src/citra/emu_window/emu_window_sdl2.h
@@ -7,7 +7,6 @@
7#include <memory> 7#include <memory>
8#include <utility> 8#include <utility>
9#include "core/frontend/emu_window.h" 9#include "core/frontend/emu_window.h"
10#include "core/frontend/motion_emu.h"
11 10
12struct SDL_Window; 11struct SDL_Window;
13 12
@@ -57,7 +56,4 @@ private:
57 using SDL_GLContext = void*; 56 using SDL_GLContext = void*;
58 /// The OpenGL context associated with the window 57 /// The OpenGL context associated with the window
59 SDL_GLContext gl_context; 58 SDL_GLContext gl_context;
60
61 /// Motion sensors emulation
62 std::unique_ptr<Motion::MotionEmu> motion_emu;
63}; 59};
diff --git a/src/citra_qt/bootmanager.cpp b/src/citra_qt/bootmanager.cpp
index 30554890f..7107bfc60 100644
--- a/src/citra_qt/bootmanager.cpp
+++ b/src/citra_qt/bootmanager.cpp
@@ -17,6 +17,7 @@
17#include "core/settings.h" 17#include "core/settings.h"
18#include "input_common/keyboard.h" 18#include "input_common/keyboard.h"
19#include "input_common/main.h" 19#include "input_common/main.h"
20#include "input_common/motion_emu.h"
20#include "network/network.h" 21#include "network/network.h"
21 22
22EmuThread::EmuThread(GRenderWindow* render_window) 23EmuThread::EmuThread(GRenderWindow* render_window)
@@ -201,7 +202,6 @@ qreal GRenderWindow::windowPixelRatio() {
201} 202}
202 203
203void GRenderWindow::closeEvent(QCloseEvent* event) { 204void GRenderWindow::closeEvent(QCloseEvent* event) {
204 motion_emu = nullptr;
205 emit Closed(); 205 emit Closed();
206 QWidget::closeEvent(event); 206 QWidget::closeEvent(event);
207} 207}
@@ -221,7 +221,7 @@ void GRenderWindow::mousePressEvent(QMouseEvent* event) {
221 this->TouchPressed(static_cast<unsigned>(pos.x() * pixelRatio), 221 this->TouchPressed(static_cast<unsigned>(pos.x() * pixelRatio),
222 static_cast<unsigned>(pos.y() * pixelRatio)); 222 static_cast<unsigned>(pos.y() * pixelRatio));
223 } else if (event->button() == Qt::RightButton) { 223 } else if (event->button() == Qt::RightButton) {
224 motion_emu->BeginTilt(pos.x(), pos.y()); 224 InputCommon::GetMotionEmu()->BeginTilt(pos.x(), pos.y());
225 } 225 }
226} 226}
227 227
@@ -230,14 +230,14 @@ void GRenderWindow::mouseMoveEvent(QMouseEvent* event) {
230 qreal pixelRatio = windowPixelRatio(); 230 qreal pixelRatio = windowPixelRatio();
231 this->TouchMoved(std::max(static_cast<unsigned>(pos.x() * pixelRatio), 0u), 231 this->TouchMoved(std::max(static_cast<unsigned>(pos.x() * pixelRatio), 0u),
232 std::max(static_cast<unsigned>(pos.y() * pixelRatio), 0u)); 232 std::max(static_cast<unsigned>(pos.y() * pixelRatio), 0u));
233 motion_emu->Tilt(pos.x(), pos.y()); 233 InputCommon::GetMotionEmu()->Tilt(pos.x(), pos.y());
234} 234}
235 235
236void GRenderWindow::mouseReleaseEvent(QMouseEvent* event) { 236void GRenderWindow::mouseReleaseEvent(QMouseEvent* event) {
237 if (event->button() == Qt::LeftButton) 237 if (event->button() == Qt::LeftButton)
238 this->TouchReleased(); 238 this->TouchReleased();
239 else if (event->button() == Qt::RightButton) 239 else if (event->button() == Qt::RightButton)
240 motion_emu->EndTilt(); 240 InputCommon::GetMotionEmu()->EndTilt();
241} 241}
242 242
243void GRenderWindow::focusOutEvent(QFocusEvent* event) { 243void GRenderWindow::focusOutEvent(QFocusEvent* event) {
@@ -290,13 +290,11 @@ void GRenderWindow::OnMinimalClientAreaChangeRequest(
290} 290}
291 291
292void GRenderWindow::OnEmulationStarting(EmuThread* emu_thread) { 292void GRenderWindow::OnEmulationStarting(EmuThread* emu_thread) {
293 motion_emu = std::make_unique<Motion::MotionEmu>(*this);
294 this->emu_thread = emu_thread; 293 this->emu_thread = emu_thread;
295 child->DisablePainting(); 294 child->DisablePainting();
296} 295}
297 296
298void GRenderWindow::OnEmulationStopping() { 297void GRenderWindow::OnEmulationStopping() {
299 motion_emu = nullptr;
300 emu_thread = nullptr; 298 emu_thread = nullptr;
301 child->EnablePainting(); 299 child->EnablePainting();
302} 300}
diff --git a/src/citra_qt/bootmanager.h b/src/citra_qt/bootmanager.h
index 4b3a3b3cc..6974edcbb 100644
--- a/src/citra_qt/bootmanager.h
+++ b/src/citra_qt/bootmanager.h
@@ -12,7 +12,6 @@
12#include "common/thread.h" 12#include "common/thread.h"
13#include "core/core.h" 13#include "core/core.h"
14#include "core/frontend/emu_window.h" 14#include "core/frontend/emu_window.h"
15#include "core/frontend/motion_emu.h"
16 15
17class QKeyEvent; 16class QKeyEvent;
18class QScreen; 17class QScreen;
@@ -158,9 +157,6 @@ private:
158 157
159 EmuThread* emu_thread; 158 EmuThread* emu_thread;
160 159
161 /// Motion sensors emulation
162 std::unique_ptr<Motion::MotionEmu> motion_emu;
163
164protected: 160protected:
165 void showEvent(QShowEvent* event) override; 161 void showEvent(QShowEvent* event) override;
166}; 162};
diff --git a/src/citra_qt/configuration/config.cpp b/src/citra_qt/configuration/config.cpp
index 75abb4ce6..6e42db007 100644
--- a/src/citra_qt/configuration/config.cpp
+++ b/src/citra_qt/configuration/config.cpp
@@ -57,6 +57,11 @@ void Config::ReadValues() {
57 Settings::values.analogs[i] = default_param; 57 Settings::values.analogs[i] = default_param;
58 } 58 }
59 59
60 Settings::values.motion_device =
61 qt_config->value("motion_device", "engine:motion_emu,update_period:100,sensitivity:0.01")
62 .toString()
63 .toStdString();
64
60 qt_config->endGroup(); 65 qt_config->endGroup();
61 66
62 qt_config->beginGroup("Core"); 67 qt_config->beginGroup("Core");
@@ -203,6 +208,7 @@ void Config::SaveValues() {
203 qt_config->setValue(QString::fromStdString(Settings::NativeAnalog::mapping[i]), 208 qt_config->setValue(QString::fromStdString(Settings::NativeAnalog::mapping[i]),
204 QString::fromStdString(Settings::values.analogs[i])); 209 QString::fromStdString(Settings::values.analogs[i]));
205 } 210 }
211 qt_config->setValue("motion_device", QString::fromStdString(Settings::values.motion_device));
206 qt_config->endGroup(); 212 qt_config->endGroup();
207 213
208 qt_config->beginGroup("Core"); 214 qt_config->beginGroup("Core");
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 0a6f97e4b..53bd50eb2 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -33,7 +33,6 @@ set(SRCS
33 frontend/camera/interface.cpp 33 frontend/camera/interface.cpp
34 frontend/emu_window.cpp 34 frontend/emu_window.cpp
35 frontend/framebuffer_layout.cpp 35 frontend/framebuffer_layout.cpp
36 frontend/motion_emu.cpp
37 gdbstub/gdbstub.cpp 36 gdbstub/gdbstub.cpp
38 hle/config_mem.cpp 37 hle/config_mem.cpp
39 hle/applets/applet.cpp 38 hle/applets/applet.cpp
@@ -226,7 +225,6 @@ set(HEADERS
226 frontend/emu_window.h 225 frontend/emu_window.h
227 frontend/framebuffer_layout.h 226 frontend/framebuffer_layout.h
228 frontend/input.h 227 frontend/input.h
229 frontend/motion_emu.h
230 gdbstub/gdbstub.h 228 gdbstub/gdbstub.h
231 hle/config_mem.h 229 hle/config_mem.h
232 hle/function_wrappers.h 230 hle/function_wrappers.h
diff --git a/src/core/frontend/emu_window.cpp b/src/core/frontend/emu_window.cpp
index 4f7d54a33..60b20d4e2 100644
--- a/src/core/frontend/emu_window.cpp
+++ b/src/core/frontend/emu_window.cpp
@@ -62,29 +62,6 @@ void EmuWindow::TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y) {
62 TouchPressed(framebuffer_x, framebuffer_y); 62 TouchPressed(framebuffer_x, framebuffer_y);
63} 63}
64 64
65void EmuWindow::AccelerometerChanged(float x, float y, float z) {
66 constexpr float coef = 512;
67
68 std::lock_guard<std::mutex> lock(accel_mutex);
69
70 // TODO(wwylele): do a time stretch as it in GyroscopeChanged
71 // The time stretch formula should be like
72 // stretched_vector = (raw_vector - gravity) * stretch_ratio + gravity
73 accel_x = static_cast<s16>(x * coef);
74 accel_y = static_cast<s16>(y * coef);
75 accel_z = static_cast<s16>(z * coef);
76}
77
78void EmuWindow::GyroscopeChanged(float x, float y, float z) {
79 constexpr float FULL_FPS = 60;
80 float coef = GetGyroscopeRawToDpsCoefficient();
81 float stretch = Core::System::GetInstance().perf_stats.GetLastFrameTimeScale();
82 std::lock_guard<std::mutex> lock(gyro_mutex);
83 gyro_x = static_cast<s16>(x * coef * stretch);
84 gyro_y = static_cast<s16>(y * coef * stretch);
85 gyro_z = static_cast<s16>(z * coef * stretch);
86}
87
88void EmuWindow::UpdateCurrentFramebufferLayout(unsigned width, unsigned height) { 65void EmuWindow::UpdateCurrentFramebufferLayout(unsigned width, unsigned height) {
89 Layout::FramebufferLayout layout; 66 Layout::FramebufferLayout layout;
90 if (Settings::values.custom_layout == true) { 67 if (Settings::values.custom_layout == true) {
diff --git a/src/core/frontend/emu_window.h b/src/core/frontend/emu_window.h
index 9414123a4..7bdee251c 100644
--- a/src/core/frontend/emu_window.h
+++ b/src/core/frontend/emu_window.h
@@ -69,27 +69,6 @@ public:
69 void TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y); 69 void TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y);
70 70
71 /** 71 /**
72 * Signal accelerometer state has changed.
73 * @param x X-axis accelerometer value
74 * @param y Y-axis accelerometer value
75 * @param z Z-axis accelerometer value
76 * @note all values are in unit of g (gravitational acceleration).
77 * e.g. x = 1.0 means 9.8m/s^2 in x direction.
78 * @see GetAccelerometerState for axis explanation.
79 */
80 void AccelerometerChanged(float x, float y, float z);
81
82 /**
83 * Signal gyroscope state has changed.
84 * @param x X-axis accelerometer value
85 * @param y Y-axis accelerometer value
86 * @param z Z-axis accelerometer value
87 * @note all values are in deg/sec.
88 * @see GetGyroscopeState for axis explanation.
89 */
90 void GyroscopeChanged(float x, float y, float z);
91
92 /**
93 * Gets the current touch screen state (touch X/Y coordinates and whether or not it is pressed). 72 * Gets the current touch screen state (touch X/Y coordinates and whether or not it is pressed).
94 * @note This should be called by the core emu thread to get a state set by the window thread. 73 * @note This should be called by the core emu thread to get a state set by the window thread.
95 * @todo Fix this function to be thread-safe. 74 * @todo Fix this function to be thread-safe.
@@ -101,52 +80,6 @@ public:
101 } 80 }
102 81
103 /** 82 /**
104 * Gets the current accelerometer state (acceleration along each three axis).
105 * Axis explained:
106 * +x is the same direction as LEFT on D-pad.
107 * +y is normal to the touch screen, pointing outward.
108 * +z is the same direction as UP on D-pad.
109 * Units:
110 * 1 unit of return value = 1/512 g (measured by hw test),
111 * where g is the gravitational acceleration (9.8 m/sec2).
112 * @note This should be called by the core emu thread to get a state set by the window thread.
113 * @return std::tuple of (x, y, z)
114 */
115 std::tuple<s16, s16, s16> GetAccelerometerState() {
116 std::lock_guard<std::mutex> lock(accel_mutex);
117 return std::make_tuple(accel_x, accel_y, accel_z);
118 }
119
120 /**
121 * Gets the current gyroscope state (angular rates about each three axis).
122 * Axis explained:
123 * +x is the same direction as LEFT on D-pad.
124 * +y is normal to the touch screen, pointing outward.
125 * +z is the same direction as UP on D-pad.
126 * Orientation is determined by right-hand rule.
127 * Units:
128 * 1 unit of return value = (1/coef) deg/sec,
129 * where coef is the return value of GetGyroscopeRawToDpsCoefficient().
130 * @note This should be called by the core emu thread to get a state set by the window thread.
131 * @return std::tuple of (x, y, z)
132 */
133 std::tuple<s16, s16, s16> GetGyroscopeState() {
134 std::lock_guard<std::mutex> lock(gyro_mutex);
135 return std::make_tuple(gyro_x, gyro_y, gyro_z);
136 }
137
138 /**
139 * Gets the coefficient for units conversion of gyroscope state.
140 * The conversion formula is r = coefficient * v,
141 * where v is angular rate in deg/sec,
142 * and r is the gyroscope state.
143 * @return float-type coefficient
144 */
145 f32 GetGyroscopeRawToDpsCoefficient() const {
146 return 14.375f; // taken from hw test, and gyroscope's document
147 }
148
149 /**
150 * Returns currently active configuration. 83 * Returns currently active configuration.
151 * @note Accesses to the returned object need not be consistent because it may be modified in 84 * @note Accesses to the returned object need not be consistent because it may be modified in
152 * another thread 85 * another thread
@@ -187,12 +120,6 @@ protected:
187 touch_x = 0; 120 touch_x = 0;
188 touch_y = 0; 121 touch_y = 0;
189 touch_pressed = false; 122 touch_pressed = false;
190 accel_x = 0;
191 accel_y = -512;
192 accel_z = 0;
193 gyro_x = 0;
194 gyro_y = 0;
195 gyro_z = 0;
196 } 123 }
197 virtual ~EmuWindow() {} 124 virtual ~EmuWindow() {}
198 125
@@ -255,16 +182,6 @@ private:
255 u16 touch_x; ///< Touchpad X-position in native 3DS pixel coordinates (0-320) 182 u16 touch_x; ///< Touchpad X-position in native 3DS pixel coordinates (0-320)
256 u16 touch_y; ///< Touchpad Y-position in native 3DS pixel coordinates (0-240) 183 u16 touch_y; ///< Touchpad Y-position in native 3DS pixel coordinates (0-240)
257 184
258 std::mutex accel_mutex;
259 s16 accel_x; ///< Accelerometer X-axis value in native 3DS units
260 s16 accel_y; ///< Accelerometer Y-axis value in native 3DS units
261 s16 accel_z; ///< Accelerometer Z-axis value in native 3DS units
262
263 std::mutex gyro_mutex;
264 s16 gyro_x; ///< Gyroscope X-axis value in native 3DS units
265 s16 gyro_y; ///< Gyroscope Y-axis value in native 3DS units
266 s16 gyro_z; ///< Gyroscope Z-axis value in native 3DS units
267
268 /** 185 /**
269 * Clip the provided coordinates to be inside the touchscreen area. 186 * Clip the provided coordinates to be inside the touchscreen area.
270 */ 187 */
diff --git a/src/core/frontend/input.h b/src/core/frontend/input.h
index 0a5713dc0..5916a901d 100644
--- a/src/core/frontend/input.h
+++ b/src/core/frontend/input.h
@@ -11,6 +11,7 @@
11#include <utility> 11#include <utility>
12#include "common/logging/log.h" 12#include "common/logging/log.h"
13#include "common/param_package.h" 13#include "common/param_package.h"
14#include "common/vector_math.h"
14 15
15namespace Input { 16namespace Input {
16 17
@@ -107,4 +108,22 @@ using ButtonDevice = InputDevice<bool>;
107 */ 108 */
108using AnalogDevice = InputDevice<std::tuple<float, float>>; 109using AnalogDevice = InputDevice<std::tuple<float, float>>;
109 110
111/**
112 * A motion device is an input device that returns a tuple of accelerometer state vector and
113 * gyroscope state vector.
114 *
115 * For both vectors:
116 * x+ is the same direction as LEFT on D-pad.
117 * y+ is normal to the touch screen, pointing outward.
118 * z+ is the same direction as UP on D-pad.
119 *
120 * For accelerometer state vector
121 * Units: g (gravitational acceleration)
122 *
123 * For gyroscope state vector:
124 * Orientation is determined by right-hand rule.
125 * Units: deg/sec
126 */
127using MotionDevice = InputDevice<std::tuple<Math::Vec3<float>, Math::Vec3<float>>>;
128
110} // namespace Input 129} // namespace Input
diff --git a/src/core/frontend/motion_emu.cpp b/src/core/frontend/motion_emu.cpp
deleted file mode 100644
index 9a5b3185d..000000000
--- a/src/core/frontend/motion_emu.cpp
+++ /dev/null
@@ -1,89 +0,0 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/math_util.h"
6#include "common/quaternion.h"
7#include "core/frontend/emu_window.h"
8#include "core/frontend/motion_emu.h"
9
10namespace Motion {
11
12static constexpr int update_millisecond = 100;
13static constexpr auto update_duration =
14 std::chrono::duration_cast<std::chrono::steady_clock::duration>(
15 std::chrono::milliseconds(update_millisecond));
16
17MotionEmu::MotionEmu(EmuWindow& emu_window)
18 : motion_emu_thread(&MotionEmu::MotionEmuThread, this, std::ref(emu_window)) {}
19
20MotionEmu::~MotionEmu() {
21 if (motion_emu_thread.joinable()) {
22 shutdown_event.Set();
23 motion_emu_thread.join();
24 }
25}
26
27void MotionEmu::MotionEmuThread(EmuWindow& emu_window) {
28 auto update_time = std::chrono::steady_clock::now();
29 Math::Quaternion<float> q = MakeQuaternion(Math::Vec3<float>(), 0);
30 Math::Quaternion<float> old_q;
31
32 while (!shutdown_event.WaitUntil(update_time)) {
33 update_time += update_duration;
34 old_q = q;
35
36 {
37 std::lock_guard<std::mutex> guard(tilt_mutex);
38
39 // Find the quaternion describing current 3DS tilting
40 q = MakeQuaternion(Math::MakeVec(-tilt_direction.y, 0.0f, tilt_direction.x),
41 tilt_angle);
42 }
43
44 auto inv_q = q.Inverse();
45
46 // Set the gravity vector in world space
47 auto gravity = Math::MakeVec(0.0f, -1.0f, 0.0f);
48
49 // Find the angular rate vector in world space
50 auto angular_rate = ((q - old_q) * inv_q).xyz * 2;
51 angular_rate *= 1000 / update_millisecond / MathUtil::PI * 180;
52
53 // Transform the two vectors from world space to 3DS space
54 gravity = QuaternionRotate(inv_q, gravity);
55 angular_rate = QuaternionRotate(inv_q, angular_rate);
56
57 // Update the sensor state
58 emu_window.AccelerometerChanged(gravity.x, gravity.y, gravity.z);
59 emu_window.GyroscopeChanged(angular_rate.x, angular_rate.y, angular_rate.z);
60 }
61}
62
63void MotionEmu::BeginTilt(int x, int y) {
64 mouse_origin = Math::MakeVec(x, y);
65 is_tilting = true;
66}
67
68void MotionEmu::Tilt(int x, int y) {
69 constexpr float SENSITIVITY = 0.01f;
70 auto mouse_move = Math::MakeVec(x, y) - mouse_origin;
71 if (is_tilting) {
72 std::lock_guard<std::mutex> guard(tilt_mutex);
73 if (mouse_move.x == 0 && mouse_move.y == 0) {
74 tilt_angle = 0;
75 } else {
76 tilt_direction = mouse_move.Cast<float>();
77 tilt_angle = MathUtil::Clamp(tilt_direction.Normalize() * SENSITIVITY, 0.0f,
78 MathUtil::PI * 0.5f);
79 }
80 }
81}
82
83void MotionEmu::EndTilt() {
84 std::lock_guard<std::mutex> guard(tilt_mutex);
85 tilt_angle = 0;
86 is_tilting = false;
87}
88
89} // namespace Motion
diff --git a/src/core/frontend/motion_emu.h b/src/core/frontend/motion_emu.h
deleted file mode 100644
index 99d41a726..000000000
--- a/src/core/frontend/motion_emu.h
+++ /dev/null
@@ -1,52 +0,0 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6#include "common/thread.h"
7#include "common/vector_math.h"
8
9class EmuWindow;
10
11namespace Motion {
12
13class MotionEmu final {
14public:
15 MotionEmu(EmuWindow& emu_window);
16 ~MotionEmu();
17
18 /**
19 * Signals that a motion sensor tilt has begun.
20 * @param x the x-coordinate of the cursor
21 * @param y the y-coordinate of the cursor
22 */
23 void BeginTilt(int x, int y);
24
25 /**
26 * Signals that a motion sensor tilt is occurring.
27 * @param x the x-coordinate of the cursor
28 * @param y the y-coordinate of the cursor
29 */
30 void Tilt(int x, int y);
31
32 /**
33 * Signals that a motion sensor tilt has ended.
34 */
35 void EndTilt();
36
37private:
38 Math::Vec2<int> mouse_origin;
39
40 std::mutex tilt_mutex;
41 Math::Vec2<float> tilt_direction;
42 float tilt_angle = 0;
43
44 bool is_tilting = false;
45
46 Common::Event shutdown_event;
47 std::thread motion_emu_thread;
48
49 void MotionEmuThread(EmuWindow& emu_window);
50};
51
52} // namespace Motion
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index 2014b8461..31f34a7ae 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -7,6 +7,7 @@
7#include <cmath> 7#include <cmath>
8#include <memory> 8#include <memory>
9#include "common/logging/log.h" 9#include "common/logging/log.h"
10#include "core/core.h"
10#include "core/core_timing.h" 11#include "core/core_timing.h"
11#include "core/frontend/emu_window.h" 12#include "core/frontend/emu_window.h"
12#include "core/frontend/input.h" 13#include "core/frontend/input.h"
@@ -50,10 +51,14 @@ constexpr u64 pad_update_ticks = BASE_CLOCK_RATE_ARM11 / 234;
50constexpr u64 accelerometer_update_ticks = BASE_CLOCK_RATE_ARM11 / 104; 51constexpr u64 accelerometer_update_ticks = BASE_CLOCK_RATE_ARM11 / 104;
51constexpr u64 gyroscope_update_ticks = BASE_CLOCK_RATE_ARM11 / 101; 52constexpr u64 gyroscope_update_ticks = BASE_CLOCK_RATE_ARM11 / 101;
52 53
54constexpr float accelerometer_coef = 512.0f; // measured from hw test result
55constexpr float gyroscope_coef = 14.375f; // got from hwtest GetGyroscopeLowRawToDpsCoefficient call
56
53static std::atomic<bool> is_device_reload_pending; 57static std::atomic<bool> is_device_reload_pending;
54static std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeButton::NUM_BUTTONS_HID> 58static std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeButton::NUM_BUTTONS_HID>
55 buttons; 59 buttons;
56static std::unique_ptr<Input::AnalogDevice> circle_pad; 60static std::unique_ptr<Input::AnalogDevice> circle_pad;
61static std::unique_ptr<Input::MotionDevice> motion_device;
57 62
58DirectionState GetStickDirectionState(s16 circle_pad_x, s16 circle_pad_y) { 63DirectionState GetStickDirectionState(s16 circle_pad_x, s16 circle_pad_y) {
59 // 30 degree and 60 degree are angular thresholds for directions 64 // 30 degree and 60 degree are angular thresholds for directions
@@ -90,6 +95,7 @@ static void LoadInputDevices() {
90 buttons.begin(), Input::CreateDevice<Input::ButtonDevice>); 95 buttons.begin(), Input::CreateDevice<Input::ButtonDevice>);
91 circle_pad = Input::CreateDevice<Input::AnalogDevice>( 96 circle_pad = Input::CreateDevice<Input::AnalogDevice>(
92 Settings::values.analogs[Settings::NativeAnalog::CirclePad]); 97 Settings::values.analogs[Settings::NativeAnalog::CirclePad]);
98 motion_device = Input::CreateDevice<Input::MotionDevice>(Settings::values.motion_device);
93} 99}
94 100
95static void UnloadInputDevices() { 101static void UnloadInputDevices() {
@@ -97,6 +103,7 @@ static void UnloadInputDevices() {
97 button.reset(); 103 button.reset();
98 } 104 }
99 circle_pad.reset(); 105 circle_pad.reset();
106 motion_device.reset();
100} 107}
101 108
102static void UpdatePadCallback(u64 userdata, int cycles_late) { 109static void UpdatePadCallback(u64 userdata, int cycles_late) {
@@ -193,10 +200,19 @@ static void UpdateAccelerometerCallback(u64 userdata, int cycles_late) {
193 mem->accelerometer.index = next_accelerometer_index; 200 mem->accelerometer.index = next_accelerometer_index;
194 next_accelerometer_index = (next_accelerometer_index + 1) % mem->accelerometer.entries.size(); 201 next_accelerometer_index = (next_accelerometer_index + 1) % mem->accelerometer.entries.size();
195 202
203 Math::Vec3<float> accel;
204 std::tie(accel, std::ignore) = motion_device->GetStatus();
205 accel *= accelerometer_coef;
206 // TODO(wwylele): do a time stretch like the one in UpdateGyroscopeCallback
207 // The time stretch formula should be like
208 // stretched_vector = (raw_vector - gravity) * stretch_ratio + gravity
209
196 AccelerometerDataEntry& accelerometer_entry = 210 AccelerometerDataEntry& accelerometer_entry =
197 mem->accelerometer.entries[mem->accelerometer.index]; 211 mem->accelerometer.entries[mem->accelerometer.index];
198 std::tie(accelerometer_entry.x, accelerometer_entry.y, accelerometer_entry.z) = 212
199 VideoCore::g_emu_window->GetAccelerometerState(); 213 accelerometer_entry.x = static_cast<s16>(accel.x);
214 accelerometer_entry.y = static_cast<s16>(accel.y);
215 accelerometer_entry.z = static_cast<s16>(accel.z);
200 216
201 // Make up "raw" entry 217 // Make up "raw" entry
202 // TODO(wwylele): 218 // TODO(wwylele):
@@ -227,8 +243,14 @@ static void UpdateGyroscopeCallback(u64 userdata, int cycles_late) {
227 next_gyroscope_index = (next_gyroscope_index + 1) % mem->gyroscope.entries.size(); 243 next_gyroscope_index = (next_gyroscope_index + 1) % mem->gyroscope.entries.size();
228 244
229 GyroscopeDataEntry& gyroscope_entry = mem->gyroscope.entries[mem->gyroscope.index]; 245 GyroscopeDataEntry& gyroscope_entry = mem->gyroscope.entries[mem->gyroscope.index];
230 std::tie(gyroscope_entry.x, gyroscope_entry.y, gyroscope_entry.z) = 246
231 VideoCore::g_emu_window->GetGyroscopeState(); 247 Math::Vec3<float> gyro;
248 std::tie(std::ignore, gyro) = motion_device->GetStatus();
249 double stretch = Core::System::GetInstance().perf_stats.GetLastFrameTimeScale();
250 gyro *= gyroscope_coef * stretch;
251 gyroscope_entry.x = static_cast<s16>(gyro.x);
252 gyroscope_entry.y = static_cast<s16>(gyro.y);
253 gyroscope_entry.z = static_cast<s16>(gyro.z);
232 254
233 // Make up "raw" entry 255 // Make up "raw" entry
234 mem->gyroscope.raw_entry.x = gyroscope_entry.x; 256 mem->gyroscope.raw_entry.x = gyroscope_entry.x;
@@ -326,7 +348,7 @@ void GetGyroscopeLowRawToDpsCoefficient(Service::Interface* self) {
326 348
327 cmd_buff[1] = RESULT_SUCCESS.raw; 349 cmd_buff[1] = RESULT_SUCCESS.raw;
328 350
329 f32 coef = VideoCore::g_emu_window->GetGyroscopeRawToDpsCoefficient(); 351 f32 coef = gyroscope_coef;
330 memcpy(&cmd_buff[2], &coef, 4); 352 memcpy(&cmd_buff[2], &coef, 4);
331} 353}
332 354
diff --git a/src/core/settings.h b/src/core/settings.h
index ee16bb90a..7e15b119b 100644
--- a/src/core/settings.h
+++ b/src/core/settings.h
@@ -79,6 +79,7 @@ struct Values {
79 // Controls 79 // Controls
80 std::array<std::string, NativeButton::NumButtons> buttons; 80 std::array<std::string, NativeButton::NumButtons> buttons;
81 std::array<std::string, NativeAnalog::NumAnalogs> analogs; 81 std::array<std::string, NativeAnalog::NumAnalogs> analogs;
82 std::string motion_device;
82 83
83 // Core 84 // Core
84 bool use_cpu_jit; 85 bool use_cpu_jit;
diff --git a/src/input_common/CMakeLists.txt b/src/input_common/CMakeLists.txt
index e3e36ada7..92792a702 100644
--- a/src/input_common/CMakeLists.txt
+++ b/src/input_common/CMakeLists.txt
@@ -2,12 +2,14 @@ set(SRCS
2 analog_from_button.cpp 2 analog_from_button.cpp
3 keyboard.cpp 3 keyboard.cpp
4 main.cpp 4 main.cpp
5 motion_emu.cpp
5 ) 6 )
6 7
7set(HEADERS 8set(HEADERS
8 analog_from_button.h 9 analog_from_button.h
9 keyboard.h 10 keyboard.h
10 main.h 11 main.h
12 motion_emu.h
11 ) 13 )
12 14
13if(SDL2_FOUND) 15if(SDL2_FOUND)
diff --git a/src/input_common/main.cpp b/src/input_common/main.cpp
index 699f41e6b..557353740 100644
--- a/src/input_common/main.cpp
+++ b/src/input_common/main.cpp
@@ -7,6 +7,7 @@
7#include "input_common/analog_from_button.h" 7#include "input_common/analog_from_button.h"
8#include "input_common/keyboard.h" 8#include "input_common/keyboard.h"
9#include "input_common/main.h" 9#include "input_common/main.h"
10#include "input_common/motion_emu.h"
10#ifdef HAVE_SDL2 11#ifdef HAVE_SDL2
11#include "input_common/sdl/sdl.h" 12#include "input_common/sdl/sdl.h"
12#endif 13#endif
@@ -14,12 +15,16 @@
14namespace InputCommon { 15namespace InputCommon {
15 16
16static std::shared_ptr<Keyboard> keyboard; 17static std::shared_ptr<Keyboard> keyboard;
18static std::shared_ptr<MotionEmu> motion_emu;
17 19
18void Init() { 20void Init() {
19 keyboard = std::make_shared<InputCommon::Keyboard>(); 21 keyboard = std::make_shared<Keyboard>();
20 Input::RegisterFactory<Input::ButtonDevice>("keyboard", keyboard); 22 Input::RegisterFactory<Input::ButtonDevice>("keyboard", keyboard);
21 Input::RegisterFactory<Input::AnalogDevice>("analog_from_button", 23 Input::RegisterFactory<Input::AnalogDevice>("analog_from_button",
22 std::make_shared<InputCommon::AnalogFromButton>()); 24 std::make_shared<AnalogFromButton>());
25 motion_emu = std::make_shared<MotionEmu>();
26 Input::RegisterFactory<Input::MotionDevice>("motion_emu", motion_emu);
27
23#ifdef HAVE_SDL2 28#ifdef HAVE_SDL2
24 SDL::Init(); 29 SDL::Init();
25#endif 30#endif
@@ -29,6 +34,8 @@ void Shutdown() {
29 Input::UnregisterFactory<Input::ButtonDevice>("keyboard"); 34 Input::UnregisterFactory<Input::ButtonDevice>("keyboard");
30 keyboard.reset(); 35 keyboard.reset();
31 Input::UnregisterFactory<Input::AnalogDevice>("analog_from_button"); 36 Input::UnregisterFactory<Input::AnalogDevice>("analog_from_button");
37 Input::UnregisterFactory<Input::MotionDevice>("motion_emu");
38 motion_emu.reset();
32 39
33#ifdef HAVE_SDL2 40#ifdef HAVE_SDL2
34 SDL::Shutdown(); 41 SDL::Shutdown();
@@ -39,6 +46,10 @@ Keyboard* GetKeyboard() {
39 return keyboard.get(); 46 return keyboard.get();
40} 47}
41 48
49MotionEmu* GetMotionEmu() {
50 return motion_emu.get();
51}
52
42std::string GenerateKeyboardParam(int key_code) { 53std::string GenerateKeyboardParam(int key_code) {
43 Common::ParamPackage param{ 54 Common::ParamPackage param{
44 {"engine", "keyboard"}, {"code", std::to_string(key_code)}, 55 {"engine", "keyboard"}, {"code", std::to_string(key_code)},
diff --git a/src/input_common/main.h b/src/input_common/main.h
index e7fb3890e..5604f0fa8 100644
--- a/src/input_common/main.h
+++ b/src/input_common/main.h
@@ -19,6 +19,11 @@ class Keyboard;
19/// Gets the keyboard button device factory. 19/// Gets the keyboard button device factory.
20Keyboard* GetKeyboard(); 20Keyboard* GetKeyboard();
21 21
22class MotionEmu;
23
24/// Gets the motion emulation factory.
25MotionEmu* GetMotionEmu();
26
22/// Generates a serialized param package for creating a keyboard button device 27/// Generates a serialized param package for creating a keyboard button device
23std::string GenerateKeyboardParam(int key_code); 28std::string GenerateKeyboardParam(int key_code);
24 29
diff --git a/src/input_common/motion_emu.cpp b/src/input_common/motion_emu.cpp
new file mode 100644
index 000000000..a1761f184
--- /dev/null
+++ b/src/input_common/motion_emu.cpp
@@ -0,0 +1,165 @@
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 <chrono>
6#include <mutex>
7#include <thread>
8#include <tuple>
9#include "common/math_util.h"
10#include "common/quaternion.h"
11#include "common/thread.h"
12#include "common/vector_math.h"
13#include "input_common/motion_emu.h"
14
15namespace InputCommon {
16
17// Implementation class of the motion emulation device
18class MotionEmuDevice {
19public:
20 MotionEmuDevice(int update_millisecond, float sensitivity)
21 : update_millisecond(update_millisecond),
22 update_duration(std::chrono::duration_cast<std::chrono::steady_clock::duration>(
23 std::chrono::milliseconds(update_millisecond))),
24 sensitivity(sensitivity), motion_emu_thread(&MotionEmuDevice::MotionEmuThread, this) {}
25
26 ~MotionEmuDevice() {
27 if (motion_emu_thread.joinable()) {
28 shutdown_event.Set();
29 motion_emu_thread.join();
30 }
31 }
32
33 void BeginTilt(int x, int y) {
34 mouse_origin = Math::MakeVec(x, y);
35 is_tilting = true;
36 }
37
38 void Tilt(int x, int y) {
39 auto mouse_move = Math::MakeVec(x, y) - mouse_origin;
40 if (is_tilting) {
41 std::lock_guard<std::mutex> guard(tilt_mutex);
42 if (mouse_move.x == 0 && mouse_move.y == 0) {
43 tilt_angle = 0;
44 } else {
45 tilt_direction = mouse_move.Cast<float>();
46 tilt_angle = MathUtil::Clamp(tilt_direction.Normalize() * sensitivity, 0.0f,
47 MathUtil::PI * 0.5f);
48 }
49 }
50 }
51
52 void EndTilt() {
53 std::lock_guard<std::mutex> guard(tilt_mutex);
54 tilt_angle = 0;
55 is_tilting = false;
56 }
57
58 std::tuple<Math::Vec3<float>, Math::Vec3<float>> GetStatus() {
59 std::lock_guard<std::mutex> guard(status_mutex);
60 return status;
61 }
62
63private:
64 const int update_millisecond;
65 const std::chrono::steady_clock::duration update_duration;
66 const float sensitivity;
67
68 Math::Vec2<int> mouse_origin;
69
70 std::mutex tilt_mutex;
71 Math::Vec2<float> tilt_direction;
72 float tilt_angle = 0;
73
74 bool is_tilting = false;
75
76 Common::Event shutdown_event;
77 std::thread motion_emu_thread;
78
79 std::tuple<Math::Vec3<float>, Math::Vec3<float>> status;
80 std::mutex status_mutex;
81
82 void MotionEmuThread() {
83 auto update_time = std::chrono::steady_clock::now();
84 Math::Quaternion<float> q = MakeQuaternion(Math::Vec3<float>(), 0);
85 Math::Quaternion<float> old_q;
86
87 while (!shutdown_event.WaitUntil(update_time)) {
88 update_time += update_duration;
89 old_q = q;
90
91 {
92 std::lock_guard<std::mutex> guard(tilt_mutex);
93
94 // Find the quaternion describing current 3DS tilting
95 q = MakeQuaternion(Math::MakeVec(-tilt_direction.y, 0.0f, tilt_direction.x),
96 tilt_angle);
97 }
98
99 auto inv_q = q.Inverse();
100
101 // Set the gravity vector in world space
102 auto gravity = Math::MakeVec(0.0f, -1.0f, 0.0f);
103
104 // Find the angular rate vector in world space
105 auto angular_rate = ((q - old_q) * inv_q).xyz * 2;
106 angular_rate *= 1000 / update_millisecond / MathUtil::PI * 180;
107
108 // Transform the two vectors from world space to 3DS space
109 gravity = QuaternionRotate(inv_q, gravity);
110 angular_rate = QuaternionRotate(inv_q, angular_rate);
111
112 // Update the sensor state
113 {
114 std::lock_guard<std::mutex> guard(status_mutex);
115 status = std::make_tuple(gravity, angular_rate);
116 }
117 }
118 }
119};
120
121// Interface wrapper held by input receiver as a unique_ptr. It holds the implementation class as
122// a shared_ptr, which is also observed by the factory class as a weak_ptr. In this way the factory
123// can forward all the inputs to the implementation only when it is valid.
124class MotionEmuDeviceWrapper : public Input::MotionDevice {
125public:
126 MotionEmuDeviceWrapper(int update_millisecond, float sensitivity) {
127 device = std::make_shared<MotionEmuDevice>(update_millisecond, sensitivity);
128 }
129
130 std::tuple<Math::Vec3<float>, Math::Vec3<float>> GetStatus() const {
131 return device->GetStatus();
132 }
133
134 std::shared_ptr<MotionEmuDevice> device;
135};
136
137std::unique_ptr<Input::MotionDevice> MotionEmu::Create(const Common::ParamPackage& params) {
138 int update_period = params.Get("update_period", 100);
139 float sensitivity = params.Get("sensitivity", 0.01f);
140 auto device_wrapper = std::make_unique<MotionEmuDeviceWrapper>(update_period, sensitivity);
141 // Previously created device is disconnected here. Having two motion devices for 3DS is not
142 // expected.
143 current_device = device_wrapper->device;
144 return std::move(device_wrapper);
145}
146
147void MotionEmu::BeginTilt(int x, int y) {
148 if (auto ptr = current_device.lock()) {
149 ptr->BeginTilt(x, y);
150 }
151}
152
153void MotionEmu::Tilt(int x, int y) {
154 if (auto ptr = current_device.lock()) {
155 ptr->Tilt(x, y);
156 }
157}
158
159void MotionEmu::EndTilt() {
160 if (auto ptr = current_device.lock()) {
161 ptr->EndTilt();
162 }
163}
164
165} // namespace InputCommon
diff --git a/src/input_common/motion_emu.h b/src/input_common/motion_emu.h
new file mode 100644
index 000000000..7a7e22467
--- /dev/null
+++ b/src/input_common/motion_emu.h
@@ -0,0 +1,46 @@
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
9namespace InputCommon {
10
11class MotionEmuDevice;
12
13class MotionEmu : public Input::Factory<Input::MotionDevice> {
14public:
15 /**
16 * Creates a motion device emulated from mouse input
17 * @param params contains parameters for creating the device:
18 * - "update_period": update period in milliseconds
19 * - "sensitivity": the coefficient converting mouse movement to tilting angle
20 */
21 std::unique_ptr<Input::MotionDevice> Create(const Common::ParamPackage& params) override;
22
23 /**
24 * Signals that a motion sensor tilt has begun.
25 * @param x the x-coordinate of the cursor
26 * @param y the y-coordinate of the cursor
27 */
28 void BeginTilt(int x, int y);
29
30 /**
31 * Signals that a motion sensor tilt is occurring.
32 * @param x the x-coordinate of the cursor
33 * @param y the y-coordinate of the cursor
34 */
35 void Tilt(int x, int y);
36
37 /**
38 * Signals that a motion sensor tilt has ended.
39 */
40 void EndTilt();
41
42private:
43 std::weak_ptr<MotionEmuDevice> current_device;
44};
45
46} // namespace InputCommon