Browse Source
Merge pull request #4939 from german77/MouseInput
Merge pull request #4939 from german77/MouseInput
InputCommon: Implement full mouse supportpull/15/merge
committed by
GitHub
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 793 additions and 277 deletions
-
9src/core/frontend/input.h
-
6src/input_common/CMakeLists.txt
-
73src/input_common/main.cpp
-
41src/input_common/main.h
-
179src/input_common/motion_emu.cpp
-
46src/input_common/motion_emu.h
-
125src/input_common/mouse/mouse_input.cpp
-
99src/input_common/mouse/mouse_input.h
-
261src/input_common/mouse/mouse_poller.cpp
-
109src/input_common/mouse/mouse_poller.h
-
26src/yuzu/bootmanager.cpp
-
87src/yuzu/configuration/configure_input_player.cpp
-
8src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
-
1src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp
@ -1,179 +0,0 @@ |
|||||
// Copyright 2017 Citra Emulator Project
|
|
||||
// Licensed under GPLv2 or any later version
|
|
||||
// Refer to the license.txt file included.
|
|
||||
|
|
||||
#include <algorithm>
|
|
||||
#include <chrono>
|
|
||||
#include <mutex>
|
|
||||
#include <thread>
|
|
||||
#include <tuple>
|
|
||||
#include "common/math_util.h"
|
|
||||
#include "common/quaternion.h"
|
|
||||
#include "common/thread.h"
|
|
||||
#include "common/vector_math.h"
|
|
||||
#include "input_common/motion_emu.h"
|
|
||||
|
|
||||
namespace InputCommon { |
|
||||
|
|
||||
// Implementation class of the motion emulation device
|
|
||||
class MotionEmuDevice { |
|
||||
public: |
|
||||
explicit 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 = Common::MakeVec(x, y); |
|
||||
is_tilting = true; |
|
||||
} |
|
||||
|
|
||||
void Tilt(int x, int y) { |
|
||||
if (!is_tilting) { |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
std::lock_guard guard{tilt_mutex}; |
|
||||
const auto mouse_move = Common::MakeVec(x, y) - mouse_origin; |
|
||||
if (mouse_move.x == 0 && mouse_move.y == 0) { |
|
||||
tilt_angle = 0; |
|
||||
} else { |
|
||||
tilt_direction = mouse_move.Cast<float>(); |
|
||||
tilt_angle = |
|
||||
std::clamp(tilt_direction.Normalize() * sensitivity, 0.0f, Common::PI * 0.5f); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
void EndTilt() { |
|
||||
std::lock_guard guard{tilt_mutex}; |
|
||||
tilt_angle = 0; |
|
||||
is_tilting = false; |
|
||||
} |
|
||||
|
|
||||
Input::MotionStatus 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; |
|
||||
|
|
||||
Common::Vec2<int> mouse_origin; |
|
||||
|
|
||||
std::mutex tilt_mutex; |
|
||||
Common::Vec2<float> tilt_direction; |
|
||||
float tilt_angle = 0; |
|
||||
|
|
||||
bool is_tilting = false; |
|
||||
|
|
||||
Common::Event shutdown_event; |
|
||||
|
|
||||
Input::MotionStatus status; |
|
||||
std::mutex status_mutex; |
|
||||
|
|
||||
// Note: always keep the thread declaration at the end so that other objects are initialized
|
|
||||
// before this!
|
|
||||
std::thread motion_emu_thread; |
|
||||
|
|
||||
void MotionEmuThread() { |
|
||||
auto update_time = std::chrono::steady_clock::now(); |
|
||||
Common::Quaternion<float> q = Common::MakeQuaternion(Common::Vec3<float>(), 0); |
|
||||
|
|
||||
while (!shutdown_event.WaitUntil(update_time)) { |
|
||||
update_time += update_duration; |
|
||||
const Common::Quaternion<float> old_q = q; |
|
||||
|
|
||||
{ |
|
||||
std::lock_guard guard{tilt_mutex}; |
|
||||
|
|
||||
// Find the quaternion describing current 3DS tilting
|
|
||||
q = Common::MakeQuaternion( |
|
||||
Common::MakeVec(-tilt_direction.y, 0.0f, tilt_direction.x), tilt_angle); |
|
||||
} |
|
||||
|
|
||||
const auto inv_q = q.Inverse(); |
|
||||
|
|
||||
// Set the gravity vector in world space
|
|
||||
auto gravity = Common::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 *= static_cast<float>(1000 / update_millisecond) / Common::PI * 180.0f; |
|
||||
|
|
||||
// Transform the two vectors from world space to 3DS space
|
|
||||
gravity = QuaternionRotate(inv_q, gravity); |
|
||||
angular_rate = QuaternionRotate(inv_q, angular_rate); |
|
||||
|
|
||||
// TODO: Calculate the correct rotation vector and orientation matrix
|
|
||||
const auto matrix4x4 = q.ToMatrix(); |
|
||||
const auto rotation = Common::MakeVec(0.0f, 0.0f, 0.0f); |
|
||||
const std::array orientation{ |
|
||||
Common::Vec3f(matrix4x4[0], matrix4x4[1], -matrix4x4[2]), |
|
||||
Common::Vec3f(matrix4x4[4], matrix4x4[5], -matrix4x4[6]), |
|
||||
Common::Vec3f(-matrix4x4[8], -matrix4x4[9], matrix4x4[10]), |
|
||||
}; |
|
||||
|
|
||||
// Update the sensor state
|
|
||||
{ |
|
||||
std::lock_guard guard{status_mutex}; |
|
||||
status = std::make_tuple(gravity, angular_rate, rotation, orientation); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
// 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: |
|
||||
explicit MotionEmuDeviceWrapper(int update_millisecond, float sensitivity) { |
|
||||
device = std::make_shared<MotionEmuDevice>(update_millisecond, sensitivity); |
|
||||
} |
|
||||
|
|
||||
Input::MotionStatus GetStatus() const override { |
|
||||
return device->GetStatus(); |
|
||||
} |
|
||||
|
|
||||
std::shared_ptr<MotionEmuDevice> device; |
|
||||
}; |
|
||||
|
|
||||
std::unique_ptr<Input::MotionDevice> MotionEmu::Create(const Common::ParamPackage& params) { |
|
||||
const int update_period = params.Get("update_period", 100); |
|
||||
const 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 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,46 +0,0 @@ |
|||||
// Copyright 2017 Citra Emulator Project |
|
||||
// Licensed under GPLv2 or any later version |
|
||||
// Refer to the license.txt file included. |
|
||||
|
|
||||
#pragma once |
|
||||
|
|
||||
#include "core/frontend/input.h" |
|
||||
|
|
||||
namespace InputCommon { |
|
||||
|
|
||||
class MotionEmuDevice; |
|
||||
|
|
||||
class MotionEmu : public Input::Factory<Input::MotionDevice> { |
|
||||
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<Input::MotionDevice> 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<MotionEmuDevice> current_device; |
|
||||
}; |
|
||||
|
|
||||
} // namespace InputCommon |
|
||||
@ -0,0 +1,125 @@ |
|||||
|
// Copyright 2020 yuzu Emulator Project
|
||||
|
// Licensed under GPLv2+
|
||||
|
// Refer to the license.txt file included.
|
||||
|
|
||||
|
#include "common/logging/log.h"
|
||||
|
#include "common/math_util.h"
|
||||
|
#include "common/param_package.h"
|
||||
|
#include "input_common/mouse/mouse_input.h"
|
||||
|
|
||||
|
namespace MouseInput { |
||||
|
|
||||
|
Mouse::Mouse() { |
||||
|
update_thread = std::thread(&Mouse::UpdateThread, this); |
||||
|
} |
||||
|
|
||||
|
Mouse::~Mouse() { |
||||
|
update_thread_running = false; |
||||
|
if (update_thread.joinable()) { |
||||
|
update_thread.join(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
void Mouse::UpdateThread() { |
||||
|
constexpr int update_time = 10; |
||||
|
while (update_thread_running) { |
||||
|
for (MouseInfo& info : mouse_info) { |
||||
|
Common::Vec3f angular_direction = {-info.tilt_direction.y, 0.0f, |
||||
|
-info.tilt_direction.x}; |
||||
|
|
||||
|
info.motion.SetGyroscope(angular_direction * info.tilt_speed); |
||||
|
info.motion.UpdateRotation(update_time * 1000); |
||||
|
info.motion.UpdateOrientation(update_time * 1000); |
||||
|
info.tilt_speed = 0; |
||||
|
info.data.motion = info.motion.GetMotion(); |
||||
|
} |
||||
|
if (configuring) { |
||||
|
UpdateYuzuSettings(); |
||||
|
} |
||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(update_time)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
void Mouse::UpdateYuzuSettings() { |
||||
|
MouseStatus pad_status{}; |
||||
|
if (buttons != 0) { |
||||
|
pad_status.button = last_button; |
||||
|
mouse_queue.Push(pad_status); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
void Mouse::PressButton(int x, int y, int button_) { |
||||
|
if (button_ >= static_cast<int>(mouse_info.size())) { |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
int button = 1 << button_; |
||||
|
buttons |= static_cast<u16>(button); |
||||
|
last_button = static_cast<MouseButton>(button_); |
||||
|
|
||||
|
mouse_info[button_].mouse_origin = Common::MakeVec(x, y); |
||||
|
mouse_info[button_].last_mouse_position = Common::MakeVec(x, y); |
||||
|
mouse_info[button_].data.pressed = true; |
||||
|
} |
||||
|
|
||||
|
void Mouse::MouseMove(int x, int y) { |
||||
|
for (MouseInfo& info : mouse_info) { |
||||
|
if (info.data.pressed) { |
||||
|
auto mouse_move = Common::MakeVec(x, y) - info.mouse_origin; |
||||
|
auto mouse_change = Common::MakeVec(x, y) - info.last_mouse_position; |
||||
|
info.last_mouse_position = Common::MakeVec(x, y); |
||||
|
info.data.axis = {mouse_move.x, -mouse_move.y}; |
||||
|
|
||||
|
if (mouse_change.x == 0 && mouse_change.y == 0) { |
||||
|
info.tilt_speed = 0; |
||||
|
} else { |
||||
|
info.tilt_direction = mouse_change.Cast<float>(); |
||||
|
info.tilt_speed = info.tilt_direction.Normalize() * info.sensitivity; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
void Mouse::ReleaseButton(int button_) { |
||||
|
if (button_ >= static_cast<int>(mouse_info.size())) { |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
int button = 1 << button_; |
||||
|
buttons &= static_cast<u16>(0xFF - button); |
||||
|
|
||||
|
mouse_info[button_].tilt_speed = 0; |
||||
|
mouse_info[button_].data.pressed = false; |
||||
|
mouse_info[button_].data.axis = {0, 0}; |
||||
|
} |
||||
|
|
||||
|
void Mouse::BeginConfiguration() { |
||||
|
buttons = 0; |
||||
|
last_button = MouseButton::Undefined; |
||||
|
mouse_queue.Clear(); |
||||
|
configuring = true; |
||||
|
} |
||||
|
|
||||
|
void Mouse::EndConfiguration() { |
||||
|
buttons = 0; |
||||
|
last_button = MouseButton::Undefined; |
||||
|
mouse_queue.Clear(); |
||||
|
configuring = false; |
||||
|
} |
||||
|
|
||||
|
Common::SPSCQueue<MouseStatus>& Mouse::GetMouseQueue() { |
||||
|
return mouse_queue; |
||||
|
} |
||||
|
|
||||
|
const Common::SPSCQueue<MouseStatus>& Mouse::GetMouseQueue() const { |
||||
|
return mouse_queue; |
||||
|
} |
||||
|
|
||||
|
MouseData& Mouse::GetMouseState(std::size_t button) { |
||||
|
return mouse_info[button].data; |
||||
|
} |
||||
|
|
||||
|
const MouseData& Mouse::GetMouseState(std::size_t button) const { |
||||
|
return mouse_info[button].data; |
||||
|
} |
||||
|
} // namespace MouseInput
|
||||
@ -0,0 +1,99 @@ |
|||||
|
// Copyright 2020 yuzu Emulator Project |
||||
|
// Licensed under GPLv2 or any later version |
||||
|
// Refer to the license.txt file included. |
||||
|
|
||||
|
#pragma once |
||||
|
|
||||
|
#include <algorithm> |
||||
|
#include <functional> |
||||
|
#include <mutex> |
||||
|
#include <thread> |
||||
|
#include <unordered_map> |
||||
|
#include "common/common_types.h" |
||||
|
#include "common/threadsafe_queue.h" |
||||
|
#include "core/frontend/input.h" |
||||
|
#include "input_common/main.h" |
||||
|
#include "input_common/motion_input.h" |
||||
|
|
||||
|
namespace MouseInput { |
||||
|
|
||||
|
enum class MouseButton { |
||||
|
Left, |
||||
|
Wheel, |
||||
|
Right, |
||||
|
Foward, |
||||
|
Backward, |
||||
|
Undefined, |
||||
|
}; |
||||
|
|
||||
|
struct MouseStatus { |
||||
|
MouseButton button{MouseButton::Undefined}; |
||||
|
}; |
||||
|
|
||||
|
struct MouseData { |
||||
|
bool pressed{}; |
||||
|
std::array<int, 2> axis{}; |
||||
|
Input::MotionStatus motion{}; |
||||
|
Input::TouchStatus touch{}; |
||||
|
}; |
||||
|
|
||||
|
class Mouse { |
||||
|
public: |
||||
|
Mouse(); |
||||
|
~Mouse(); |
||||
|
|
||||
|
/// Used for polling |
||||
|
void BeginConfiguration(); |
||||
|
void EndConfiguration(); |
||||
|
|
||||
|
/** |
||||
|
* Signals that a button is pressed. |
||||
|
* @param x the x-coordinate of the cursor |
||||
|
* @param y the y-coordinate of the cursor |
||||
|
* @param button the button pressed |
||||
|
*/ |
||||
|
void PressButton(int x, int y, int button_); |
||||
|
|
||||
|
/** |
||||
|
* Signals that mouse has moved. |
||||
|
* @param x the x-coordinate of the cursor |
||||
|
* @param y the y-coordinate of the cursor |
||||
|
*/ |
||||
|
void MouseMove(int x, int y); |
||||
|
|
||||
|
/** |
||||
|
* Signals that a motion sensor tilt has ended. |
||||
|
*/ |
||||
|
void ReleaseButton(int button_); |
||||
|
|
||||
|
[[nodiscard]] Common::SPSCQueue<MouseStatus>& GetMouseQueue(); |
||||
|
[[nodiscard]] const Common::SPSCQueue<MouseStatus>& GetMouseQueue() const; |
||||
|
|
||||
|
[[nodiscard]] MouseData& GetMouseState(std::size_t button); |
||||
|
[[nodiscard]] const MouseData& GetMouseState(std::size_t button) const; |
||||
|
|
||||
|
private: |
||||
|
void UpdateThread(); |
||||
|
void UpdateYuzuSettings(); |
||||
|
|
||||
|
struct MouseInfo { |
||||
|
InputCommon::MotionInput motion{0.0f, 0.0f, 0.0f}; |
||||
|
Common::Vec2<int> mouse_origin; |
||||
|
Common::Vec2<int> last_mouse_position; |
||||
|
bool is_tilting = false; |
||||
|
float sensitivity{0.120f}; |
||||
|
|
||||
|
float tilt_speed = 0; |
||||
|
Common::Vec2<float> tilt_direction; |
||||
|
MouseData data; |
||||
|
}; |
||||
|
|
||||
|
u16 buttons{}; |
||||
|
std::thread update_thread; |
||||
|
MouseButton last_button{MouseButton::Undefined}; |
||||
|
std::array<MouseInfo, 5> mouse_info; |
||||
|
Common::SPSCQueue<MouseStatus> mouse_queue; |
||||
|
bool configuring{false}; |
||||
|
bool update_thread_running{true}; |
||||
|
}; |
||||
|
} // namespace MouseInput |
||||
@ -0,0 +1,261 @@ |
|||||
|
// Copyright 2020 yuzu Emulator Project
|
||||
|
// Licensed under GPLv2 or any later version
|
||||
|
// Refer to the license.txt file included.
|
||||
|
|
||||
|
#include <atomic>
|
||||
|
#include <list>
|
||||
|
#include <mutex>
|
||||
|
#include <utility>
|
||||
|
#include "common/assert.h"
|
||||
|
#include "common/threadsafe_queue.h"
|
||||
|
#include "input_common/mouse/mouse_input.h"
|
||||
|
#include "input_common/mouse/mouse_poller.h"
|
||||
|
|
||||
|
namespace InputCommon { |
||||
|
|
||||
|
class MouseButton final : public Input::ButtonDevice { |
||||
|
public: |
||||
|
explicit MouseButton(u32 button_, const MouseInput::Mouse* mouse_input_) |
||||
|
: button(button_), mouse_input(mouse_input_) {} |
||||
|
|
||||
|
bool GetStatus() const override { |
||||
|
return mouse_input->GetMouseState(button).pressed; |
||||
|
} |
||||
|
|
||||
|
private: |
||||
|
const u32 button; |
||||
|
const MouseInput::Mouse* mouse_input; |
||||
|
}; |
||||
|
|
||||
|
MouseButtonFactory::MouseButtonFactory(std::shared_ptr<MouseInput::Mouse> mouse_input_) |
||||
|
: mouse_input(std::move(mouse_input_)) {} |
||||
|
|
||||
|
std::unique_ptr<Input::ButtonDevice> MouseButtonFactory::Create( |
||||
|
const Common::ParamPackage& params) { |
||||
|
const auto button_id = params.Get("button", 0); |
||||
|
|
||||
|
return std::make_unique<MouseButton>(button_id, mouse_input.get()); |
||||
|
} |
||||
|
|
||||
|
Common::ParamPackage MouseButtonFactory::GetNextInput() const { |
||||
|
MouseInput::MouseStatus pad; |
||||
|
Common::ParamPackage params; |
||||
|
auto& queue = mouse_input->GetMouseQueue(); |
||||
|
while (queue.Pop(pad)) { |
||||
|
// This while loop will break on the earliest detected button
|
||||
|
if (pad.button != MouseInput::MouseButton::Undefined) { |
||||
|
params.Set("engine", "mouse"); |
||||
|
params.Set("button", static_cast<u16>(pad.button)); |
||||
|
return params; |
||||
|
} |
||||
|
} |
||||
|
return params; |
||||
|
} |
||||
|
|
||||
|
void MouseButtonFactory::BeginConfiguration() { |
||||
|
polling = true; |
||||
|
mouse_input->BeginConfiguration(); |
||||
|
} |
||||
|
|
||||
|
void MouseButtonFactory::EndConfiguration() { |
||||
|
polling = false; |
||||
|
mouse_input->EndConfiguration(); |
||||
|
} |
||||
|
|
||||
|
class MouseAnalog final : public Input::AnalogDevice { |
||||
|
public: |
||||
|
explicit MouseAnalog(u32 port_, u32 axis_x_, u32 axis_y_, float deadzone_, float range_, |
||||
|
const MouseInput::Mouse* mouse_input_) |
||||
|
: button(port_), axis_x(axis_x_), axis_y(axis_y_), deadzone(deadzone_), range(range_), |
||||
|
mouse_input(mouse_input_) {} |
||||
|
|
||||
|
float GetAxis(u32 axis) const { |
||||
|
std::lock_guard lock{mutex}; |
||||
|
const auto axis_value = |
||||
|
static_cast<float>(mouse_input->GetMouseState(button).axis.at(axis)); |
||||
|
return axis_value / (100.0f * range); |
||||
|
} |
||||
|
|
||||
|
std::pair<float, float> GetAnalog(u32 analog_axis_x, u32 analog_axis_y) const { |
||||
|
float x = GetAxis(analog_axis_x); |
||||
|
float y = GetAxis(analog_axis_y); |
||||
|
|
||||
|
// Make sure the coordinates are in the unit circle,
|
||||
|
// otherwise normalize it.
|
||||
|
float r = x * x + y * y; |
||||
|
if (r > 1.0f) { |
||||
|
r = std::sqrt(r); |
||||
|
x /= r; |
||||
|
y /= r; |
||||
|
} |
||||
|
|
||||
|
return {x, y}; |
||||
|
} |
||||
|
|
||||
|
std::tuple<float, float> GetStatus() const override { |
||||
|
const auto [x, y] = GetAnalog(axis_x, axis_y); |
||||
|
const float r = std::sqrt((x * x) + (y * y)); |
||||
|
if (r > deadzone) { |
||||
|
return {x / r * (r - deadzone) / (1 - deadzone), |
||||
|
y / r * (r - deadzone) / (1 - deadzone)}; |
||||
|
} |
||||
|
return {0.0f, 0.0f}; |
||||
|
} |
||||
|
|
||||
|
private: |
||||
|
const u32 button; |
||||
|
const u32 axis_x; |
||||
|
const u32 axis_y; |
||||
|
const float deadzone; |
||||
|
const float range; |
||||
|
const MouseInput::Mouse* mouse_input; |
||||
|
mutable std::mutex mutex; |
||||
|
}; |
||||
|
|
||||
|
/// An analog device factory that creates analog devices from GC Adapter
|
||||
|
MouseAnalogFactory::MouseAnalogFactory(std::shared_ptr<MouseInput::Mouse> mouse_input_) |
||||
|
: mouse_input(std::move(mouse_input_)) {} |
||||
|
|
||||
|
/**
|
||||
|
* Creates analog device from joystick axes |
||||
|
* @param params contains parameters for creating the device: |
||||
|
* - "port": the nth gcpad on the adapter |
||||
|
* - "axis_x": the index of the axis to be bind as x-axis |
||||
|
* - "axis_y": the index of the axis to be bind as y-axis |
||||
|
*/ |
||||
|
std::unique_ptr<Input::AnalogDevice> MouseAnalogFactory::Create( |
||||
|
const Common::ParamPackage& params) { |
||||
|
const auto port = static_cast<u32>(params.Get("port", 0)); |
||||
|
const auto axis_x = static_cast<u32>(params.Get("axis_x", 0)); |
||||
|
const auto axis_y = static_cast<u32>(params.Get("axis_y", 1)); |
||||
|
const auto deadzone = std::clamp(params.Get("deadzone", 0.0f), 0.0f, 1.0f); |
||||
|
const auto range = std::clamp(params.Get("range", 1.0f), 0.50f, 1.50f); |
||||
|
|
||||
|
return std::make_unique<MouseAnalog>(port, axis_x, axis_y, deadzone, range, mouse_input.get()); |
||||
|
} |
||||
|
|
||||
|
void MouseAnalogFactory::BeginConfiguration() { |
||||
|
polling = true; |
||||
|
mouse_input->BeginConfiguration(); |
||||
|
} |
||||
|
|
||||
|
void MouseAnalogFactory::EndConfiguration() { |
||||
|
polling = false; |
||||
|
mouse_input->EndConfiguration(); |
||||
|
} |
||||
|
|
||||
|
Common::ParamPackage MouseAnalogFactory::GetNextInput() const { |
||||
|
MouseInput::MouseStatus pad; |
||||
|
Common::ParamPackage params; |
||||
|
auto& queue = mouse_input->GetMouseQueue(); |
||||
|
while (queue.Pop(pad)) { |
||||
|
// This while loop will break on the earliest detected button
|
||||
|
if (pad.button != MouseInput::MouseButton::Undefined) { |
||||
|
params.Set("engine", "mouse"); |
||||
|
params.Set("port", static_cast<u16>(pad.button)); |
||||
|
params.Set("axis_x", 0); |
||||
|
params.Set("axis_y", 1); |
||||
|
return params; |
||||
|
} |
||||
|
} |
||||
|
return params; |
||||
|
} |
||||
|
|
||||
|
class MouseMotion final : public Input::MotionDevice { |
||||
|
public: |
||||
|
explicit MouseMotion(u32 button_, const MouseInput::Mouse* mouse_input_) |
||||
|
: button(button_), mouse_input(mouse_input_) {} |
||||
|
|
||||
|
Input::MotionStatus GetStatus() const override { |
||||
|
return mouse_input->GetMouseState(button).motion; |
||||
|
} |
||||
|
|
||||
|
private: |
||||
|
const u32 button; |
||||
|
const MouseInput::Mouse* mouse_input; |
||||
|
}; |
||||
|
|
||||
|
MouseMotionFactory::MouseMotionFactory(std::shared_ptr<MouseInput::Mouse> mouse_input_) |
||||
|
: mouse_input(std::move(mouse_input_)) {} |
||||
|
|
||||
|
std::unique_ptr<Input::MotionDevice> MouseMotionFactory::Create( |
||||
|
const Common::ParamPackage& params) { |
||||
|
const auto button_id = params.Get("button", 0); |
||||
|
|
||||
|
return std::make_unique<MouseMotion>(button_id, mouse_input.get()); |
||||
|
} |
||||
|
|
||||
|
Common::ParamPackage MouseMotionFactory::GetNextInput() const { |
||||
|
MouseInput::MouseStatus pad; |
||||
|
Common::ParamPackage params; |
||||
|
auto& queue = mouse_input->GetMouseQueue(); |
||||
|
while (queue.Pop(pad)) { |
||||
|
// This while loop will break on the earliest detected button
|
||||
|
if (pad.button != MouseInput::MouseButton::Undefined) { |
||||
|
params.Set("engine", "mouse"); |
||||
|
params.Set("button", static_cast<u16>(pad.button)); |
||||
|
return params; |
||||
|
} |
||||
|
} |
||||
|
return params; |
||||
|
} |
||||
|
|
||||
|
void MouseMotionFactory::BeginConfiguration() { |
||||
|
polling = true; |
||||
|
mouse_input->BeginConfiguration(); |
||||
|
} |
||||
|
|
||||
|
void MouseMotionFactory::EndConfiguration() { |
||||
|
polling = false; |
||||
|
mouse_input->EndConfiguration(); |
||||
|
} |
||||
|
|
||||
|
class MouseTouch final : public Input::TouchDevice { |
||||
|
public: |
||||
|
explicit MouseTouch(u32 button_, const MouseInput::Mouse* mouse_input_) |
||||
|
: button(button_), mouse_input(mouse_input_) {} |
||||
|
|
||||
|
Input::TouchStatus GetStatus() const override { |
||||
|
return mouse_input->GetMouseState(button).touch; |
||||
|
} |
||||
|
|
||||
|
private: |
||||
|
const u32 button; |
||||
|
const MouseInput::Mouse* mouse_input; |
||||
|
}; |
||||
|
|
||||
|
MouseTouchFactory::MouseTouchFactory(std::shared_ptr<MouseInput::Mouse> mouse_input_) |
||||
|
: mouse_input(std::move(mouse_input_)) {} |
||||
|
|
||||
|
std::unique_ptr<Input::TouchDevice> MouseTouchFactory::Create(const Common::ParamPackage& params) { |
||||
|
const auto button_id = params.Get("button", 0); |
||||
|
|
||||
|
return std::make_unique<MouseTouch>(button_id, mouse_input.get()); |
||||
|
} |
||||
|
|
||||
|
Common::ParamPackage MouseTouchFactory::GetNextInput() const { |
||||
|
MouseInput::MouseStatus pad; |
||||
|
Common::ParamPackage params; |
||||
|
auto& queue = mouse_input->GetMouseQueue(); |
||||
|
while (queue.Pop(pad)) { |
||||
|
// This while loop will break on the earliest detected button
|
||||
|
if (pad.button != MouseInput::MouseButton::Undefined) { |
||||
|
params.Set("engine", "mouse"); |
||||
|
params.Set("button", static_cast<u16>(pad.button)); |
||||
|
return params; |
||||
|
} |
||||
|
} |
||||
|
return params; |
||||
|
} |
||||
|
|
||||
|
void MouseTouchFactory::BeginConfiguration() { |
||||
|
polling = true; |
||||
|
mouse_input->BeginConfiguration(); |
||||
|
} |
||||
|
|
||||
|
void MouseTouchFactory::EndConfiguration() { |
||||
|
polling = false; |
||||
|
mouse_input->EndConfiguration(); |
||||
|
} |
||||
|
|
||||
|
} // namespace InputCommon
|
||||
@ -0,0 +1,109 @@ |
|||||
|
// Copyright 2020 yuzu Emulator Project |
||||
|
// Licensed under GPLv2 or any later version |
||||
|
// Refer to the license.txt file included. |
||||
|
|
||||
|
#pragma once |
||||
|
|
||||
|
#include <memory> |
||||
|
#include "core/frontend/input.h" |
||||
|
#include "input_common/mouse/mouse_input.h" |
||||
|
|
||||
|
namespace InputCommon { |
||||
|
|
||||
|
/** |
||||
|
* A button device factory representing a mouse. It receives mouse events and forward them |
||||
|
* to all button devices it created. |
||||
|
*/ |
||||
|
class MouseButtonFactory final : public Input::Factory<Input::ButtonDevice> { |
||||
|
public: |
||||
|
explicit MouseButtonFactory(std::shared_ptr<MouseInput::Mouse> mouse_input_); |
||||
|
|
||||
|
/** |
||||
|
* Creates a button device from a button press |
||||
|
* @param params contains parameters for creating the device: |
||||
|
* - "code": the code of the key to bind with the button |
||||
|
*/ |
||||
|
std::unique_ptr<Input::ButtonDevice> Create(const Common::ParamPackage& params) override; |
||||
|
|
||||
|
Common::ParamPackage GetNextInput() const; |
||||
|
|
||||
|
/// For device input configuration/polling |
||||
|
void BeginConfiguration(); |
||||
|
void EndConfiguration(); |
||||
|
|
||||
|
bool IsPolling() const { |
||||
|
return polling; |
||||
|
} |
||||
|
|
||||
|
private: |
||||
|
std::shared_ptr<MouseInput::Mouse> mouse_input; |
||||
|
bool polling = false; |
||||
|
}; |
||||
|
|
||||
|
/// An analog device factory that creates analog devices from mouse |
||||
|
class MouseAnalogFactory final : public Input::Factory<Input::AnalogDevice> { |
||||
|
public: |
||||
|
explicit MouseAnalogFactory(std::shared_ptr<MouseInput::Mouse> mouse_input_); |
||||
|
|
||||
|
std::unique_ptr<Input::AnalogDevice> Create(const Common::ParamPackage& params) override; |
||||
|
|
||||
|
Common::ParamPackage GetNextInput() const; |
||||
|
|
||||
|
/// For device input configuration/polling |
||||
|
void BeginConfiguration(); |
||||
|
void EndConfiguration(); |
||||
|
|
||||
|
bool IsPolling() const { |
||||
|
return polling; |
||||
|
} |
||||
|
|
||||
|
private: |
||||
|
std::shared_ptr<MouseInput::Mouse> mouse_input; |
||||
|
bool polling = false; |
||||
|
}; |
||||
|
|
||||
|
/// A motion device factory that creates motion devices from mouse |
||||
|
class MouseMotionFactory final : public Input::Factory<Input::MotionDevice> { |
||||
|
public: |
||||
|
explicit MouseMotionFactory(std::shared_ptr<MouseInput::Mouse> mouse_input_); |
||||
|
|
||||
|
std::unique_ptr<Input::MotionDevice> Create(const Common::ParamPackage& params) override; |
||||
|
|
||||
|
Common::ParamPackage GetNextInput() const; |
||||
|
|
||||
|
/// For device input configuration/polling |
||||
|
void BeginConfiguration(); |
||||
|
void EndConfiguration(); |
||||
|
|
||||
|
bool IsPolling() const { |
||||
|
return polling; |
||||
|
} |
||||
|
|
||||
|
private: |
||||
|
std::shared_ptr<MouseInput::Mouse> mouse_input; |
||||
|
bool polling = false; |
||||
|
}; |
||||
|
|
||||
|
/// An touch device factory that creates touch devices from mouse |
||||
|
class MouseTouchFactory final : public Input::Factory<Input::TouchDevice> { |
||||
|
public: |
||||
|
explicit MouseTouchFactory(std::shared_ptr<MouseInput::Mouse> mouse_input_); |
||||
|
|
||||
|
std::unique_ptr<Input::TouchDevice> Create(const Common::ParamPackage& params) override; |
||||
|
|
||||
|
Common::ParamPackage GetNextInput() const; |
||||
|
|
||||
|
/// For device input configuration/polling |
||||
|
void BeginConfiguration(); |
||||
|
void EndConfiguration(); |
||||
|
|
||||
|
bool IsPolling() const { |
||||
|
return polling; |
||||
|
} |
||||
|
|
||||
|
private: |
||||
|
std::shared_ptr<MouseInput::Mouse> mouse_input; |
||||
|
bool polling = false; |
||||
|
}; |
||||
|
|
||||
|
} // namespace InputCommon |
||||
Write
Preview
Loading…
Cancel
Save
Reference in new issue