mirror of
				https://git.zaroz.cloud/nintendo-back-up/yuzu/yuzu-mainline.git
				synced 2025-03-21 01:53:15 +00:00 
			
		
		
		
	move MotionEmu from core/frontend to input_common as a InputDevice
This commit is contained in:
		
							parent
							
								
									867eabd6b7
								
							
						
					
					
						commit
						188194908c
					
				@ -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);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -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)
 | 
			
		||||
 | 
			
		||||
@ -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<Motion::MotionEmu>(*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();
 | 
			
		||||
 | 
			
		||||
@ -7,7 +7,6 @@
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include <utility>
 | 
			
		||||
#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::MotionEmu> motion_emu;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -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<unsigned>(pos.x() * pixelRatio),
 | 
			
		||||
                           static_cast<unsigned>(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<unsigned>(pos.x() * pixelRatio), 0u),
 | 
			
		||||
                     std::max(static_cast<unsigned>(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<Motion::MotionEmu>(*this);
 | 
			
		||||
    this->emu_thread = emu_thread;
 | 
			
		||||
    child->DisablePainting();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GRenderWindow::OnEmulationStopping() {
 | 
			
		||||
    motion_emu = nullptr;
 | 
			
		||||
    emu_thread = nullptr;
 | 
			
		||||
    child->EnablePainting();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -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::MotionEmu> motion_emu;
 | 
			
		||||
 | 
			
		||||
protected:
 | 
			
		||||
    void showEvent(QShowEvent* event) override;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -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");
 | 
			
		||||
 | 
			
		||||
@ -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
 | 
			
		||||
 | 
			
		||||
@ -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<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) {
 | 
			
		||||
 | 
			
		||||
@ -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<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
 | 
			
		||||
@ -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.
 | 
			
		||||
     */
 | 
			
		||||
 | 
			
		||||
@ -112,16 +112,15 @@ 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 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
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
@ -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
 | 
			
		||||
@ -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)
 | 
			
		||||
 | 
			
		||||
@ -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> keyboard;
 | 
			
		||||
static std::shared_ptr<MotionEmu> motion_emu;
 | 
			
		||||
 | 
			
		||||
void Init() {
 | 
			
		||||
    keyboard = std::make_shared<InputCommon::Keyboard>();
 | 
			
		||||
    keyboard = std::make_shared<Keyboard>();
 | 
			
		||||
    Input::RegisterFactory<Input::ButtonDevice>("keyboard", keyboard);
 | 
			
		||||
    Input::RegisterFactory<Input::AnalogDevice>("analog_from_button",
 | 
			
		||||
                                                std::make_shared<InputCommon::AnalogFromButton>());
 | 
			
		||||
                                                std::make_shared<AnalogFromButton>());
 | 
			
		||||
    motion_emu = std::make_shared<MotionEmu>();
 | 
			
		||||
    Input::RegisterFactory<Input::MotionDevice>("motion_emu", motion_emu);
 | 
			
		||||
 | 
			
		||||
#ifdef HAVE_SDL2
 | 
			
		||||
    SDL::Init();
 | 
			
		||||
#endif
 | 
			
		||||
@ -29,6 +34,8 @@ void Shutdown() {
 | 
			
		||||
    Input::UnregisterFactory<Input::ButtonDevice>("keyboard");
 | 
			
		||||
    keyboard.reset();
 | 
			
		||||
    Input::UnregisterFactory<Input::AnalogDevice>("analog_from_button");
 | 
			
		||||
    Input::UnregisterFactory<Input::MotionDevice>("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)},
 | 
			
		||||
 | 
			
		||||
@ -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);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										159
									
								
								src/input_common/motion_emu.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										159
									
								
								src/input_common/motion_emu.cpp
									
									
									
									
									
										Normal file
									
								
							@ -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::steady_clock::duration>(
 | 
			
		||||
              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<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 EndTilt() {
 | 
			
		||||
        std::lock_guard<std::mutex> guard(tilt_mutex);
 | 
			
		||||
        tilt_angle = 0;
 | 
			
		||||
        is_tilting = false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::tuple<Math::Vec3<float>, Math::Vec3<float>> GetStatus() {
 | 
			
		||||
        std::lock_guard<std::mutex> guard(status_mutex);
 | 
			
		||||
        return status;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    const int update_millisecond;
 | 
			
		||||
    const std::chrono::steady_clock::duration update_duration;
 | 
			
		||||
    const float sensitivity;
 | 
			
		||||
 | 
			
		||||
    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;
 | 
			
		||||
 | 
			
		||||
    std::tuple<Math::Vec3<float>, Math::Vec3<float>> status;
 | 
			
		||||
    std::mutex status_mutex;
 | 
			
		||||
 | 
			
		||||
    void MotionEmuThread() {
 | 
			
		||||
        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
 | 
			
		||||
            {
 | 
			
		||||
                std::lock_guard<std::mutex> 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<MotionEmuDevice>(update_millisecond, sensitivity);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::tuple<Math::Vec3<float>, Math::Vec3<float>> GetStatus() const {
 | 
			
		||||
        return device->GetStatus();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::shared_ptr<MotionEmuDevice> device;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
std::unique_ptr<Input::MotionDevice> 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<MotionEmuDeviceWrapper>(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
 | 
			
		||||
@ -1,19 +1,25 @@
 | 
			
		||||
// Copyright 2016 Citra Emulator Project
 | 
			
		||||
// 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"
 | 
			
		||||
 | 
			
		||||
class EmuWindow;
 | 
			
		||||
namespace InputCommon {
 | 
			
		||||
 | 
			
		||||
namespace Motion {
 | 
			
		||||
class MotionEmuDevice;
 | 
			
		||||
 | 
			
		||||
class MotionEmu final {
 | 
			
		||||
class MotionEmu : public Input::Factory<Input::MotionDevice> {
 | 
			
		||||
public:
 | 
			
		||||
    MotionEmu(EmuWindow& emu_window);
 | 
			
		||||
    ~MotionEmu();
 | 
			
		||||
    /**
 | 
			
		||||
     * 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<Input::MotionDevice> Create(const Common::ParamPackage& params) override;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Signals that a motion sensor tilt has begun.
 | 
			
		||||
@ -35,18 +41,7 @@ public:
 | 
			
		||||
    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);
 | 
			
		||||
    std::weak_ptr<MotionEmuDevice> current_device;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace Motion
 | 
			
		||||
} // namespace InputCommon
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user