summaryrefslogtreecommitdiff
path: root/src/input_common
diff options
context:
space:
mode:
Diffstat (limited to 'src/input_common')
-rw-r--r--src/input_common/CMakeLists.txt4
-rw-r--r--src/input_common/gcadapter/gc_adapter.cpp2
-rw-r--r--src/input_common/gcadapter/gc_adapter.h2
-rw-r--r--src/input_common/gcadapter/gc_poller.cpp22
-rw-r--r--src/input_common/main.cpp11
-rw-r--r--src/input_common/main.h3
-rw-r--r--src/input_common/motion_input.cpp181
-rw-r--r--src/input_common/motion_input.h68
-rw-r--r--src/input_common/sdl/sdl_impl.cpp114
-rw-r--r--src/input_common/touch_from_button.cpp50
-rw-r--r--src/input_common/touch_from_button.h23
11 files changed, 416 insertions, 64 deletions
diff --git a/src/input_common/CMakeLists.txt b/src/input_common/CMakeLists.txt
index 56267c8a8..09361e37e 100644
--- a/src/input_common/CMakeLists.txt
+++ b/src/input_common/CMakeLists.txt
@@ -7,8 +7,12 @@ add_library(input_common STATIC
7 main.h 7 main.h
8 motion_emu.cpp 8 motion_emu.cpp
9 motion_emu.h 9 motion_emu.h
10 motion_input.cpp
11 motion_input.h
10 settings.cpp 12 settings.cpp
11 settings.h 13 settings.h
14 touch_from_button.cpp
15 touch_from_button.h
12 gcadapter/gc_adapter.cpp 16 gcadapter/gc_adapter.cpp
13 gcadapter/gc_adapter.h 17 gcadapter/gc_adapter.h
14 gcadapter/gc_poller.cpp 18 gcadapter/gc_poller.cpp
diff --git a/src/input_common/gcadapter/gc_adapter.cpp b/src/input_common/gcadapter/gc_adapter.cpp
index 74759ea7d..c6c423c4b 100644
--- a/src/input_common/gcadapter/gc_adapter.cpp
+++ b/src/input_common/gcadapter/gc_adapter.cpp
@@ -283,7 +283,7 @@ void Adapter::Reset() {
283 } 283 }
284} 284}
285 285
286bool Adapter::DeviceConnected(std::size_t port) { 286bool Adapter::DeviceConnected(std::size_t port) const {
287 return adapter_controllers_status[port] != ControllerTypes::None; 287 return adapter_controllers_status[port] != ControllerTypes::None;
288} 288}
289 289
diff --git a/src/input_common/gcadapter/gc_adapter.h b/src/input_common/gcadapter/gc_adapter.h
index bed81915c..20e97d283 100644
--- a/src/input_common/gcadapter/gc_adapter.h
+++ b/src/input_common/gcadapter/gc_adapter.h
@@ -76,7 +76,7 @@ public:
76 void EndConfiguration(); 76 void EndConfiguration();
77 77
78 /// Returns true if there is a device connected to port 78 /// Returns true if there is a device connected to port
79 bool DeviceConnected(std::size_t port); 79 bool DeviceConnected(std::size_t port) const;
80 80
81 std::array<Common::SPSCQueue<GCPadStatus>, 4>& GetPadQueue(); 81 std::array<Common::SPSCQueue<GCPadStatus>, 4>& GetPadQueue();
82 const std::array<Common::SPSCQueue<GCPadStatus>, 4>& GetPadQueue() const; 82 const std::array<Common::SPSCQueue<GCPadStatus>, 4>& GetPadQueue() const;
diff --git a/src/input_common/gcadapter/gc_poller.cpp b/src/input_common/gcadapter/gc_poller.cpp
index 71cd85eeb..92e9e8e89 100644
--- a/src/input_common/gcadapter/gc_poller.cpp
+++ b/src/input_common/gcadapter/gc_poller.cpp
@@ -15,7 +15,7 @@ namespace InputCommon {
15 15
16class GCButton final : public Input::ButtonDevice { 16class GCButton final : public Input::ButtonDevice {
17public: 17public:
18 explicit GCButton(int port_, int button_, GCAdapter::Adapter* adapter) 18 explicit GCButton(int port_, int button_, const GCAdapter::Adapter* adapter)
19 : port(port_), button(button_), gcadapter(adapter) {} 19 : port(port_), button(button_), gcadapter(adapter) {}
20 20
21 ~GCButton() override; 21 ~GCButton() override;
@@ -30,15 +30,16 @@ public:
30private: 30private:
31 const int port; 31 const int port;
32 const int button; 32 const int button;
33 GCAdapter::Adapter* gcadapter; 33 const GCAdapter::Adapter* gcadapter;
34}; 34};
35 35
36class GCAxisButton final : public Input::ButtonDevice { 36class GCAxisButton final : public Input::ButtonDevice {
37public: 37public:
38 explicit GCAxisButton(int port_, int axis_, float threshold_, bool trigger_if_greater_, 38 explicit GCAxisButton(int port_, int axis_, float threshold_, bool trigger_if_greater_,
39 GCAdapter::Adapter* adapter) 39 const GCAdapter::Adapter* adapter)
40 : port(port_), axis(axis_), threshold(threshold_), trigger_if_greater(trigger_if_greater_), 40 : port(port_), axis(axis_), threshold(threshold_), trigger_if_greater(trigger_if_greater_),
41 gcadapter(adapter), origin_value(adapter->GetOriginValue(port_, axis_)) {} 41 gcadapter(adapter),
42 origin_value(static_cast<float>(adapter->GetOriginValue(port_, axis_))) {}
42 43
43 bool GetStatus() const override { 44 bool GetStatus() const override {
44 if (gcadapter->DeviceConnected(port)) { 45 if (gcadapter->DeviceConnected(port)) {
@@ -59,7 +60,7 @@ private:
59 const int axis; 60 const int axis;
60 float threshold; 61 float threshold;
61 bool trigger_if_greater; 62 bool trigger_if_greater;
62 GCAdapter::Adapter* gcadapter; 63 const GCAdapter::Adapter* gcadapter;
63 const float origin_value; 64 const float origin_value;
64}; 65};
65 66
@@ -148,11 +149,12 @@ void GCButtonFactory::EndConfiguration() {
148 149
149class GCAnalog final : public Input::AnalogDevice { 150class GCAnalog final : public Input::AnalogDevice {
150public: 151public:
151 GCAnalog(int port_, int axis_x_, int axis_y_, float deadzone_, GCAdapter::Adapter* adapter, 152 GCAnalog(int port_, int axis_x_, int axis_y_, float deadzone_,
152 float range_) 153 const GCAdapter::Adapter* adapter, float range_)
153 : port(port_), axis_x(axis_x_), axis_y(axis_y_), deadzone(deadzone_), gcadapter(adapter), 154 : port(port_), axis_x(axis_x_), axis_y(axis_y_), deadzone(deadzone_), gcadapter(adapter),
154 origin_value_x(adapter->GetOriginValue(port_, axis_x_)), 155 origin_value_x(static_cast<float>(adapter->GetOriginValue(port_, axis_x_))),
155 origin_value_y(adapter->GetOriginValue(port_, axis_y_)), range(range_) {} 156 origin_value_y(static_cast<float>(adapter->GetOriginValue(port_, axis_y_))),
157 range(range_) {}
156 158
157 float GetAxis(int axis) const { 159 float GetAxis(int axis) const {
158 if (gcadapter->DeviceConnected(port)) { 160 if (gcadapter->DeviceConnected(port)) {
@@ -210,7 +212,7 @@ private:
210 const int axis_x; 212 const int axis_x;
211 const int axis_y; 213 const int axis_y;
212 const float deadzone; 214 const float deadzone;
213 GCAdapter::Adapter* gcadapter; 215 const GCAdapter::Adapter* gcadapter;
214 const float origin_value_x; 216 const float origin_value_x;
215 const float origin_value_y; 217 const float origin_value_y;
216 const float range; 218 const float range;
diff --git a/src/input_common/main.cpp b/src/input_common/main.cpp
index 57e7a25fe..ea1a1cee6 100644
--- a/src/input_common/main.cpp
+++ b/src/input_common/main.cpp
@@ -11,6 +11,7 @@
11#include "input_common/keyboard.h" 11#include "input_common/keyboard.h"
12#include "input_common/main.h" 12#include "input_common/main.h"
13#include "input_common/motion_emu.h" 13#include "input_common/motion_emu.h"
14#include "input_common/touch_from_button.h"
14#include "input_common/udp/udp.h" 15#include "input_common/udp/udp.h"
15#ifdef HAVE_SDL2 16#ifdef HAVE_SDL2
16#include "input_common/sdl/sdl.h" 17#include "input_common/sdl/sdl.h"
@@ -32,6 +33,8 @@ struct InputSubsystem::Impl {
32 std::make_shared<AnalogFromButton>()); 33 std::make_shared<AnalogFromButton>());
33 motion_emu = std::make_shared<MotionEmu>(); 34 motion_emu = std::make_shared<MotionEmu>();
34 Input::RegisterFactory<Input::MotionDevice>("motion_emu", motion_emu); 35 Input::RegisterFactory<Input::MotionDevice>("motion_emu", motion_emu);
36 Input::RegisterFactory<Input::TouchDevice>("touch_from_button",
37 std::make_shared<TouchFromButtonFactory>());
35 38
36#ifdef HAVE_SDL2 39#ifdef HAVE_SDL2
37 sdl = SDL::Init(); 40 sdl = SDL::Init();
@@ -46,6 +49,7 @@ struct InputSubsystem::Impl {
46 Input::UnregisterFactory<Input::AnalogDevice>("analog_from_button"); 49 Input::UnregisterFactory<Input::AnalogDevice>("analog_from_button");
47 Input::UnregisterFactory<Input::MotionDevice>("motion_emu"); 50 Input::UnregisterFactory<Input::MotionDevice>("motion_emu");
48 motion_emu.reset(); 51 motion_emu.reset();
52 Input::UnregisterFactory<Input::TouchDevice>("touch_from_button");
49#ifdef HAVE_SDL2 53#ifdef HAVE_SDL2
50 sdl.reset(); 54 sdl.reset();
51#endif 55#endif
@@ -171,6 +175,13 @@ const GCButtonFactory* InputSubsystem::GetGCButtons() const {
171 return impl->gcbuttons.get(); 175 return impl->gcbuttons.get();
172} 176}
173 177
178void InputSubsystem::ReloadInputDevices() {
179 if (!impl->udp) {
180 return;
181 }
182 impl->udp->ReloadUDPClient();
183}
184
174std::vector<std::unique_ptr<Polling::DevicePoller>> InputSubsystem::GetPollers( 185std::vector<std::unique_ptr<Polling::DevicePoller>> InputSubsystem::GetPollers(
175 Polling::DeviceType type) const { 186 Polling::DeviceType type) const {
176#ifdef HAVE_SDL2 187#ifdef HAVE_SDL2
diff --git a/src/input_common/main.h b/src/input_common/main.h
index 58e5dc250..f3fbf696e 100644
--- a/src/input_common/main.h
+++ b/src/input_common/main.h
@@ -115,6 +115,9 @@ public:
115 /// Retrieves the underlying GameCube button handler. 115 /// Retrieves the underlying GameCube button handler.
116 [[nodiscard]] const GCButtonFactory* GetGCButtons() const; 116 [[nodiscard]] const GCButtonFactory* GetGCButtons() const;
117 117
118 /// Reloads the input devices
119 void ReloadInputDevices();
120
118 /// Get all DevicePoller from all backends for a specific device type 121 /// Get all DevicePoller from all backends for a specific device type
119 [[nodiscard]] std::vector<std::unique_ptr<Polling::DevicePoller>> GetPollers( 122 [[nodiscard]] std::vector<std::unique_ptr<Polling::DevicePoller>> GetPollers(
120 Polling::DeviceType type) const; 123 Polling::DeviceType type) const;
diff --git a/src/input_common/motion_input.cpp b/src/input_common/motion_input.cpp
new file mode 100644
index 000000000..22a849866
--- /dev/null
+++ b/src/input_common/motion_input.cpp
@@ -0,0 +1,181 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included
4
5#include "common/math_util.h"
6#include "input_common/motion_input.h"
7
8namespace InputCommon {
9
10MotionInput::MotionInput(f32 new_kp, f32 new_ki, f32 new_kd)
11 : kp(new_kp), ki(new_ki), kd(new_kd), quat{{0, 0, -1}, 0} {}
12
13void MotionInput::SetAcceleration(const Common::Vec3f& acceleration) {
14 accel = acceleration;
15}
16
17void MotionInput::SetGyroscope(const Common::Vec3f& gyroscope) {
18 gyro = gyroscope - gyro_drift;
19 if (gyro.Length2() < gyro_threshold) {
20 gyro = {};
21 }
22}
23
24void MotionInput::SetQuaternion(const Common::Quaternion<f32>& quaternion) {
25 quat = quaternion;
26}
27
28void MotionInput::SetGyroDrift(const Common::Vec3f& drift) {
29 gyro_drift = drift;
30}
31
32void MotionInput::SetGyroThreshold(f32 threshold) {
33 gyro_threshold = threshold;
34}
35
36void MotionInput::EnableReset(bool reset) {
37 reset_enabled = reset;
38}
39
40void MotionInput::ResetRotations() {
41 rotations = {};
42}
43
44bool MotionInput::IsMoving(f32 sensitivity) const {
45 return gyro.Length() >= sensitivity || accel.Length() <= 0.9f || accel.Length() >= 1.1f;
46}
47
48bool MotionInput::IsCalibrated(f32 sensitivity) const {
49 return real_error.Length() < sensitivity;
50}
51
52void MotionInput::UpdateRotation(u64 elapsed_time) {
53 const f32 sample_period = elapsed_time / 1000000.0f;
54 if (sample_period > 0.1f) {
55 return;
56 }
57 rotations += gyro * sample_period;
58}
59
60void MotionInput::UpdateOrientation(u64 elapsed_time) {
61 if (!IsCalibrated(0.1f)) {
62 ResetOrientation();
63 }
64 // Short name local variable for readability
65 f32 q1 = quat.w;
66 f32 q2 = quat.xyz[0];
67 f32 q3 = quat.xyz[1];
68 f32 q4 = quat.xyz[2];
69 const f32 sample_period = elapsed_time / 1000000.0f;
70
71 // ignore invalid elapsed time
72 if (sample_period > 0.1f) {
73 return;
74 }
75
76 const auto normal_accel = accel.Normalized();
77 auto rad_gyro = gyro * Common::PI * 2;
78 const f32 swap = rad_gyro.x;
79 rad_gyro.x = rad_gyro.y;
80 rad_gyro.y = -swap;
81 rad_gyro.z = -rad_gyro.z;
82
83 // Ignore drift correction if acceleration is not reliable
84 if (accel.Length() >= 0.75f && accel.Length() <= 1.25f) {
85 const f32 ax = -normal_accel.x;
86 const f32 ay = normal_accel.y;
87 const f32 az = -normal_accel.z;
88
89 // Estimated direction of gravity
90 const f32 vx = 2.0f * (q2 * q4 - q1 * q3);
91 const f32 vy = 2.0f * (q1 * q2 + q3 * q4);
92 const f32 vz = q1 * q1 - q2 * q2 - q3 * q3 + q4 * q4;
93
94 // Error is cross product between estimated direction and measured direction of gravity
95 const Common::Vec3f new_real_error = {az * vx - ax * vz, ay * vz - az * vy,
96 ax * vy - ay * vx};
97
98 derivative_error = new_real_error - real_error;
99 real_error = new_real_error;
100
101 // Prevent integral windup
102 if (ki != 0.0f && !IsCalibrated(0.05f)) {
103 integral_error += real_error;
104 } else {
105 integral_error = {};
106 }
107
108 // Apply feedback terms
109 rad_gyro += kp * real_error;
110 rad_gyro += ki * integral_error;
111 rad_gyro += kd * derivative_error;
112 }
113
114 const f32 gx = rad_gyro.y;
115 const f32 gy = rad_gyro.x;
116 const f32 gz = rad_gyro.z;
117
118 // Integrate rate of change of quaternion
119 const f32 pa = q2;
120 const f32 pb = q3;
121 const f32 pc = q4;
122 q1 = q1 + (-q2 * gx - q3 * gy - q4 * gz) * (0.5f * sample_period);
123 q2 = pa + (q1 * gx + pb * gz - pc * gy) * (0.5f * sample_period);
124 q3 = pb + (q1 * gy - pa * gz + pc * gx) * (0.5f * sample_period);
125 q4 = pc + (q1 * gz + pa * gy - pb * gx) * (0.5f * sample_period);
126
127 quat.w = q1;
128 quat.xyz[0] = q2;
129 quat.xyz[1] = q3;
130 quat.xyz[2] = q4;
131 quat = quat.Normalized();
132}
133
134std::array<Common::Vec3f, 3> MotionInput::GetOrientation() const {
135 const Common::Quaternion<float> quad{
136 .xyz = {-quat.xyz[1], -quat.xyz[0], -quat.w},
137 .w = -quat.xyz[2],
138 };
139 const std::array<float, 16> matrix4x4 = quad.ToMatrix();
140
141 return {Common::Vec3f(matrix4x4[0], matrix4x4[1], -matrix4x4[2]),
142 Common::Vec3f(matrix4x4[4], matrix4x4[5], -matrix4x4[6]),
143 Common::Vec3f(-matrix4x4[8], -matrix4x4[9], matrix4x4[10])};
144}
145
146Common::Vec3f MotionInput::GetAcceleration() const {
147 return accel;
148}
149
150Common::Vec3f MotionInput::GetGyroscope() const {
151 return gyro;
152}
153
154Common::Quaternion<f32> MotionInput::GetQuaternion() const {
155 return quat;
156}
157
158Common::Vec3f MotionInput::GetRotations() const {
159 return rotations;
160}
161
162void MotionInput::ResetOrientation() {
163 if (!reset_enabled) {
164 return;
165 }
166 if (!IsMoving(0.5f) && accel.z <= -0.9f) {
167 ++reset_counter;
168 if (reset_counter > 900) {
169 // TODO: calculate quaternion from gravity vector
170 quat.w = 0;
171 quat.xyz[0] = 0;
172 quat.xyz[1] = 0;
173 quat.xyz[2] = -1;
174 integral_error = {};
175 reset_counter = 0;
176 }
177 } else {
178 reset_counter = 0;
179 }
180}
181} // namespace InputCommon
diff --git a/src/input_common/motion_input.h b/src/input_common/motion_input.h
new file mode 100644
index 000000000..54b4439d9
--- /dev/null
+++ b/src/input_common/motion_input.h
@@ -0,0 +1,68 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included
4
5#pragma once
6
7#include "common/common_types.h"
8#include "common/quaternion.h"
9#include "common/vector_math.h"
10
11namespace InputCommon {
12
13class MotionInput {
14public:
15 MotionInput(f32 new_kp, f32 new_ki, f32 new_kd);
16
17 MotionInput(const MotionInput&) = default;
18 MotionInput& operator=(const MotionInput&) = default;
19
20 MotionInput(MotionInput&&) = default;
21 MotionInput& operator=(MotionInput&&) = default;
22
23 void SetAcceleration(const Common::Vec3f& acceleration);
24 void SetGyroscope(const Common::Vec3f& acceleration);
25 void SetQuaternion(const Common::Quaternion<f32>& quaternion);
26 void SetGyroDrift(const Common::Vec3f& drift);
27 void SetGyroThreshold(f32 threshold);
28
29 void EnableReset(bool reset);
30 void ResetRotations();
31
32 void UpdateRotation(u64 elapsed_time);
33 void UpdateOrientation(u64 elapsed_time);
34
35 std::array<Common::Vec3f, 3> GetOrientation() const;
36 Common::Vec3f GetAcceleration() const;
37 Common::Vec3f GetGyroscope() const;
38 Common::Vec3f GetRotations() const;
39 Common::Quaternion<f32> GetQuaternion() const;
40
41 bool IsMoving(f32 sensitivity) const;
42 bool IsCalibrated(f32 sensitivity) const;
43
44private:
45 void ResetOrientation();
46
47 // PID constants
48 const f32 kp;
49 const f32 ki;
50 const f32 kd;
51
52 // PID errors
53 Common::Vec3f real_error;
54 Common::Vec3f integral_error;
55 Common::Vec3f derivative_error;
56
57 Common::Quaternion<f32> quat;
58 Common::Vec3f rotations;
59 Common::Vec3f accel;
60 Common::Vec3f gyro;
61 Common::Vec3f gyro_drift;
62
63 f32 gyro_threshold = 0.0f;
64 u32 reset_counter = 0;
65 bool reset_enabled = true;
66};
67
68} // namespace InputCommon
diff --git a/src/input_common/sdl/sdl_impl.cpp b/src/input_common/sdl/sdl_impl.cpp
index c8d9eb2bc..a9e676f4b 100644
--- a/src/input_common/sdl/sdl_impl.cpp
+++ b/src/input_common/sdl/sdl_impl.cpp
@@ -3,6 +3,7 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <algorithm> 5#include <algorithm>
6#include <array>
6#include <atomic> 7#include <atomic>
7#include <cmath> 8#include <cmath>
8#include <functional> 9#include <functional>
@@ -358,7 +359,7 @@ public:
358 return std::make_tuple(x / r * (r - deadzone) / (1 - deadzone), 359 return std::make_tuple(x / r * (r - deadzone) / (1 - deadzone),
359 y / r * (r - deadzone) / (1 - deadzone)); 360 y / r * (r - deadzone) / (1 - deadzone));
360 } 361 }
361 return std::make_tuple<float, float>(0.0f, 0.0f); 362 return {};
362 } 363 }
363 364
364 bool GetAnalogDirectionStatus(Input::AnalogDirection direction) const override { 365 bool GetAnalogDirectionStatus(Input::AnalogDirection direction) const override {
@@ -574,10 +575,10 @@ std::vector<Common::ParamPackage> SDLState::GetInputDevices() {
574 575
575namespace { 576namespace {
576Common::ParamPackage BuildAnalogParamPackageForButton(int port, std::string guid, u8 axis, 577Common::ParamPackage BuildAnalogParamPackageForButton(int port, std::string guid, u8 axis,
577 float value = 0.1) { 578 float value = 0.1f) {
578 Common::ParamPackage params({{"engine", "sdl"}}); 579 Common::ParamPackage params({{"engine", "sdl"}});
579 params.Set("port", port); 580 params.Set("port", port);
580 params.Set("guid", guid); 581 params.Set("guid", std::move(guid));
581 params.Set("axis", axis); 582 params.Set("axis", axis);
582 if (value > 0) { 583 if (value > 0) {
583 params.Set("direction", "+"); 584 params.Set("direction", "+");
@@ -592,7 +593,7 @@ Common::ParamPackage BuildAnalogParamPackageForButton(int port, std::string guid
592Common::ParamPackage BuildButtonParamPackageForButton(int port, std::string guid, u8 button) { 593Common::ParamPackage BuildButtonParamPackageForButton(int port, std::string guid, u8 button) {
593 Common::ParamPackage params({{"engine", "sdl"}}); 594 Common::ParamPackage params({{"engine", "sdl"}});
594 params.Set("port", port); 595 params.Set("port", port);
595 params.Set("guid", guid); 596 params.Set("guid", std::move(guid));
596 params.Set("button", button); 597 params.Set("button", button);
597 return params; 598 return params;
598} 599}
@@ -601,7 +602,7 @@ Common::ParamPackage BuildHatParamPackageForButton(int port, std::string guid, u
601 Common::ParamPackage params({{"engine", "sdl"}}); 602 Common::ParamPackage params({{"engine", "sdl"}});
602 603
603 params.Set("port", port); 604 params.Set("port", port);
604 params.Set("guid", guid); 605 params.Set("guid", std::move(guid));
605 params.Set("hat", hat); 606 params.Set("hat", hat);
606 switch (value) { 607 switch (value) {
607 case SDL_HAT_UP: 608 case SDL_HAT_UP:
@@ -670,55 +671,62 @@ Common::ParamPackage BuildParamPackageForAnalog(int port, const std::string& gui
670} // Anonymous namespace 671} // Anonymous namespace
671 672
672ButtonMapping SDLState::GetButtonMappingForDevice(const Common::ParamPackage& params) { 673ButtonMapping SDLState::GetButtonMappingForDevice(const Common::ParamPackage& params) {
673 // This list is missing ZL/ZR since those are not considered buttons in SDL GameController.
674 // We will add those afterwards
675 // This list also excludes Screenshot since theres not really a mapping for that
676 std::unordered_map<Settings::NativeButton::Values, SDL_GameControllerButton>
677 switch_to_sdl_button = {
678 {Settings::NativeButton::A, SDL_CONTROLLER_BUTTON_B},
679 {Settings::NativeButton::B, SDL_CONTROLLER_BUTTON_A},
680 {Settings::NativeButton::X, SDL_CONTROLLER_BUTTON_Y},
681 {Settings::NativeButton::Y, SDL_CONTROLLER_BUTTON_X},
682 {Settings::NativeButton::LStick, SDL_CONTROLLER_BUTTON_LEFTSTICK},
683 {Settings::NativeButton::RStick, SDL_CONTROLLER_BUTTON_RIGHTSTICK},
684 {Settings::NativeButton::L, SDL_CONTROLLER_BUTTON_LEFTSHOULDER},
685 {Settings::NativeButton::R, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER},
686 {Settings::NativeButton::Plus, SDL_CONTROLLER_BUTTON_START},
687 {Settings::NativeButton::Minus, SDL_CONTROLLER_BUTTON_BACK},
688 {Settings::NativeButton::DLeft, SDL_CONTROLLER_BUTTON_DPAD_LEFT},
689 {Settings::NativeButton::DUp, SDL_CONTROLLER_BUTTON_DPAD_UP},
690 {Settings::NativeButton::DRight, SDL_CONTROLLER_BUTTON_DPAD_RIGHT},
691 {Settings::NativeButton::DDown, SDL_CONTROLLER_BUTTON_DPAD_DOWN},
692 {Settings::NativeButton::SL, SDL_CONTROLLER_BUTTON_LEFTSHOULDER},
693 {Settings::NativeButton::SR, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER},
694 {Settings::NativeButton::Home, SDL_CONTROLLER_BUTTON_GUIDE},
695 };
696 if (!params.Has("guid") || !params.Has("port")) { 674 if (!params.Has("guid") || !params.Has("port")) {
697 return {}; 675 return {};
698 } 676 }
699 const auto joystick = GetSDLJoystickByGUID(params.Get("guid", ""), params.Get("port", 0)); 677 const auto joystick = GetSDLJoystickByGUID(params.Get("guid", ""), params.Get("port", 0));
700 auto controller = joystick->GetSDLGameController(); 678 auto* controller = joystick->GetSDLGameController();
701 if (!controller) { 679 if (controller == nullptr) {
702 return {}; 680 return {};
703 } 681 }
704 682
705 ButtonMapping mapping{}; 683 // This list is missing ZL/ZR since those are not considered buttons in SDL GameController.
684 // We will add those afterwards
685 // This list also excludes Screenshot since theres not really a mapping for that
686 using ButtonBindings =
687 std::array<std::pair<Settings::NativeButton::Values, SDL_GameControllerButton>, 17>;
688 static constexpr ButtonBindings switch_to_sdl_button{{
689 {Settings::NativeButton::A, SDL_CONTROLLER_BUTTON_B},
690 {Settings::NativeButton::B, SDL_CONTROLLER_BUTTON_A},
691 {Settings::NativeButton::X, SDL_CONTROLLER_BUTTON_Y},
692 {Settings::NativeButton::Y, SDL_CONTROLLER_BUTTON_X},
693 {Settings::NativeButton::LStick, SDL_CONTROLLER_BUTTON_LEFTSTICK},
694 {Settings::NativeButton::RStick, SDL_CONTROLLER_BUTTON_RIGHTSTICK},
695 {Settings::NativeButton::L, SDL_CONTROLLER_BUTTON_LEFTSHOULDER},
696 {Settings::NativeButton::R, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER},
697 {Settings::NativeButton::Plus, SDL_CONTROLLER_BUTTON_START},
698 {Settings::NativeButton::Minus, SDL_CONTROLLER_BUTTON_BACK},
699 {Settings::NativeButton::DLeft, SDL_CONTROLLER_BUTTON_DPAD_LEFT},
700 {Settings::NativeButton::DUp, SDL_CONTROLLER_BUTTON_DPAD_UP},
701 {Settings::NativeButton::DRight, SDL_CONTROLLER_BUTTON_DPAD_RIGHT},
702 {Settings::NativeButton::DDown, SDL_CONTROLLER_BUTTON_DPAD_DOWN},
703 {Settings::NativeButton::SL, SDL_CONTROLLER_BUTTON_LEFTSHOULDER},
704 {Settings::NativeButton::SR, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER},
705 {Settings::NativeButton::Home, SDL_CONTROLLER_BUTTON_GUIDE},
706 }};
707
708 // Add the missing bindings for ZL/ZR
709 using ZBindings =
710 std::array<std::pair<Settings::NativeButton::Values, SDL_GameControllerAxis>, 2>;
711 static constexpr ZBindings switch_to_sdl_axis{{
712 {Settings::NativeButton::ZL, SDL_CONTROLLER_AXIS_TRIGGERLEFT},
713 {Settings::NativeButton::ZR, SDL_CONTROLLER_AXIS_TRIGGERRIGHT},
714 }};
715
716 ButtonMapping mapping;
717 mapping.reserve(switch_to_sdl_button.size() + switch_to_sdl_axis.size());
718
706 for (const auto& [switch_button, sdl_button] : switch_to_sdl_button) { 719 for (const auto& [switch_button, sdl_button] : switch_to_sdl_button) {
707 const auto& binding = SDL_GameControllerGetBindForButton(controller, sdl_button); 720 const auto& binding = SDL_GameControllerGetBindForButton(controller, sdl_button);
708 mapping[switch_button] = 721 mapping.insert_or_assign(
709 BuildParamPackageForBinding(joystick->GetPort(), joystick->GetGUID(), binding); 722 switch_button,
723 BuildParamPackageForBinding(joystick->GetPort(), joystick->GetGUID(), binding));
710 } 724 }
711
712 // Add the missing bindings for ZL/ZR
713 std::unordered_map<Settings::NativeButton::Values, SDL_GameControllerAxis> switch_to_sdl_axis =
714 {
715 {Settings::NativeButton::ZL, SDL_CONTROLLER_AXIS_TRIGGERLEFT},
716 {Settings::NativeButton::ZR, SDL_CONTROLLER_AXIS_TRIGGERRIGHT},
717 };
718 for (const auto& [switch_button, sdl_axis] : switch_to_sdl_axis) { 725 for (const auto& [switch_button, sdl_axis] : switch_to_sdl_axis) {
719 const auto& binding = SDL_GameControllerGetBindForAxis(controller, sdl_axis); 726 const auto& binding = SDL_GameControllerGetBindForAxis(controller, sdl_axis);
720 mapping[switch_button] = 727 mapping.insert_or_assign(
721 BuildParamPackageForBinding(joystick->GetPort(), joystick->GetGUID(), binding); 728 switch_button,
729 BuildParamPackageForBinding(joystick->GetPort(), joystick->GetGUID(), binding));
722 } 730 }
723 731
724 return mapping; 732 return mapping;
@@ -729,8 +737,8 @@ AnalogMapping SDLState::GetAnalogMappingForDevice(const Common::ParamPackage& pa
729 return {}; 737 return {};
730 } 738 }
731 const auto joystick = GetSDLJoystickByGUID(params.Get("guid", ""), params.Get("port", 0)); 739 const auto joystick = GetSDLJoystickByGUID(params.Get("guid", ""), params.Get("port", 0));
732 auto controller = joystick->GetSDLGameController(); 740 auto* controller = joystick->GetSDLGameController();
733 if (!controller) { 741 if (controller == nullptr) {
734 return {}; 742 return {};
735 } 743 }
736 744
@@ -739,16 +747,18 @@ AnalogMapping SDLState::GetAnalogMappingForDevice(const Common::ParamPackage& pa
739 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTX); 747 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTX);
740 const auto& binding_left_y = 748 const auto& binding_left_y =
741 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTY); 749 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTY);
742 mapping[Settings::NativeAnalog::LStick] = 750 mapping.insert_or_assign(Settings::NativeAnalog::LStick,
743 BuildParamPackageForAnalog(joystick->GetPort(), joystick->GetGUID(), 751 BuildParamPackageForAnalog(joystick->GetPort(), joystick->GetGUID(),
744 binding_left_x.value.axis, binding_left_y.value.axis); 752 binding_left_x.value.axis,
753 binding_left_y.value.axis));
745 const auto& binding_right_x = 754 const auto& binding_right_x =
746 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTX); 755 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTX);
747 const auto& binding_right_y = 756 const auto& binding_right_y =
748 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTY); 757 SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTY);
749 mapping[Settings::NativeAnalog::RStick] = 758 mapping.insert_or_assign(Settings::NativeAnalog::RStick,
750 BuildParamPackageForAnalog(joystick->GetPort(), joystick->GetGUID(), 759 BuildParamPackageForAnalog(joystick->GetPort(), joystick->GetGUID(),
751 binding_right_x.value.axis, binding_right_y.value.axis); 760 binding_right_x.value.axis,
761 binding_right_y.value.axis));
752 return mapping; 762 return mapping;
753} 763}
754 764
@@ -784,7 +794,7 @@ public:
784 } 794 }
785 return {}; 795 return {};
786 } 796 }
787 std::optional<Common::ParamPackage> FromEvent(const SDL_Event& event) { 797 [[nodiscard]] std::optional<Common::ParamPackage> FromEvent(const SDL_Event& event) const {
788 switch (event.type) { 798 switch (event.type) {
789 case SDL_JOYAXISMOTION: 799 case SDL_JOYAXISMOTION:
790 if (std::abs(event.jaxis.value / 32767.0) < 0.5) { 800 if (std::abs(event.jaxis.value / 32767.0) < 0.5) {
@@ -795,7 +805,7 @@ public:
795 case SDL_JOYHATMOTION: 805 case SDL_JOYHATMOTION:
796 return {SDLEventToButtonParamPackage(state, event)}; 806 return {SDLEventToButtonParamPackage(state, event)};
797 } 807 }
798 return {}; 808 return std::nullopt;
799 } 809 }
800}; 810};
801 811
diff --git a/src/input_common/touch_from_button.cpp b/src/input_common/touch_from_button.cpp
new file mode 100644
index 000000000..98da0ef1a
--- /dev/null
+++ b/src/input_common/touch_from_button.cpp
@@ -0,0 +1,50 @@
1// Copyright 2020 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/frontend/framebuffer_layout.h"
6#include "core/settings.h"
7#include "input_common/touch_from_button.h"
8
9namespace InputCommon {
10
11class TouchFromButtonDevice final : public Input::TouchDevice {
12public:
13 TouchFromButtonDevice() {
14 for (const auto& config_entry :
15 Settings::values.touch_from_button_maps[Settings::values.touch_from_button_map_index]
16 .buttons) {
17 const Common::ParamPackage package{config_entry};
18 map.emplace_back(
19 Input::CreateDevice<Input::ButtonDevice>(config_entry),
20 std::clamp(package.Get("x", 0), 0, static_cast<int>(Layout::ScreenUndocked::Width)),
21 std::clamp(package.Get("y", 0), 0,
22 static_cast<int>(Layout::ScreenUndocked::Height)));
23 }
24 }
25
26 std::tuple<float, float, bool> GetStatus() const override {
27 for (const auto& m : map) {
28 const bool state = std::get<0>(m)->GetStatus();
29 if (state) {
30 const float x = static_cast<float>(std::get<1>(m)) /
31 static_cast<int>(Layout::ScreenUndocked::Width);
32 const float y = static_cast<float>(std::get<2>(m)) /
33 static_cast<int>(Layout::ScreenUndocked::Height);
34 return {x, y, true};
35 }
36 }
37 return {};
38 }
39
40private:
41 // A vector of the mapped button, its x and its y-coordinate
42 std::vector<std::tuple<std::unique_ptr<Input::ButtonDevice>, int, int>> map;
43};
44
45std::unique_ptr<Input::TouchDevice> TouchFromButtonFactory::Create(
46 const Common::ParamPackage& params) {
47 return std::make_unique<TouchFromButtonDevice>();
48}
49
50} // namespace InputCommon
diff --git a/src/input_common/touch_from_button.h b/src/input_common/touch_from_button.h
new file mode 100644
index 000000000..8b4d1aa96
--- /dev/null
+++ b/src/input_common/touch_from_button.h
@@ -0,0 +1,23 @@
1// Copyright 2020 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <memory>
8#include "core/frontend/input.h"
9
10namespace InputCommon {
11
12/**
13 * A touch device factory that takes a list of button devices and combines them into a touch device.
14 */
15class TouchFromButtonFactory final : public Input::Factory<Input::TouchDevice> {
16public:
17 /**
18 * Creates a touch device from a list of button devices
19 */
20 std::unique_ptr<Input::TouchDevice> Create(const Common::ParamPackage& params) override;
21};
22
23} // namespace InputCommon