summaryrefslogtreecommitdiff
path: root/src/core/frontend
diff options
context:
space:
mode:
authorGravatar bunnei2017-10-09 23:56:20 -0400
committerGravatar bunnei2017-10-09 23:56:20 -0400
commitb1d5db1cf60344b6b081c9d03cb6ccc3264326cd (patch)
treefde377c4ba3c0f92c032e6f5ec8627aae37270ef /src/core/frontend
parentloader: Various improvements for NSO/NRO loaders. (diff)
parentMerge pull request #2996 from MerryMage/split-travis (diff)
downloadyuzu-b1d5db1cf60344b6b081c9d03cb6ccc3264326cd.tar.gz
yuzu-b1d5db1cf60344b6b081c9d03cb6ccc3264326cd.tar.xz
yuzu-b1d5db1cf60344b6b081c9d03cb6ccc3264326cd.zip
Merge remote-tracking branch 'upstream/master' into nx
# Conflicts: # src/core/CMakeLists.txt # src/core/arm/dynarmic/arm_dynarmic.cpp # src/core/arm/dyncom/arm_dyncom.cpp # src/core/hle/kernel/process.cpp # src/core/hle/kernel/thread.cpp # src/core/hle/kernel/thread.h # src/core/hle/kernel/vm_manager.cpp # src/core/loader/3dsx.cpp # src/core/loader/elf.cpp # src/core/loader/ncch.cpp # src/core/memory.cpp # src/core/memory.h # src/core/memory_setup.h
Diffstat (limited to 'src/core/frontend')
-rw-r--r--src/core/frontend/emu_window.cpp97
-rw-r--r--src/core/frontend/emu_window.h114
-rw-r--r--src/core/frontend/framebuffer_layout.cpp36
-rw-r--r--src/core/frontend/framebuffer_layout.h11
-rw-r--r--src/core/frontend/input.h25
-rw-r--r--src/core/frontend/motion_emu.cpp89
-rw-r--r--src/core/frontend/motion_emu.h52
7 files changed, 137 insertions, 287 deletions
diff --git a/src/core/frontend/emu_window.cpp b/src/core/frontend/emu_window.cpp
index 4f7d54a33..e67394177 100644
--- a/src/core/frontend/emu_window.cpp
+++ b/src/core/frontend/emu_window.cpp
@@ -2,14 +2,55 @@
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 <cmath> 5#include <cmath>
7#include "common/assert.h" 6#include <mutex>
8#include "core/3ds.h"
9#include "core/core.h"
10#include "core/frontend/emu_window.h" 7#include "core/frontend/emu_window.h"
8#include "core/frontend/input.h"
11#include "core/settings.h" 9#include "core/settings.h"
12 10
11class EmuWindow::TouchState : public Input::Factory<Input::TouchDevice>,
12 public std::enable_shared_from_this<TouchState> {
13public:
14 std::unique_ptr<Input::TouchDevice> Create(const Common::ParamPackage&) override {
15 return std::make_unique<Device>(shared_from_this());
16 }
17
18 std::mutex mutex;
19
20 bool touch_pressed = false; ///< True if touchpad area is currently pressed, otherwise false
21
22 float touch_x = 0.0f; ///< Touchpad X-position
23 float touch_y = 0.0f; ///< Touchpad Y-position
24
25private:
26 class Device : public Input::TouchDevice {
27 public:
28 explicit Device(std::weak_ptr<TouchState>&& touch_state) : touch_state(touch_state) {}
29 std::tuple<float, float, bool> GetStatus() const override {
30 if (auto state = touch_state.lock()) {
31 std::lock_guard<std::mutex> guard(state->mutex);
32 return std::make_tuple(state->touch_x, state->touch_y, state->touch_pressed);
33 }
34 return std::make_tuple(0.0f, 0.0f, false);
35 }
36
37 private:
38 std::weak_ptr<TouchState> touch_state;
39 };
40};
41
42EmuWindow::EmuWindow() {
43 // TODO: Find a better place to set this.
44 config.min_client_area_size = std::make_pair(400u, 480u);
45 active_config = config;
46 touch_state = std::make_shared<TouchState>();
47 Input::RegisterFactory<Input::TouchDevice>("emu_window", touch_state);
48}
49
50EmuWindow::~EmuWindow() {
51 Input::UnregisterFactory<Input::TouchDevice>("emu_window");
52}
53
13/** 54/**
14 * Check if the given x/y coordinates are within the touchpad specified by the framebuffer layout 55 * Check if the given x/y coordinates are within the touchpad specified by the framebuffer layout
15 * @param layout FramebufferLayout object describing the framebuffer size and screen positions 56 * @param layout FramebufferLayout object describing the framebuffer size and screen positions
@@ -38,22 +79,26 @@ void EmuWindow::TouchPressed(unsigned framebuffer_x, unsigned framebuffer_y) {
38 if (!IsWithinTouchscreen(framebuffer_layout, framebuffer_x, framebuffer_y)) 79 if (!IsWithinTouchscreen(framebuffer_layout, framebuffer_x, framebuffer_y))
39 return; 80 return;
40 81
41 touch_x = Core::kScreenBottomWidth * (framebuffer_x - framebuffer_layout.bottom_screen.left) / 82 std::lock_guard<std::mutex> guard(touch_state->mutex);
42 (framebuffer_layout.bottom_screen.right - framebuffer_layout.bottom_screen.left); 83 touch_state->touch_x =
43 touch_y = Core::kScreenBottomHeight * (framebuffer_y - framebuffer_layout.bottom_screen.top) / 84 static_cast<float>(framebuffer_x - framebuffer_layout.bottom_screen.left) /
44 (framebuffer_layout.bottom_screen.bottom - framebuffer_layout.bottom_screen.top); 85 (framebuffer_layout.bottom_screen.right - framebuffer_layout.bottom_screen.left);
86 touch_state->touch_y =
87 static_cast<float>(framebuffer_y - framebuffer_layout.bottom_screen.top) /
88 (framebuffer_layout.bottom_screen.bottom - framebuffer_layout.bottom_screen.top);
45 89
46 touch_pressed = true; 90 touch_state->touch_pressed = true;
47} 91}
48 92
49void EmuWindow::TouchReleased() { 93void EmuWindow::TouchReleased() {
50 touch_pressed = false; 94 std::lock_guard<std::mutex> guard(touch_state->mutex);
51 touch_x = 0; 95 touch_state->touch_pressed = false;
52 touch_y = 0; 96 touch_state->touch_x = 0;
97 touch_state->touch_y = 0;
53} 98}
54 99
55void EmuWindow::TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y) { 100void EmuWindow::TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y) {
56 if (!touch_pressed) 101 if (!touch_state->touch_pressed)
57 return; 102 return;
58 103
59 if (!IsWithinTouchscreen(framebuffer_layout, framebuffer_x, framebuffer_y)) 104 if (!IsWithinTouchscreen(framebuffer_layout, framebuffer_x, framebuffer_y))
@@ -62,29 +107,6 @@ void EmuWindow::TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y) {
62 TouchPressed(framebuffer_x, framebuffer_y); 107 TouchPressed(framebuffer_x, framebuffer_y);
63} 108}
64 109
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) { 110void EmuWindow::UpdateCurrentFramebufferLayout(unsigned width, unsigned height) {
89 Layout::FramebufferLayout layout; 111 Layout::FramebufferLayout layout;
90 if (Settings::values.custom_layout == true) { 112 if (Settings::values.custom_layout == true) {
@@ -97,6 +119,9 @@ void EmuWindow::UpdateCurrentFramebufferLayout(unsigned width, unsigned height)
97 case Settings::LayoutOption::LargeScreen: 119 case Settings::LayoutOption::LargeScreen:
98 layout = Layout::LargeFrameLayout(width, height, Settings::values.swap_screen); 120 layout = Layout::LargeFrameLayout(width, height, Settings::values.swap_screen);
99 break; 121 break;
122 case Settings::LayoutOption::SideScreen:
123 layout = Layout::SideFrameLayout(width, height, Settings::values.swap_screen);
124 break;
100 case Settings::LayoutOption::Default: 125 case Settings::LayoutOption::Default:
101 default: 126 default:
102 layout = Layout::DefaultFrameLayout(width, height, Settings::values.swap_screen); 127 layout = Layout::DefaultFrameLayout(width, height, Settings::values.swap_screen);
diff --git a/src/core/frontend/emu_window.h b/src/core/frontend/emu_window.h
index 9414123a4..c10dee51b 100644
--- a/src/core/frontend/emu_window.h
+++ b/src/core/frontend/emu_window.h
@@ -4,11 +4,10 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <mutex> 7#include <memory>
8#include <tuple> 8#include <tuple>
9#include <utility> 9#include <utility>
10#include "common/common_types.h" 10#include "common/common_types.h"
11#include "common/math_util.h"
12#include "core/frontend/framebuffer_layout.h" 11#include "core/frontend/framebuffer_layout.h"
13 12
14/** 13/**
@@ -69,84 +68,6 @@ public:
69 void TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y); 68 void TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y);
70 69
71 /** 70 /**
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).
94 * @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.
96 * @return std::tuple of (x, y, pressed) where `x` and `y` are the touch coordinates and
97 * `pressed` is true if the touch screen is currently being pressed
98 */
99 std::tuple<u16, u16, bool> GetTouchState() const {
100 return std::make_tuple(touch_x, touch_y, touch_pressed);
101 }
102
103 /**
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. 71 * Returns currently active configuration.
151 * @note Accesses to the returned object need not be consistent because it may be modified in 72 * @note Accesses to the returned object need not be consistent because it may be modified in
152 * another thread 73 * another thread
@@ -180,21 +101,8 @@ public:
180 void UpdateCurrentFramebufferLayout(unsigned width, unsigned height); 101 void UpdateCurrentFramebufferLayout(unsigned width, unsigned height);
181 102
182protected: 103protected:
183 EmuWindow() { 104 EmuWindow();
184 // TODO: Find a better place to set this. 105 virtual ~EmuWindow();
185 config.min_client_area_size = std::make_pair(400u, 480u);
186 active_config = config;
187 touch_x = 0;
188 touch_y = 0;
189 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 }
197 virtual ~EmuWindow() {}
198 106
199 /** 107 /**
200 * Processes any pending configuration changes from the last SetConfig call. 108 * Processes any pending configuration changes from the last SetConfig call.
@@ -250,20 +158,8 @@ private:
250 /// ProcessConfigurationChanges) 158 /// ProcessConfigurationChanges)
251 WindowConfig active_config; ///< Internal active configuration 159 WindowConfig active_config; ///< Internal active configuration
252 160
253 bool touch_pressed; ///< True if touchpad area is currently pressed, otherwise false 161 class TouchState;
254 162 std::shared_ptr<TouchState> touch_state;
255 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)
257
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 163
268 /** 164 /**
269 * Clip the provided coordinates to be inside the touchscreen area. 165 * Clip the provided coordinates to be inside the touchscreen area.
diff --git a/src/core/frontend/framebuffer_layout.cpp b/src/core/frontend/framebuffer_layout.cpp
index d2d02f9ff..e9f778fcb 100644
--- a/src/core/frontend/framebuffer_layout.cpp
+++ b/src/core/frontend/framebuffer_layout.cpp
@@ -141,6 +141,40 @@ FramebufferLayout LargeFrameLayout(unsigned width, unsigned height, bool swapped
141 return res; 141 return res;
142} 142}
143 143
144FramebufferLayout SideFrameLayout(unsigned width, unsigned height, bool swapped) {
145 ASSERT(width > 0);
146 ASSERT(height > 0);
147
148 FramebufferLayout res{width, height, true, true, {}, {}};
149 // Aspect ratio of both screens side by side
150 const float emulation_aspect_ratio = static_cast<float>(Core::kScreenTopHeight) /
151 (Core::kScreenTopWidth + Core::kScreenBottomWidth);
152 float window_aspect_ratio = static_cast<float>(height) / width;
153 MathUtil::Rectangle<unsigned> screen_window_area{0, 0, width, height};
154 // Find largest Rectangle that can fit in the window size with the given aspect ratio
155 MathUtil::Rectangle<unsigned> screen_rect =
156 maxRectangle(screen_window_area, emulation_aspect_ratio);
157 // Find sizes of top and bottom screen
158 MathUtil::Rectangle<unsigned> top_screen = maxRectangle(screen_rect, TOP_SCREEN_ASPECT_RATIO);
159 MathUtil::Rectangle<unsigned> bot_screen = maxRectangle(screen_rect, BOT_SCREEN_ASPECT_RATIO);
160
161 if (window_aspect_ratio < emulation_aspect_ratio) {
162 // Apply borders to the left and right sides of the window.
163 u32 shift_horizontal = (screen_window_area.GetWidth() - screen_rect.GetWidth()) / 2;
164 top_screen = top_screen.TranslateX(shift_horizontal);
165 bot_screen = bot_screen.TranslateX(shift_horizontal);
166 } else {
167 // Window is narrower than the emulation content => apply borders to the top and bottom
168 u32 shift_vertical = (screen_window_area.GetHeight() - screen_rect.GetHeight()) / 2;
169 top_screen = top_screen.TranslateY(shift_vertical);
170 bot_screen = bot_screen.TranslateY(shift_vertical);
171 }
172 // Move the top screen to the right if we are swapped.
173 res.top_screen = swapped ? top_screen.TranslateX(bot_screen.GetWidth()) : top_screen;
174 res.bottom_screen = swapped ? bot_screen : bot_screen.TranslateX(top_screen.GetWidth());
175 return res;
176}
177
144FramebufferLayout CustomFrameLayout(unsigned width, unsigned height) { 178FramebufferLayout CustomFrameLayout(unsigned width, unsigned height) {
145 ASSERT(width > 0); 179 ASSERT(width > 0);
146 ASSERT(height > 0); 180 ASSERT(height > 0);
@@ -158,4 +192,4 @@ FramebufferLayout CustomFrameLayout(unsigned width, unsigned height) {
158 res.bottom_screen = bot_screen; 192 res.bottom_screen = bot_screen;
159 return res; 193 return res;
160} 194}
161} 195} // namespace Layout
diff --git a/src/core/frontend/framebuffer_layout.h b/src/core/frontend/framebuffer_layout.h
index 9a7738969..4983cf103 100644
--- a/src/core/frontend/framebuffer_layout.h
+++ b/src/core/frontend/framebuffer_layout.h
@@ -54,6 +54,17 @@ FramebufferLayout SingleFrameLayout(unsigned width, unsigned height, bool is_swa
54FramebufferLayout LargeFrameLayout(unsigned width, unsigned height, bool is_swapped); 54FramebufferLayout LargeFrameLayout(unsigned width, unsigned height, bool is_swapped);
55 55
56/** 56/**
57* Factory method for constructing a Frame with the Top screen and bottom
58* screen side by side
59* This is useful for devices with small screens, like the GPDWin
60* @param width Window framebuffer width in pixels
61* @param height Window framebuffer height in pixels
62* @param is_swapped if true, the bottom screen will be the left display
63* @return Newly created FramebufferLayout object with default screen regions initialized
64*/
65FramebufferLayout SideFrameLayout(unsigned width, unsigned height, bool is_swapped);
66
67/**
57 * Factory method for constructing a custom FramebufferLayout 68 * Factory method for constructing a custom FramebufferLayout
58 * @param width Window framebuffer width in pixels 69 * @param width Window framebuffer width in pixels
59 * @param height Window framebuffer height in pixels 70 * @param height Window framebuffer height in pixels
diff --git a/src/core/frontend/input.h b/src/core/frontend/input.h
index 0a5713dc0..8c256beb5 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,28 @@ 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
129/**
130 * A touch device is an input device that returns a tuple of two floats and a bool. The floats are
131 * x and y coordinates in the range 0.0 - 1.0, and the bool indicates whether it is pressed.
132 */
133using TouchDevice = InputDevice<std::tuple<float, float, bool>>;
134
110} // namespace Input 135} // 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