From 867eabd6b7effc8ba6c21d7fcbd0480b14f81ad0 Mon Sep 17 00:00:00 2001 From: wwylele Date: Sun, 6 Aug 2017 22:54:19 +0300 Subject: HID: use MotionDevice for Accelerometer and Gyroscope --- src/core/frontend/input.h | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) (limited to 'src/core/frontend') diff --git a/src/core/frontend/input.h b/src/core/frontend/input.h index 0a5713dc0..a8be49440 100644 --- a/src/core/frontend/input.h +++ b/src/core/frontend/input.h @@ -11,6 +11,7 @@ #include #include "common/logging/log.h" #include "common/param_package.h" +#include "common/vector_math.h" namespace Input { @@ -107,4 +108,23 @@ using ButtonDevice = InputDevice; */ using AnalogDevice = InputDevice>; +/** + * A motion device is an input device that returns a tuple of accelerometer state vector and + * gyroscope state vector. + * + * For accelerometer state vector: + * 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: measured in unit of gravitational acceleration + * + * For gyroscope state vector: + * 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: deg/sec + */ +using MotionDevice = InputDevice, Math::Vec3>>; + } // namespace Input -- cgit v1.2.3 From 188194908c2785bd1e03485941b9148777cdddd7 Mon Sep 17 00:00:00 2001 From: wwylele Date: Mon, 7 Aug 2017 00:04:06 +0300 Subject: move MotionEmu from core/frontend to input_common as a InputDevice --- src/citra/config.cpp | 3 + src/citra/default_ini.h | 8 +- src/citra/emu_window/emu_window_sdl2.cpp | 10 +- src/citra/emu_window/emu_window_sdl2.h | 4 - src/citra_qt/bootmanager.cpp | 10 +- src/citra_qt/bootmanager.h | 4 - src/citra_qt/configuration/config.cpp | 6 ++ src/core/CMakeLists.txt | 2 - src/core/frontend/emu_window.cpp | 23 ----- src/core/frontend/emu_window.h | 83 ---------------- src/core/frontend/input.h | 9 +- src/core/frontend/motion_emu.cpp | 89 ----------------- src/core/frontend/motion_emu.h | 52 ---------- src/input_common/CMakeLists.txt | 2 + src/input_common/main.cpp | 15 ++- src/input_common/main.h | 5 + src/input_common/motion_emu.cpp | 159 +++++++++++++++++++++++++++++++ src/input_common/motion_emu.h | 47 +++++++++ 18 files changed, 254 insertions(+), 277 deletions(-) delete mode 100644 src/core/frontend/motion_emu.cpp delete mode 100644 src/core/frontend/motion_emu.h create mode 100644 src/input_common/motion_emu.cpp create mode 100644 src/input_common/motion_emu.h (limited to 'src/core/frontend') 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() { Settings::values.analogs[i] = default_param; } + Settings::values.motion_device = sdl2_config->Get( + "Controls", "motion_device", "engine:motion_emu,update_period:100,sensitivity:0.01"); + // Core Settings::values.use_cpu_jit = sdl2_config->GetBoolean("Core", "use_cpu_jit", true); diff --git a/src/citra/default_ini.h b/src/citra/default_ini.h index b0a0ebd3b..9ea779dd8 100644 --- a/src/citra/default_ini.h +++ b/src/citra/default_ini.h @@ -43,7 +43,7 @@ button_zr= button_home= # for analog input, the following devices are available: -# - "analog_from_button" (default) for emulating analog input from direction buttons. Required parameters: +# - "analog_from_button" (default) for emulating analog input from direction buttons. Required parameters: # - "up", "down", "left", "right": sub-devices for each direction. # Should be in the format as a button input devices using escape characters, for example, "engine$0keyboard$1code$00" # - "modifier": sub-devices as a modifier. @@ -56,6 +56,12 @@ button_home= circle_pad= c_stick= +# for motion input, the following devices are available: +# - "motion_emu" (default) for emulating motion input from mouse input. Required parameters: +# - "update_period": update period in milliseconds (default to 100) +# - "sensitivity": the coefficient converting mouse movement to tilting angle (default to 0.01) +motion_device= + [Core] # Whether to use the Just-In-Time (JIT) compiler for CPU emulation # 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 @@ #include "core/settings.h" #include "input_common/keyboard.h" #include "input_common/main.h" +#include "input_common/motion_emu.h" #include "network/network.h" void EmuWindow_SDL2::OnMouseMotion(s32 x, s32 y) { TouchMoved((unsigned)std::max(x, 0), (unsigned)std::max(y, 0)); - motion_emu->Tilt(x, y); + InputCommon::GetMotionEmu()->Tilt(x, y); } void 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) { } } else if (button == SDL_BUTTON_RIGHT) { if (state == SDL_PRESSED) { - motion_emu->BeginTilt(x, y); + InputCommon::GetMotionEmu()->BeginTilt(x, y); } else { - motion_emu->EndTilt(); + InputCommon::GetMotionEmu()->EndTilt(); } } } @@ -61,8 +62,6 @@ EmuWindow_SDL2::EmuWindow_SDL2() { InputCommon::Init(); Network::Init(); - motion_emu = std::make_unique(*this); - SDL_SetMainReady(); // Initialize the window @@ -117,7 +116,6 @@ EmuWindow_SDL2::EmuWindow_SDL2() { EmuWindow_SDL2::~EmuWindow_SDL2() { SDL_GL_DeleteContext(gl_context); SDL_Quit(); - motion_emu = nullptr; Network::Shutdown(); 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 @@ #include #include #include "core/frontend/emu_window.h" -#include "core/frontend/motion_emu.h" struct SDL_Window; @@ -57,7 +56,4 @@ private: using SDL_GLContext = void*; /// The OpenGL context associated with the window SDL_GLContext gl_context; - - /// Motion sensors emulation - std::unique_ptr motion_emu; }; 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 @@ #include "core/settings.h" #include "input_common/keyboard.h" #include "input_common/main.h" +#include "input_common/motion_emu.h" #include "network/network.h" EmuThread::EmuThread(GRenderWindow* render_window) @@ -201,7 +202,6 @@ qreal GRenderWindow::windowPixelRatio() { } void GRenderWindow::closeEvent(QCloseEvent* event) { - motion_emu = nullptr; emit Closed(); QWidget::closeEvent(event); } @@ -221,7 +221,7 @@ void GRenderWindow::mousePressEvent(QMouseEvent* event) { this->TouchPressed(static_cast(pos.x() * pixelRatio), static_cast(pos.y() * pixelRatio)); } else if (event->button() == Qt::RightButton) { - motion_emu->BeginTilt(pos.x(), pos.y()); + InputCommon::GetMotionEmu()->BeginTilt(pos.x(), pos.y()); } } @@ -230,14 +230,14 @@ void GRenderWindow::mouseMoveEvent(QMouseEvent* event) { qreal pixelRatio = windowPixelRatio(); this->TouchMoved(std::max(static_cast(pos.x() * pixelRatio), 0u), std::max(static_cast(pos.y() * pixelRatio), 0u)); - motion_emu->Tilt(pos.x(), pos.y()); + InputCommon::GetMotionEmu()->Tilt(pos.x(), pos.y()); } void GRenderWindow::mouseReleaseEvent(QMouseEvent* event) { if (event->button() == Qt::LeftButton) this->TouchReleased(); else if (event->button() == Qt::RightButton) - motion_emu->EndTilt(); + InputCommon::GetMotionEmu()->EndTilt(); } void GRenderWindow::focusOutEvent(QFocusEvent* event) { @@ -290,13 +290,11 @@ void GRenderWindow::OnMinimalClientAreaChangeRequest( } void GRenderWindow::OnEmulationStarting(EmuThread* emu_thread) { - motion_emu = std::make_unique(*this); this->emu_thread = emu_thread; child->DisablePainting(); } void GRenderWindow::OnEmulationStopping() { - motion_emu = nullptr; emu_thread = nullptr; child->EnablePainting(); } 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 @@ #include "common/thread.h" #include "core/core.h" #include "core/frontend/emu_window.h" -#include "core/frontend/motion_emu.h" class QKeyEvent; class QScreen; @@ -158,9 +157,6 @@ private: EmuThread* emu_thread; - /// Motion sensors emulation - std::unique_ptr motion_emu; - protected: void showEvent(QShowEvent* event) override; }; 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() { Settings::values.analogs[i] = default_param; } + Settings::values.motion_device = + qt_config->value("motion_device", "engine:motion_emu,update_period:100,sensitivity:0.01") + .toString() + .toStdString(); + qt_config->endGroup(); qt_config->beginGroup("Core"); @@ -203,6 +208,7 @@ void Config::SaveValues() { qt_config->setValue(QString::fromStdString(Settings::NativeAnalog::mapping[i]), QString::fromStdString(Settings::values.analogs[i])); } + qt_config->setValue("motion_device", QString::fromStdString(Settings::values.motion_device)); qt_config->endGroup(); qt_config->beginGroup("Core"); diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 360f407f3..ea36b7d95 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -33,7 +33,6 @@ set(SRCS frontend/camera/interface.cpp frontend/emu_window.cpp frontend/framebuffer_layout.cpp - frontend/motion_emu.cpp gdbstub/gdbstub.cpp hle/config_mem.cpp hle/applets/applet.cpp @@ -226,7 +225,6 @@ set(HEADERS frontend/emu_window.h frontend/framebuffer_layout.h frontend/input.h - frontend/motion_emu.h gdbstub/gdbstub.h hle/config_mem.h 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) { TouchPressed(framebuffer_x, framebuffer_y); } -void EmuWindow::AccelerometerChanged(float x, float y, float z) { - constexpr float coef = 512; - - std::lock_guard 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(x * coef); - accel_y = static_cast(y * coef); - accel_z = static_cast(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 lock(gyro_mutex); - gyro_x = static_cast(x * coef * stretch); - gyro_y = static_cast(y * coef * stretch); - gyro_z = static_cast(z * coef * stretch); -} - void EmuWindow::UpdateCurrentFramebufferLayout(unsigned width, unsigned height) { Layout::FramebufferLayout layout; 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 @@ -68,27 +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. @@ -100,52 +79,6 @@ public: 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 GetAccelerometerState() { - std::lock_guard 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 GetGyroscopeState() { - std::lock_guard 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 @@ -187,12 +120,6 @@ protected: 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() {} @@ -255,16 +182,6 @@ private: 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 - /** * Clip the provided coordinates to be inside the touchscreen area. */ diff --git a/src/core/frontend/input.h b/src/core/frontend/input.h index a8be49440..5916a901d 100644 --- a/src/core/frontend/input.h +++ b/src/core/frontend/input.h @@ -112,16 +112,15 @@ using AnalogDevice = InputDevice>; * A motion device is an input device that returns a tuple of accelerometer state vector and * gyroscope state vector. * - * For accelerometer 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. - * Units: measured in unit of gravitational acceleration + * + * For accelerometer state vector + * Units: g (gravitational acceleration) * * For gyroscope state vector: - * 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: deg/sec */ 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::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 q = MakeQuaternion(Math::Vec3(), 0); - Math::Quaternion old_q; - - while (!shutdown_event.WaitUntil(update_time)) { - update_time += update_duration; - old_q = q; - - { - std::lock_guard 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 guard(tilt_mutex); - if (mouse_move.x == 0 && mouse_move.y == 0) { - tilt_angle = 0; - } else { - tilt_direction = mouse_move.Cast(); - tilt_angle = MathUtil::Clamp(tilt_direction.Normalize() * SENSITIVITY, 0.0f, - MathUtil::PI * 0.5f); - } - } -} - -void MotionEmu::EndTilt() { - std::lock_guard 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 mouse_origin; - - std::mutex tilt_mutex; - Math::Vec2 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 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 analog_from_button.cpp keyboard.cpp main.cpp + motion_emu.cpp ) set(HEADERS analog_from_button.h keyboard.h main.h + motion_emu.h ) if(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 @@ #include "input_common/analog_from_button.h" #include "input_common/keyboard.h" #include "input_common/main.h" +#include "input_common/motion_emu.h" #ifdef HAVE_SDL2 #include "input_common/sdl/sdl.h" #endif @@ -14,12 +15,16 @@ namespace InputCommon { static std::shared_ptr keyboard; +static std::shared_ptr motion_emu; void Init() { - keyboard = std::make_shared(); + keyboard = std::make_shared(); Input::RegisterFactory("keyboard", keyboard); Input::RegisterFactory("analog_from_button", - std::make_shared()); + std::make_shared()); + motion_emu = std::make_shared(); + Input::RegisterFactory("motion_emu", motion_emu); + #ifdef HAVE_SDL2 SDL::Init(); #endif @@ -29,6 +34,8 @@ void Shutdown() { Input::UnregisterFactory("keyboard"); keyboard.reset(); Input::UnregisterFactory("analog_from_button"); + Input::UnregisterFactory("motion_emu"); + motion_emu.reset(); #ifdef HAVE_SDL2 SDL::Shutdown(); @@ -39,6 +46,10 @@ Keyboard* GetKeyboard() { return keyboard.get(); } +MotionEmu* GetMotionEmu() { + return motion_emu.get(); +} + std::string GenerateKeyboardParam(int key_code) { Common::ParamPackage param{ {"engine", "keyboard"}, {"code", std::to_string(key_code)}, diff --git a/src/input_common/main.h b/src/input_common/main.h index 140bbd014..74283f7c6 100644 --- a/src/input_common/main.h +++ b/src/input_common/main.h @@ -19,6 +19,11 @@ class Keyboard; /// Gets the keyboard button device factory. Keyboard* GetKeyboard(); +class MotionEmu; + +/// Gets the motion emulation factory. +MotionEmu* GetMotionEmu(); + /// Generates a serialized param package for creating a keyboard button device std::string GenerateKeyboardParam(int key_code); diff --git a/src/input_common/motion_emu.cpp b/src/input_common/motion_emu.cpp new file mode 100644 index 000000000..65e80529d --- /dev/null +++ b/src/input_common/motion_emu.cpp @@ -0,0 +1,159 @@ +// Copyright 2017 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 "input_common/motion_emu.h" + +namespace InputCommon { + +// Implementation class of the motion emulation device +class MotionEmuDevice { +public: + MotionEmuDevice(int update_millisecond, float sensitivity) + : update_millisecond(update_millisecond), + update_duration(std::chrono::duration_cast( + std::chrono::milliseconds(update_millisecond))), + sensitivity(sensitivity), motion_emu_thread(&MotionEmuDevice::MotionEmuThread, this) {} + + ~MotionEmuDevice() { + if (motion_emu_thread.joinable()) { + shutdown_event.Set(); + motion_emu_thread.join(); + } + } + + void BeginTilt(int x, int y) { + mouse_origin = Math::MakeVec(x, y); + is_tilting = true; + } + + void Tilt(int x, int y) { + auto mouse_move = Math::MakeVec(x, y) - mouse_origin; + if (is_tilting) { + std::lock_guard guard(tilt_mutex); + if (mouse_move.x == 0 && mouse_move.y == 0) { + tilt_angle = 0; + } else { + tilt_direction = mouse_move.Cast(); + tilt_angle = MathUtil::Clamp(tilt_direction.Normalize() * sensitivity, 0.0f, + MathUtil::PI * 0.5f); + } + } + } + + void EndTilt() { + std::lock_guard guard(tilt_mutex); + tilt_angle = 0; + is_tilting = false; + } + + std::tuple, Math::Vec3> GetStatus() { + std::lock_guard guard(status_mutex); + return status; + } + +private: + const int update_millisecond; + const std::chrono::steady_clock::duration update_duration; + const float sensitivity; + + Math::Vec2 mouse_origin; + + std::mutex tilt_mutex; + Math::Vec2 tilt_direction; + float tilt_angle = 0; + + bool is_tilting = false; + + Common::Event shutdown_event; + std::thread motion_emu_thread; + + std::tuple, Math::Vec3> status; + std::mutex status_mutex; + + void MotionEmuThread() { + auto update_time = std::chrono::steady_clock::now(); + Math::Quaternion q = MakeQuaternion(Math::Vec3(), 0); + Math::Quaternion old_q; + + while (!shutdown_event.WaitUntil(update_time)) { + update_time += update_duration; + old_q = q; + + { + std::lock_guard 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 + { + std::lock_guard guard(status_mutex); + status = std::make_tuple(gravity, angular_rate); + } + } + } +}; + +// Interface wrapper held by input receiver as a unique_ptr. It holds the implementation class as +// a shared_ptr, which is also observed by the factory class as a weak_ptr. In this way the factory +// can forward all the inputs to the implementation only when it is valid. +class MotionEmuDeviceWrapper : public Input::MotionDevice { +public: + MotionEmuDeviceWrapper(int update_millisecond, float sensitivity) { + device = std::make_shared(update_millisecond, sensitivity); + } + + std::tuple, Math::Vec3> GetStatus() const { + return device->GetStatus(); + } + + std::shared_ptr device; +}; + +std::unique_ptr MotionEmu::Create(const Common::ParamPackage& params) { + int update_period = params.Get("update_period", 100); + float sensitivity = params.Get("sensitivity", 0.01f); + auto device_wrapper = std::make_unique(update_period, sensitivity); + // Previously created device is disconnected here. Having two motion devices for 3DS is not + // expected. + current_device = device_wrapper->device; + return std::move(device_wrapper); +} + +void MotionEmu::BeginTilt(int x, int y) { + if (auto ptr = current_device.lock()) { + ptr->BeginTilt(x, y); + } +} + +void MotionEmu::Tilt(int x, int y) { + if (auto ptr = current_device.lock()) { + ptr->Tilt(x, y); + } +} + +void MotionEmu::EndTilt() { + if (auto ptr = current_device.lock()) { + ptr->EndTilt(); + } +} + +} // namespace InputCommon diff --git a/src/input_common/motion_emu.h b/src/input_common/motion_emu.h new file mode 100644 index 000000000..195fb69d2 --- /dev/null +++ b/src/input_common/motion_emu.h @@ -0,0 +1,47 @@ +// Copyright 2017 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" +#include "core/frontend/input.h" + +namespace InputCommon { + +class MotionEmuDevice; + +class MotionEmu : public Input::Factory { +public: + /** + * Creates a motion device emulated from mouse input + * @param params contains parameters for creating the device: + * - "update_period": update period in milliseconds + * - "sensitivity": the coefficient converting mouse movement to tilting angle + */ + std::unique_ptr Create(const Common::ParamPackage& params) override; + + /** + * 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: + std::weak_ptr current_device; +}; + +} // namespace InputCommon -- cgit v1.2.3 From c84e60b4700778db236d3177714a076c515f9ce7 Mon Sep 17 00:00:00 2001 From: wwylele Date: Tue, 8 Aug 2017 21:34:17 +0300 Subject: HID: use TouchDevice for touch pad --- src/core/frontend/input.h | 6 ++++++ src/core/hle/service/hid/hid.cpp | 12 ++++++++---- src/core/settings.h | 1 + 3 files changed, 15 insertions(+), 4 deletions(-) (limited to 'src/core/frontend') diff --git a/src/core/frontend/input.h b/src/core/frontend/input.h index 5916a901d..8c256beb5 100644 --- a/src/core/frontend/input.h +++ b/src/core/frontend/input.h @@ -126,4 +126,10 @@ using AnalogDevice = InputDevice>; */ using MotionDevice = InputDevice, Math::Vec3>>; +/** + * 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>; + } // namespace Input diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index 31f34a7ae..aa5d821f9 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp @@ -7,9 +7,9 @@ #include #include #include "common/logging/log.h" +#include "core/3ds.h" #include "core/core.h" #include "core/core_timing.h" -#include "core/frontend/emu_window.h" #include "core/frontend/input.h" #include "core/hle/ipc.h" #include "core/hle/kernel/event.h" @@ -19,7 +19,6 @@ #include "core/hle/service/hid/hid_spvr.h" #include "core/hle/service/hid/hid_user.h" #include "core/hle/service/service.h" -#include "video_core/video_core.h" namespace Service { namespace HID { @@ -59,6 +58,7 @@ static std::array, Settings::NativeButton:: buttons; static std::unique_ptr circle_pad; static std::unique_ptr motion_device; +static std::unique_ptr touch_device; DirectionState GetStickDirectionState(s16 circle_pad_x, s16 circle_pad_y) { // 30 degree and 60 degree are angular thresholds for directions @@ -96,6 +96,7 @@ static void LoadInputDevices() { circle_pad = Input::CreateDevice( Settings::values.analogs[Settings::NativeAnalog::CirclePad]); motion_device = Input::CreateDevice(Settings::values.motion_device); + touch_device = Input::CreateDevice(Settings::values.touch_device); } static void UnloadInputDevices() { @@ -104,6 +105,7 @@ static void UnloadInputDevices() { } circle_pad.reset(); motion_device.reset(); + touch_device.reset(); } static void UpdatePadCallback(u64 userdata, int cycles_late) { @@ -172,8 +174,10 @@ static void UpdatePadCallback(u64 userdata, int cycles_late) { // Get the current touch entry TouchDataEntry& touch_entry = mem->touch.entries[mem->touch.index]; bool pressed = false; - - std::tie(touch_entry.x, touch_entry.y, pressed) = VideoCore::g_emu_window->GetTouchState(); + float x, y; + std::tie(x, y, pressed) = touch_device->GetStatus(); + touch_entry.x = static_cast(x * Core::kScreenBottomWidth); + touch_entry.y = static_cast(y * Core::kScreenBottomHeight); touch_entry.valid.Assign(pressed ? 1 : 0); // TODO(bunnei): We're not doing anything with offset 0xA8 + 0x18 of HID SharedMemory, which diff --git a/src/core/settings.h b/src/core/settings.h index 7e15b119b..088d7651a 100644 --- a/src/core/settings.h +++ b/src/core/settings.h @@ -80,6 +80,7 @@ struct Values { std::array buttons; std::array analogs; std::string motion_device; + std::string touch_device; // Core bool use_cpu_jit; -- cgit v1.2.3 From 2617de1fe6f6f1fc846a8e038e1ea77a894554b2 Mon Sep 17 00:00:00 2001 From: wwylele Date: Wed, 9 Aug 2017 02:57:42 +0300 Subject: EmuWindow: refactor touch input into a TouchDevice --- src/citra/config.cpp | 2 + src/citra/default_ini.h | 4 ++ src/citra_qt/configuration/config.cpp | 3 ++ src/core/frontend/emu_window.cpp | 71 ++++++++++++++++++++++++++++------- src/core/frontend/emu_window.h | 31 +++------------ 5 files changed, 72 insertions(+), 39 deletions(-) (limited to 'src/core/frontend') diff --git a/src/citra/config.cpp b/src/citra/config.cpp index 73846ed91..93569fc4c 100644 --- a/src/citra/config.cpp +++ b/src/citra/config.cpp @@ -78,6 +78,8 @@ void Config::ReadValues() { Settings::values.motion_device = sdl2_config->Get( "Controls", "motion_device", "engine:motion_emu,update_period:100,sensitivity:0.01"); + Settings::values.touch_device = + sdl2_config->Get("Controls", "touch_device", "engine:emu_window"); // Core Settings::values.use_cpu_jit = sdl2_config->GetBoolean("Core", "use_cpu_jit", true); diff --git a/src/citra/default_ini.h b/src/citra/default_ini.h index 9ea779dd8..fa9dda651 100644 --- a/src/citra/default_ini.h +++ b/src/citra/default_ini.h @@ -62,6 +62,10 @@ c_stick= # - "sensitivity": the coefficient converting mouse movement to tilting angle (default to 0.01) motion_device= +# for touch input, the following devices are available: +# - "emu_window" (default) for emulating touch input from mouse input to the emulation window. No parameters required +touch_device= + [Core] # Whether to use the Just-In-Time (JIT) compiler for CPU emulation # 0: Interpreter (slow), 1 (default): JIT (fast) diff --git a/src/citra_qt/configuration/config.cpp b/src/citra_qt/configuration/config.cpp index 6e42db007..e398c6f29 100644 --- a/src/citra_qt/configuration/config.cpp +++ b/src/citra_qt/configuration/config.cpp @@ -61,6 +61,8 @@ void Config::ReadValues() { qt_config->value("motion_device", "engine:motion_emu,update_period:100,sensitivity:0.01") .toString() .toStdString(); + Settings::values.touch_device = + qt_config->value("touch_device", "engine:emu_window").toString().toStdString(); qt_config->endGroup(); @@ -209,6 +211,7 @@ void Config::SaveValues() { QString::fromStdString(Settings::values.analogs[i])); } qt_config->setValue("motion_device", QString::fromStdString(Settings::values.motion_device)); + qt_config->setValue("touch_device", QString::fromStdString(Settings::values.touch_device)); qt_config->endGroup(); qt_config->beginGroup("Core"); diff --git a/src/core/frontend/emu_window.cpp b/src/core/frontend/emu_window.cpp index 60b20d4e2..787c517ff 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 #include -#include "common/assert.h" -#include "core/3ds.h" -#include "core/core.h" +#include #include "core/frontend/emu_window.h" +#include "core/frontend/input.h" #include "core/settings.h" +class EmuWindow::TouchState : public Input::Factory, + public std::enable_shared_from_this { +public: + std::unique_ptr Create(const Common::ParamPackage&) override { + return std::make_unique(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&& touch_state) : touch_state(touch_state) {} + std::tuple GetStatus() const override { + if (auto state = touch_state.lock()) { + std::lock_guard 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 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(); + Input::RegisterFactory("emu_window", touch_state); +} + +EmuWindow::~EmuWindow() { + Input::UnregisterFactory("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 guard(touch_state->mutex); + touch_state->touch_x = + static_cast(framebuffer_x - framebuffer_layout.bottom_screen.left) / + (framebuffer_layout.bottom_screen.right - framebuffer_layout.bottom_screen.left); + touch_state->touch_y = + static_cast(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 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)) diff --git a/src/core/frontend/emu_window.h b/src/core/frontend/emu_window.h index 7bdee251c..c10dee51b 100644 --- a/src/core/frontend/emu_window.h +++ b/src/core/frontend/emu_window.h @@ -4,11 +4,10 @@ #pragma once -#include +#include #include #include #include "common/common_types.h" -#include "common/math_util.h" #include "core/frontend/framebuffer_layout.h" /** @@ -68,17 +67,6 @@ public: */ void TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y); - /** - * 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 GetTouchState() const { - return std::make_tuple(touch_x, touch_y, touch_pressed); - } - /** * Returns currently active configuration. * @note Accesses to the returned object need not be consistent because it may be modified in @@ -113,15 +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; - } - virtual ~EmuWindow() {} + EmuWindow(); + virtual ~EmuWindow(); /** * Processes any pending configuration changes from the last SetConfig call. @@ -177,10 +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) + class TouchState; + std::shared_ptr touch_state; /** * Clip the provided coordinates to be inside the touchscreen area. -- cgit v1.2.3 From 3cdf854e44e7ff088fa0cbdcfa2bcc6e41822b2c Mon Sep 17 00:00:00 2001 From: ThaMighty90 <30285364+ThaMighty90@users.noreply.github.com> Date: Fri, 25 Aug 2017 23:53:07 +0200 Subject: SidebySide Layout (#2859) * added a SidebySide Layout * Reworked, so both screen have the same height and cleaned up screen translates. * added the option in the UI, hope this is the right way to do it. formated framebuffer_layout.cpp * delete the x64 files * deleted ui_configure_graphics.h * added Option for the Layout in the xml * got rid of SIDE_BY_SIDE_ASPECT_RATIO because it was useless. pulled translate into variables * changed shift variables to u32 and moved them in their respective branch. remove notr="true" for the Screen layout drop down * reworked intends :). changed function description for SideFrameLayout * some description reworking --- src/citra_qt/configuration/configure_graphics.ui | 11 ++++++-- src/core/frontend/emu_window.cpp | 3 ++ src/core/frontend/framebuffer_layout.cpp | 36 +++++++++++++++++++++++- src/core/frontend/framebuffer_layout.h | 11 ++++++++ src/core/settings.cpp | 2 +- src/core/settings.h | 5 ++-- 6 files changed, 61 insertions(+), 7 deletions(-) (limited to 'src/core/frontend') diff --git a/src/citra_qt/configuration/configure_graphics.ui b/src/citra_qt/configuration/configure_graphics.ui index 228f2a869..b340149d5 100644 --- a/src/citra_qt/configuration/configure_graphics.ui +++ b/src/citra_qt/configuration/configure_graphics.ui @@ -146,17 +146,22 @@ - Default + Default - Single Screen + Single Screen - Large Screen + Large Screen + + + + + Side by Side diff --git a/src/core/frontend/emu_window.cpp b/src/core/frontend/emu_window.cpp index 60b20d4e2..54fa5c7fa 100644 --- a/src/core/frontend/emu_window.cpp +++ b/src/core/frontend/emu_window.cpp @@ -74,6 +74,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/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(Core::kScreenTopHeight) / + (Core::kScreenTopWidth + Core::kScreenBottomWidth); + float window_aspect_ratio = static_cast(height) / width; + MathUtil::Rectangle screen_window_area{0, 0, width, height}; + // Find largest Rectangle that can fit in the window size with the given aspect ratio + MathUtil::Rectangle screen_rect = + maxRectangle(screen_window_area, emulation_aspect_ratio); + // Find sizes of top and bottom screen + MathUtil::Rectangle top_screen = maxRectangle(screen_rect, TOP_SCREEN_ASPECT_RATIO); + MathUtil::Rectangle 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 @@ -53,6 +53,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 diff --git a/src/core/settings.cpp b/src/core/settings.cpp index d4f0429d1..efcf1267d 100644 --- a/src/core/settings.cpp +++ b/src/core/settings.cpp @@ -36,4 +36,4 @@ void Apply() { Service::IR::ReloadInputDevices(); } -} // namespace +} // namespace Settings diff --git a/src/core/settings.h b/src/core/settings.h index 7e15b119b..ca657719a 100644 --- a/src/core/settings.h +++ b/src/core/settings.h @@ -15,6 +15,7 @@ enum class LayoutOption { Default, SingleScreen, LargeScreen, + SideScreen, }; namespace NativeButton { @@ -70,7 +71,7 @@ enum Values { static const std::array mapping = {{ "circle_pad", "c_stick", }}; -} // namespace NumAnalog +} // namespace NativeAnalog struct Values { // CheckNew3DS @@ -137,4 +138,4 @@ struct Values { static constexpr int REGION_VALUE_AUTO_SELECT = -1; void Apply(); -} +} // namespace Settings -- cgit v1.2.3