summaryrefslogtreecommitdiffstats
path: root/src/core/frontend
diff options
context:
space:
mode:
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 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include <algorithm>
#include <cmath>
-#include "common/assert.h"
-#include "core/3ds.h"
-#include "core/core.h"
+#include <mutex>
#include "core/frontend/emu_window.h"
+#include "core/frontend/input.h"
#include "core/settings.h"
+class EmuWindow::TouchState : public Input::Factory<Input::TouchDevice>,
+ public std::enable_shared_from_this<TouchState> {
+public:
+ std::unique_ptr<Input::TouchDevice> Create(const Common::ParamPackage&) override {
+ return std::make_unique<Device>(shared_from_this());
+ }
+
+ std::mutex mutex;
+
+ bool touch_pressed = false; ///< True if touchpad area is currently pressed, otherwise false
+
+ float touch_x = 0.0f; ///< Touchpad X-position
+ float touch_y = 0.0f; ///< Touchpad Y-position
+
+private:
+ class Device : public Input::TouchDevice {
+ public:
+ explicit Device(std::weak_ptr<TouchState>&& touch_state) : touch_state(touch_state) {}
+ std::tuple<float, float, bool> GetStatus() const override {
+ if (auto state = touch_state.lock()) {
+ std::lock_guard<std::mutex> guard(state->mutex);
+ return std::make_tuple(state->touch_x, state->touch_y, state->touch_pressed);
+ }
+ return std::make_tuple(0.0f, 0.0f, false);
+ }
+
+ private:
+ std::weak_ptr<TouchState> touch_state;
+ };
+};
+
+EmuWindow::EmuWindow() {
+ // TODO: Find a better place to set this.
+ config.min_client_area_size = std::make_pair(400u, 480u);
+ active_config = config;
+ touch_state = std::make_shared<TouchState>();
+ Input::RegisterFactory<Input::TouchDevice>("emu_window", touch_state);
+}
+
+EmuWindow::~EmuWindow() {
+ Input::UnregisterFactory<Input::TouchDevice>("emu_window");
+}
+
/**
* Check if the given x/y coordinates are within the touchpad specified by the framebuffer layout
* @param layout FramebufferLayout object describing the framebuffer size and screen positions
@@ -38,22 +79,26 @@ void EmuWindow::TouchPressed(unsigned framebuffer_x, unsigned framebuffer_y) {
if (!IsWithinTouchscreen(framebuffer_layout, framebuffer_x, framebuffer_y))
return;
- touch_x = Core::kScreenBottomWidth * (framebuffer_x - framebuffer_layout.bottom_screen.left) /
- (framebuffer_layout.bottom_screen.right - framebuffer_layout.bottom_screen.left);
- touch_y = Core::kScreenBottomHeight * (framebuffer_y - framebuffer_layout.bottom_screen.top) /
- (framebuffer_layout.bottom_screen.bottom - framebuffer_layout.bottom_screen.top);
+ std::lock_guard<std::mutex> guard(touch_state->mutex);
+ touch_state->touch_x =
+ static_cast<float>(framebuffer_x - framebuffer_layout.bottom_screen.left) /
+ (framebuffer_layout.bottom_screen.right - framebuffer_layout.bottom_screen.left);
+ touch_state->touch_y =
+ static_cast<float>(framebuffer_y - framebuffer_layout.bottom_screen.top) /
+ (framebuffer_layout.bottom_screen.bottom - framebuffer_layout.bottom_screen.top);
- touch_pressed = true;
+ touch_state->touch_pressed = true;
}
void EmuWindow::TouchReleased() {
- touch_pressed = false;
- touch_x = 0;
- touch_y = 0;
+ std::lock_guard<std::mutex> guard(touch_state->mutex);
+ touch_state->touch_pressed = false;
+ touch_state->touch_x = 0;
+ touch_state->touch_y = 0;
}
void EmuWindow::TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y) {
- if (!touch_pressed)
+ if (!touch_state->touch_pressed)
return;
if (!IsWithinTouchscreen(framebuffer_layout, framebuffer_x, framebuffer_y))
@@ -62,29 +107,6 @@ void EmuWindow::TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y) {
TouchPressed(framebuffer_x, framebuffer_y);
}
-void EmuWindow::AccelerometerChanged(float x, float y, float z) {
- constexpr float coef = 512;
-
- std::lock_guard<std::mutex> lock(accel_mutex);
-
- // TODO(wwylele): do a time stretch as it in GyroscopeChanged
- // The time stretch formula should be like
- // stretched_vector = (raw_vector - gravity) * stretch_ratio + gravity
- accel_x = static_cast<s16>(x * coef);
- accel_y = static_cast<s16>(y * coef);
- accel_z = static_cast<s16>(z * coef);
-}
-
-void EmuWindow::GyroscopeChanged(float x, float y, float z) {
- constexpr float FULL_FPS = 60;
- float coef = GetGyroscopeRawToDpsCoefficient();
- float stretch = Core::System::GetInstance().perf_stats.GetLastFrameTimeScale();
- std::lock_guard<std::mutex> lock(gyro_mutex);
- gyro_x = static_cast<s16>(x * coef * stretch);
- gyro_y = static_cast<s16>(y * coef * stretch);
- gyro_z = static_cast<s16>(z * coef * stretch);
-}
-
void EmuWindow::UpdateCurrentFramebufferLayout(unsigned width, unsigned height) {
Layout::FramebufferLayout layout;
if (Settings::values.custom_layout == true) {
@@ -97,6 +119,9 @@ void EmuWindow::UpdateCurrentFramebufferLayout(unsigned width, unsigned height)
case Settings::LayoutOption::LargeScreen:
layout = Layout::LargeFrameLayout(width, height, Settings::values.swap_screen);
break;
+ case Settings::LayoutOption::SideScreen:
+ layout = Layout::SideFrameLayout(width, height, Settings::values.swap_screen);
+ break;
case Settings::LayoutOption::Default:
default:
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 @@
#pragma once
-#include <mutex>
+#include <memory>
#include <tuple>
#include <utility>
#include "common/common_types.h"
-#include "common/math_util.h"
#include "core/frontend/framebuffer_layout.h"
/**
@@ -69,84 +68,6 @@ public:
void TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y);
/**
- * Signal accelerometer state has changed.
- * @param x X-axis accelerometer value
- * @param y Y-axis accelerometer value
- * @param z Z-axis accelerometer value
- * @note all values are in unit of g (gravitational acceleration).
- * e.g. x = 1.0 means 9.8m/s^2 in x direction.
- * @see GetAccelerometerState for axis explanation.
- */
- void AccelerometerChanged(float x, float y, float z);
-
- /**
- * Signal gyroscope state has changed.
- * @param x X-axis accelerometer value
- * @param y Y-axis accelerometer value
- * @param z Z-axis accelerometer value
- * @note all values are in deg/sec.
- * @see GetGyroscopeState for axis explanation.
- */
- void GyroscopeChanged(float x, float y, float z);
-
- /**
- * Gets the current touch screen state (touch X/Y coordinates and whether or not it is pressed).
- * @note This should be called by the core emu thread to get a state set by the window thread.
- * @todo Fix this function to be thread-safe.
- * @return std::tuple of (x, y, pressed) where `x` and `y` are the touch coordinates and
- * `pressed` is true if the touch screen is currently being pressed
- */
- std::tuple<u16, u16, bool> GetTouchState() const {
- return std::make_tuple(touch_x, touch_y, touch_pressed);
- }
-
- /**
- * Gets the current accelerometer state (acceleration along each three axis).
- * Axis explained:
- * +x is the same direction as LEFT on D-pad.
- * +y is normal to the touch screen, pointing outward.
- * +z is the same direction as UP on D-pad.
- * Units:
- * 1 unit of return value = 1/512 g (measured by hw test),
- * where g is the gravitational acceleration (9.8 m/sec2).
- * @note This should be called by the core emu thread to get a state set by the window thread.
- * @return std::tuple of (x, y, z)
- */
- std::tuple<s16, s16, s16> GetAccelerometerState() {
- std::lock_guard<std::mutex> lock(accel_mutex);
- return std::make_tuple(accel_x, accel_y, accel_z);
- }
-
- /**
- * Gets the current gyroscope state (angular rates about each three axis).
- * Axis explained:
- * +x is the same direction as LEFT on D-pad.
- * +y is normal to the touch screen, pointing outward.
- * +z is the same direction as UP on D-pad.
- * Orientation is determined by right-hand rule.
- * Units:
- * 1 unit of return value = (1/coef) deg/sec,
- * where coef is the return value of GetGyroscopeRawToDpsCoefficient().
- * @note This should be called by the core emu thread to get a state set by the window thread.
- * @return std::tuple of (x, y, z)
- */
- std::tuple<s16, s16, s16> GetGyroscopeState() {
- std::lock_guard<std::mutex> lock(gyro_mutex);
- return std::make_tuple(gyro_x, gyro_y, gyro_z);
- }
-
- /**
- * Gets the coefficient for units conversion of gyroscope state.
- * The conversion formula is r = coefficient * v,
- * where v is angular rate in deg/sec,
- * and r is the gyroscope state.
- * @return float-type coefficient
- */
- f32 GetGyroscopeRawToDpsCoefficient() const {
- return 14.375f; // taken from hw test, and gyroscope's document
- }
-
- /**
* Returns currently active configuration.
* @note Accesses to the returned object need not be consistent because it may be modified in
* another thread
@@ -180,21 +101,8 @@ public:
void UpdateCurrentFramebufferLayout(unsigned width, unsigned height);
protected:
- EmuWindow() {
- // TODO: Find a better place to set this.
- config.min_client_area_size = std::make_pair(400u, 480u);
- active_config = config;
- touch_x = 0;
- touch_y = 0;
- touch_pressed = false;
- accel_x = 0;
- accel_y = -512;
- accel_z = 0;
- gyro_x = 0;
- gyro_y = 0;
- gyro_z = 0;
- }
- virtual ~EmuWindow() {}
+ EmuWindow();
+ virtual ~EmuWindow();
/**
* Processes any pending configuration changes from the last SetConfig call.
@@ -250,20 +158,8 @@ private:
/// ProcessConfigurationChanges)
WindowConfig active_config; ///< Internal active configuration
- bool touch_pressed; ///< True if touchpad area is currently pressed, otherwise false
-
- u16 touch_x; ///< Touchpad X-position in native 3DS pixel coordinates (0-320)
- u16 touch_y; ///< Touchpad Y-position in native 3DS pixel coordinates (0-240)
-
- std::mutex accel_mutex;
- s16 accel_x; ///< Accelerometer X-axis value in native 3DS units
- s16 accel_y; ///< Accelerometer Y-axis value in native 3DS units
- s16 accel_z; ///< Accelerometer Z-axis value in native 3DS units
-
- std::mutex gyro_mutex;
- s16 gyro_x; ///< Gyroscope X-axis value in native 3DS units
- s16 gyro_y; ///< Gyroscope Y-axis value in native 3DS units
- s16 gyro_z; ///< Gyroscope Z-axis value in native 3DS units
+ class TouchState;
+ std::shared_ptr<TouchState> touch_state;
/**
* 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
return res;
}
+FramebufferLayout SideFrameLayout(unsigned width, unsigned height, bool swapped) {
+ ASSERT(width > 0);
+ ASSERT(height > 0);
+
+ FramebufferLayout res{width, height, true, true, {}, {}};
+ // Aspect ratio of both screens side by side
+ const float emulation_aspect_ratio = static_cast<float>(Core::kScreenTopHeight) /
+ (Core::kScreenTopWidth + Core::kScreenBottomWidth);
+ float window_aspect_ratio = static_cast<float>(height) / width;
+ MathUtil::Rectangle<unsigned> screen_window_area{0, 0, width, height};
+ // Find largest Rectangle that can fit in the window size with the given aspect ratio
+ MathUtil::Rectangle<unsigned> screen_rect =
+ maxRectangle(screen_window_area, emulation_aspect_ratio);
+ // Find sizes of top and bottom screen
+ MathUtil::Rectangle<unsigned> top_screen = maxRectangle(screen_rect, TOP_SCREEN_ASPECT_RATIO);
+ MathUtil::Rectangle<unsigned> bot_screen = maxRectangle(screen_rect, BOT_SCREEN_ASPECT_RATIO);
+
+ if (window_aspect_ratio < emulation_aspect_ratio) {
+ // Apply borders to the left and right sides of the window.
+ u32 shift_horizontal = (screen_window_area.GetWidth() - screen_rect.GetWidth()) / 2;
+ top_screen = top_screen.TranslateX(shift_horizontal);
+ bot_screen = bot_screen.TranslateX(shift_horizontal);
+ } else {
+ // Window is narrower than the emulation content => apply borders to the top and bottom
+ u32 shift_vertical = (screen_window_area.GetHeight() - screen_rect.GetHeight()) / 2;
+ top_screen = top_screen.TranslateY(shift_vertical);
+ bot_screen = bot_screen.TranslateY(shift_vertical);
+ }
+ // Move the top screen to the right if we are swapped.
+ res.top_screen = swapped ? top_screen.TranslateX(bot_screen.GetWidth()) : top_screen;
+ res.bottom_screen = swapped ? bot_screen : bot_screen.TranslateX(top_screen.GetWidth());
+ return res;
+}
+
FramebufferLayout CustomFrameLayout(unsigned width, unsigned height) {
ASSERT(width > 0);
ASSERT(height > 0);
@@ -158,4 +192,4 @@ FramebufferLayout CustomFrameLayout(unsigned width, unsigned height) {
res.bottom_screen = bot_screen;
return res;
}
-}
+} // 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
FramebufferLayout LargeFrameLayout(unsigned width, unsigned height, bool is_swapped);
/**
+* Factory method for constructing a Frame with the Top screen and bottom
+* screen side by side
+* This is useful for devices with small screens, like the GPDWin
+* @param width Window framebuffer width in pixels
+* @param height Window framebuffer height in pixels
+* @param is_swapped if true, the bottom screen will be the left display
+* @return Newly created FramebufferLayout object with default screen regions initialized
+*/
+FramebufferLayout SideFrameLayout(unsigned width, unsigned height, bool is_swapped);
+
+/**
* Factory method for constructing a custom FramebufferLayout
* @param width Window framebuffer width in pixels
* @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 @@
#include <utility>
#include "common/logging/log.h"
#include "common/param_package.h"
+#include "common/vector_math.h"
namespace Input {
@@ -107,4 +108,28 @@ using ButtonDevice = InputDevice<bool>;
*/
using AnalogDevice = InputDevice<std::tuple<float, float>>;
+/**
+ * A motion device is an input device that returns a tuple of accelerometer state vector and
+ * gyroscope state vector.
+ *
+ * For both vectors:
+ * x+ is the same direction as LEFT on D-pad.
+ * y+ is normal to the touch screen, pointing outward.
+ * z+ is the same direction as UP on D-pad.
+ *
+ * For accelerometer state vector
+ * Units: g (gravitational acceleration)
+ *
+ * For gyroscope state vector:
+ * Orientation is determined by right-hand rule.
+ * Units: deg/sec
+ */
+using MotionDevice = InputDevice<std::tuple<Math::Vec3<float>, Math::Vec3<float>>>;
+
+/**
+ * A touch device is an input device that returns a tuple of two floats and a bool. The floats are
+ * x and y coordinates in the range 0.0 - 1.0, and the bool indicates whether it is pressed.
+ */
+using TouchDevice = InputDevice<std::tuple<float, float, bool>>;
+
} // 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 @@
-// Copyright 2016 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#include "common/math_util.h"
-#include "common/quaternion.h"
-#include "core/frontend/emu_window.h"
-#include "core/frontend/motion_emu.h"
-
-namespace Motion {
-
-static constexpr int update_millisecond = 100;
-static constexpr auto update_duration =
- std::chrono::duration_cast<std::chrono::steady_clock::duration>(
- std::chrono::milliseconds(update_millisecond));
-
-MotionEmu::MotionEmu(EmuWindow& emu_window)
- : motion_emu_thread(&MotionEmu::MotionEmuThread, this, std::ref(emu_window)) {}
-
-MotionEmu::~MotionEmu() {
- if (motion_emu_thread.joinable()) {
- shutdown_event.Set();
- motion_emu_thread.join();
- }
-}
-
-void MotionEmu::MotionEmuThread(EmuWindow& emu_window) {
- auto update_time = std::chrono::steady_clock::now();
- Math::Quaternion<float> q = MakeQuaternion(Math::Vec3<float>(), 0);
- Math::Quaternion<float> old_q;
-
- while (!shutdown_event.WaitUntil(update_time)) {
- update_time += update_duration;
- old_q = q;
-
- {
- std::lock_guard<std::mutex> guard(tilt_mutex);
-
- // Find the quaternion describing current 3DS tilting
- q = MakeQuaternion(Math::MakeVec(-tilt_direction.y, 0.0f, tilt_direction.x),
- tilt_angle);
- }
-
- auto inv_q = q.Inverse();
-
- // Set the gravity vector in world space
- auto gravity = Math::MakeVec(0.0f, -1.0f, 0.0f);
-
- // Find the angular rate vector in world space
- auto angular_rate = ((q - old_q) * inv_q).xyz * 2;
- angular_rate *= 1000 / update_millisecond / MathUtil::PI * 180;
-
- // Transform the two vectors from world space to 3DS space
- gravity = QuaternionRotate(inv_q, gravity);
- angular_rate = QuaternionRotate(inv_q, angular_rate);
-
- // Update the sensor state
- emu_window.AccelerometerChanged(gravity.x, gravity.y, gravity.z);
- emu_window.GyroscopeChanged(angular_rate.x, angular_rate.y, angular_rate.z);
- }
-}
-
-void MotionEmu::BeginTilt(int x, int y) {
- mouse_origin = Math::MakeVec(x, y);
- is_tilting = true;
-}
-
-void MotionEmu::Tilt(int x, int y) {
- constexpr float SENSITIVITY = 0.01f;
- auto mouse_move = Math::MakeVec(x, y) - mouse_origin;
- if (is_tilting) {
- std::lock_guard<std::mutex> guard(tilt_mutex);
- if (mouse_move.x == 0 && mouse_move.y == 0) {
- tilt_angle = 0;
- } else {
- tilt_direction = mouse_move.Cast<float>();
- tilt_angle = MathUtil::Clamp(tilt_direction.Normalize() * SENSITIVITY, 0.0f,
- MathUtil::PI * 0.5f);
- }
- }
-}
-
-void MotionEmu::EndTilt() {
- std::lock_guard<std::mutex> guard(tilt_mutex);
- tilt_angle = 0;
- is_tilting = false;
-}
-
-} // 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 @@
-// Copyright 2016 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#pragma once
-#include "common/thread.h"
-#include "common/vector_math.h"
-
-class EmuWindow;
-
-namespace Motion {
-
-class MotionEmu final {
-public:
- MotionEmu(EmuWindow& emu_window);
- ~MotionEmu();
-
- /**
- * Signals that a motion sensor tilt has begun.
- * @param x the x-coordinate of the cursor
- * @param y the y-coordinate of the cursor
- */
- void BeginTilt(int x, int y);
-
- /**
- * Signals that a motion sensor tilt is occurring.
- * @param x the x-coordinate of the cursor
- * @param y the y-coordinate of the cursor
- */
- void Tilt(int x, int y);
-
- /**
- * Signals that a motion sensor tilt has ended.
- */
- void EndTilt();
-
-private:
- Math::Vec2<int> mouse_origin;
-
- std::mutex tilt_mutex;
- Math::Vec2<float> tilt_direction;
- float tilt_angle = 0;
-
- bool is_tilting = false;
-
- Common::Event shutdown_event;
- std::thread motion_emu_thread;
-
- void MotionEmuThread(EmuWindow& emu_window);
-};
-
-} // namespace Motion