summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar german772021-09-20 19:43:16 -0500
committerGravatar Narr the Reg2021-11-24 20:30:23 -0600
commitc3f54ff2329d79bdbb273678b5123cf0b1cd090c (patch)
tree671542346e2f692b2cef8dc4da6ccdb0b9e21dc2 /src
parentyuzu_cmd: Use new input (diff)
downloadyuzu-c3f54ff2329d79bdbb273678b5123cf0b1cd090c.tar.gz
yuzu-c3f54ff2329d79bdbb273678b5123cf0b1cd090c.tar.xz
yuzu-c3f54ff2329d79bdbb273678b5123cf0b1cd090c.zip
core/hid: Add emulated controllers
Diffstat (limited to 'src')
-rw-r--r--src/core/CMakeLists.txt8
-rw-r--r--src/core/hid/emulated_console.cpp208
-rw-r--r--src/core/hid/emulated_console.h142
-rw-r--r--src/core/hid/emulated_controller.cpp745
-rw-r--r--src/core/hid/emulated_controller.h232
-rw-r--r--src/core/hid/emulated_devices.cpp349
-rw-r--r--src/core/hid/emulated_devices.h137
-rw-r--r--src/core/hid/hid_core.cpp144
-rw-r--r--src/core/hid/hid_core.h60
9 files changed, 2025 insertions, 0 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index eddf455c0..09163fab9 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -135,6 +135,14 @@ add_library(core STATIC
135 frontend/input.h 135 frontend/input.h
136 hardware_interrupt_manager.cpp 136 hardware_interrupt_manager.cpp
137 hardware_interrupt_manager.h 137 hardware_interrupt_manager.h
138 hid/emulated_console.cpp
139 hid/emulated_console.h
140 hid/emulated_controller.cpp
141 hid/emulated_controller.h
142 hid/emulated_devices.cpp
143 hid/emulated_devices.h
144 hid/hid_core.cpp
145 hid/hid_core.h
138 hid/hid_types.h 146 hid/hid_types.h
139 hid/input_converter.cpp 147 hid/input_converter.cpp
140 hid/input_converter.h 148 hid/input_converter.h
diff --git a/src/core/hid/emulated_console.cpp b/src/core/hid/emulated_console.cpp
new file mode 100644
index 000000000..c65d05041
--- /dev/null
+++ b/src/core/hid/emulated_console.cpp
@@ -0,0 +1,208 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included
4
5#include <fmt/format.h>
6
7#include "core/hid/emulated_console.h"
8#include "core/hid/input_converter.h"
9
10namespace Core::HID {
11EmulatedConsole::EmulatedConsole() {}
12
13EmulatedConsole::~EmulatedConsole() = default;
14
15void EmulatedConsole::ReloadFromSettings() {
16 // Using first motion device from player 1. No need to assign a special config at the moment
17 const auto& player = Settings::values.players.GetValue()[0];
18 motion_params = Common::ParamPackage(player.motions[0]);
19
20 ReloadInput();
21}
22
23void EmulatedConsole::ReloadInput() {
24 motion_devices = Input::CreateDevice<Input::InputDevice>(motion_params);
25 if (motion_devices) {
26 Input::InputCallback motion_callback{
27 [this](Input::CallbackStatus callback) { SetMotion(callback); }};
28 motion_devices->SetCallback(motion_callback);
29 }
30
31 // TODO: Fix this mess
32 std::size_t index = 0;
33 const std::string mouse_device_string =
34 fmt::format("engine:mouse,axis_x:10,axis_y:11,button:{}", index);
35 touch_devices[index] = Input::CreateDeviceFromString<Input::InputDevice>(mouse_device_string);
36 Input::InputCallback trigger_callbackk{
37 [this, index](Input::CallbackStatus callback) { SetTouch(callback, index); }};
38 touch_devices[index]->SetCallback(trigger_callbackk);
39
40 index++;
41 const auto button_index =
42 static_cast<u64>(Settings::values.touch_from_button_map_index.GetValue());
43 const auto& touch_buttons = Settings::values.touch_from_button_maps[button_index].buttons;
44 for (const auto& config_entry : touch_buttons) {
45 Common::ParamPackage params{config_entry};
46 Common::ParamPackage touch_button_params;
47 const int x = params.Get("x", 0);
48 const int y = params.Get("y", 0);
49 params.Erase("x");
50 params.Erase("y");
51 touch_button_params.Set("engine", "touch_from_button");
52 touch_button_params.Set("button", params.Serialize());
53 touch_button_params.Set("x", x);
54 touch_button_params.Set("y", y);
55 touch_button_params.Set("touch_id", static_cast<int>(index));
56 LOG_ERROR(Common, "{} ", touch_button_params.Serialize());
57 touch_devices[index] =
58 Input::CreateDeviceFromString<Input::InputDevice>(touch_button_params.Serialize());
59 if (!touch_devices[index]) {
60 continue;
61 }
62
63 Input::InputCallback trigger_callback{
64 [this, index](Input::CallbackStatus callback) { SetTouch(callback, index); }};
65 touch_devices[index]->SetCallback(trigger_callback);
66 index++;
67 }
68}
69
70void EmulatedConsole::UnloadInput() {
71 motion_devices.reset();
72 for (auto& touch : touch_devices) {
73 touch.reset();
74 }
75}
76
77void EmulatedConsole::EnableConfiguration() {
78 is_configuring = true;
79 SaveCurrentConfig();
80}
81
82void EmulatedConsole::DisableConfiguration() {
83 is_configuring = false;
84}
85
86bool EmulatedConsole::IsConfiguring() const {
87 return is_configuring;
88}
89
90void EmulatedConsole::SaveCurrentConfig() {
91 if (!is_configuring) {
92 return;
93 }
94}
95
96void EmulatedConsole::RestoreConfig() {
97 if (!is_configuring) {
98 return;
99 }
100 ReloadFromSettings();
101}
102
103Common::ParamPackage EmulatedConsole::GetMotionParam() const {
104 return motion_params;
105}
106
107void EmulatedConsole::SetMotionParam(Common::ParamPackage param) {
108 motion_params = param;
109 ReloadInput();
110}
111
112void EmulatedConsole::SetMotion(Input::CallbackStatus callback) {
113 std::lock_guard lock{mutex};
114 auto& raw_status = console.motion_values.raw_status;
115 auto& emulated = console.motion_values.emulated;
116
117 raw_status = TransformToMotion(callback);
118 emulated.SetAcceleration(Common::Vec3f{
119 raw_status.accel.x.value,
120 raw_status.accel.y.value,
121 raw_status.accel.z.value,
122 });
123 emulated.SetGyroscope(Common::Vec3f{
124 raw_status.gyro.x.value,
125 raw_status.gyro.y.value,
126 raw_status.gyro.z.value,
127 });
128 emulated.UpdateRotation(raw_status.delta_timestamp);
129 emulated.UpdateOrientation(raw_status.delta_timestamp);
130
131 if (is_configuring) {
132 TriggerOnChange(ConsoleTriggerType::Motion);
133 return;
134 }
135
136 auto& motion = console.motion_state;
137 motion.accel = emulated.GetAcceleration();
138 motion.gyro = emulated.GetGyroscope();
139 motion.rotation = emulated.GetGyroscope();
140 motion.orientation = emulated.GetOrientation();
141 motion.quaternion = emulated.GetQuaternion();
142 motion.is_at_rest = emulated.IsMoving(motion_sensitivity);
143
144 TriggerOnChange(ConsoleTriggerType::Motion);
145}
146
147void EmulatedConsole::SetTouch(Input::CallbackStatus callback, [[maybe_unused]] std::size_t index) {
148 if (index >= console.touch_values.size()) {
149 return;
150 }
151 std::lock_guard lock{mutex};
152
153 console.touch_values[index] = TransformToTouch(callback);
154
155 if (is_configuring) {
156 TriggerOnChange(ConsoleTriggerType::Touch);
157 return;
158 }
159
160 console.touch_state[index] = {
161 .position = {console.touch_values[index].x.value, console.touch_values[index].y.value},
162 .id = console.touch_values[index].id,
163 .pressed = console.touch_values[index].pressed.value,
164 };
165
166 TriggerOnChange(ConsoleTriggerType::Touch);
167}
168
169ConsoleMotionValues EmulatedConsole::GetMotionValues() const {
170 return console.motion_values;
171}
172
173TouchValues EmulatedConsole::GetTouchValues() const {
174 return console.touch_values;
175}
176
177ConsoleMotion EmulatedConsole::GetMotion() const {
178 return console.motion_state;
179}
180
181TouchFingerState EmulatedConsole::GetTouch() const {
182 return console.touch_state;
183}
184
185void EmulatedConsole::TriggerOnChange(ConsoleTriggerType type) {
186 for (const std::pair<int, ConsoleUpdateCallback> poller_pair : callback_list) {
187 const ConsoleUpdateCallback& poller = poller_pair.second;
188 if (poller.on_change) {
189 poller.on_change(type);
190 }
191 }
192}
193
194int EmulatedConsole::SetCallback(ConsoleUpdateCallback update_callback) {
195 std::lock_guard lock{mutex};
196 callback_list.insert_or_assign(last_callback_key, update_callback);
197 return last_callback_key++;
198}
199
200void EmulatedConsole::DeleteCallback(int key) {
201 std::lock_guard lock{mutex};
202 if (!callback_list.contains(key)) {
203 LOG_ERROR(Input, "Tried to delete non-existent callback {}", key);
204 return;
205 }
206 callback_list.erase(key);
207}
208} // namespace Core::HID
diff --git a/src/core/hid/emulated_console.h b/src/core/hid/emulated_console.h
new file mode 100644
index 000000000..d9e275042
--- /dev/null
+++ b/src/core/hid/emulated_console.h
@@ -0,0 +1,142 @@
1// Copyright 2021 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 <functional>
8#include <mutex>
9#include <unordered_map>
10
11#include "common/input.h"
12#include "common/param_package.h"
13#include "common/point.h"
14#include "common/quaternion.h"
15#include "common/settings.h"
16#include "common/vector_math.h"
17#include "core/hid/hid_types.h"
18#include "core/hid/motion_input.h"
19
20namespace Core::HID {
21
22struct ConsoleMotionInfo {
23 Input::MotionStatus raw_status;
24 MotionInput emulated{};
25};
26
27using ConsoleMotionDevices = std::unique_ptr<Input::InputDevice>;
28using TouchDevices = std::array<std::unique_ptr<Input::InputDevice>, 16>;
29
30using ConsoleMotionParams = Common::ParamPackage;
31using TouchParams = std::array<Common::ParamPackage, 16>;
32
33using ConsoleMotionValues = ConsoleMotionInfo;
34using TouchValues = std::array<Input::TouchStatus, 16>;
35
36struct TouchFinger {
37 u64_le last_touch{};
38 Common::Point<float> position{};
39 u32_le id{};
40 bool pressed{};
41 Core::HID::TouchAttribute attribute{};
42};
43
44struct ConsoleMotion {
45 bool is_at_rest{};
46 Common::Vec3f accel{};
47 Common::Vec3f gyro{};
48 Common::Vec3f rotation{};
49 std::array<Common::Vec3f, 3> orientation{};
50 Common::Quaternion<f32> quaternion{};
51};
52
53using TouchFingerState = std::array<TouchFinger, 16>;
54
55struct ConsoleStatus {
56 // Data from input_common
57 ConsoleMotionValues motion_values{};
58 TouchValues touch_values{};
59
60 // Data for Nintendo devices;
61 ConsoleMotion motion_state{};
62 TouchFingerState touch_state{};
63};
64
65enum class ConsoleTriggerType {
66 Motion,
67 Touch,
68 All,
69};
70
71struct ConsoleUpdateCallback {
72 std::function<void(ConsoleTriggerType)> on_change;
73};
74
75class EmulatedConsole {
76public:
77 /**
78 * TODO: Write description
79 *
80 * @param npad_id_type
81 */
82 explicit EmulatedConsole();
83 ~EmulatedConsole();
84
85 YUZU_NON_COPYABLE(EmulatedConsole);
86 YUZU_NON_MOVEABLE(EmulatedConsole);
87
88 void ReloadFromSettings();
89 void ReloadInput();
90 void UnloadInput();
91
92 void EnableConfiguration();
93 void DisableConfiguration();
94 bool IsConfiguring() const;
95 void SaveCurrentConfig();
96 void RestoreConfig();
97
98 Common::ParamPackage GetMotionParam() const;
99
100 void SetMotionParam(Common::ParamPackage param);
101
102 ConsoleMotionValues GetMotionValues() const;
103 TouchValues GetTouchValues() const;
104
105 ConsoleMotion GetMotion() const;
106 TouchFingerState GetTouch() const;
107
108 int SetCallback(ConsoleUpdateCallback update_callback);
109 void DeleteCallback(int key);
110
111private:
112 /**
113 * Sets the status of a button. Applies toggle properties to the output.
114 *
115 * @param A CallbackStatus and a button index number
116 */
117 void SetMotion(Input::CallbackStatus callback);
118 void SetTouch(Input::CallbackStatus callback, std::size_t index);
119
120 /**
121 * Triggers a callback that something has changed
122 *
123 * @param Input type of the trigger
124 */
125 void TriggerOnChange(ConsoleTriggerType type);
126
127 bool is_configuring{false};
128 f32 motion_sensitivity{0.01f};
129
130 ConsoleMotionParams motion_params;
131 TouchParams touch_params;
132
133 ConsoleMotionDevices motion_devices;
134 TouchDevices touch_devices;
135
136 mutable std::mutex mutex;
137 std::unordered_map<int, ConsoleUpdateCallback> callback_list;
138 int last_callback_key = 0;
139 ConsoleStatus console;
140};
141
142} // namespace Core::HID
diff --git a/src/core/hid/emulated_controller.cpp b/src/core/hid/emulated_controller.cpp
new file mode 100644
index 000000000..4eb5d99bc
--- /dev/null
+++ b/src/core/hid/emulated_controller.cpp
@@ -0,0 +1,745 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included
4
5#include <fmt/format.h>
6
7#include "core/hid/emulated_controller.h"
8#include "core/hid/input_converter.h"
9
10namespace Core::HID {
11constexpr s32 HID_JOYSTICK_MAX = 0x7fff;
12constexpr s32 HID_TRIGGER_MAX = 0x7fff;
13
14EmulatedController::EmulatedController(NpadIdType npad_id_type_) : npad_id_type(npad_id_type_) {}
15
16EmulatedController::~EmulatedController() = default;
17
18NpadType EmulatedController::MapSettingsTypeToNPad(Settings::ControllerType type) {
19 switch (type) {
20 case Settings::ControllerType::ProController:
21 return NpadType::ProController;
22 case Settings::ControllerType::DualJoyconDetached:
23 return NpadType::JoyconDual;
24 case Settings::ControllerType::LeftJoycon:
25 return NpadType::JoyconLeft;
26 case Settings::ControllerType::RightJoycon:
27 return NpadType::JoyconRight;
28 case Settings::ControllerType::Handheld:
29 return NpadType::Handheld;
30 case Settings::ControllerType::GameCube:
31 return NpadType::GameCube;
32 default:
33 return NpadType::ProController;
34 }
35}
36
37Settings::ControllerType EmulatedController::MapNPadToSettingsType(NpadType type) {
38 switch (type) {
39 case NpadType::ProController:
40 return Settings::ControllerType::ProController;
41 case NpadType::JoyconDual:
42 return Settings::ControllerType::DualJoyconDetached;
43 case NpadType::JoyconLeft:
44 return Settings::ControllerType::LeftJoycon;
45 case NpadType::JoyconRight:
46 return Settings::ControllerType::RightJoycon;
47 case NpadType::Handheld:
48 return Settings::ControllerType::Handheld;
49 case NpadType::GameCube:
50 return Settings::ControllerType::GameCube;
51 default:
52 return Settings::ControllerType::ProController;
53 }
54}
55
56void EmulatedController::ReloadFromSettings() {
57 const auto player_index = NpadIdTypeToIndex(npad_id_type);
58 const auto& player = Settings::values.players.GetValue()[player_index];
59
60 for (std::size_t index = 0; index < player.buttons.size(); ++index) {
61 button_params[index] = Common::ParamPackage(player.buttons[index]);
62 }
63 for (std::size_t index = 0; index < player.analogs.size(); ++index) {
64 stick_params[index] = Common::ParamPackage(player.analogs[index]);
65 }
66 for (std::size_t index = 0; index < player.motions.size(); ++index) {
67 motion_params[index] = Common::ParamPackage(player.motions[index]);
68 }
69 ReloadInput();
70}
71
72void EmulatedController::ReloadInput() {
73 const auto player_index = NpadIdTypeToIndex(npad_id_type);
74 const auto& player = Settings::values.players.GetValue()[player_index];
75 const auto left_side = button_params[Settings::NativeButton::ZL];
76 const auto right_side = button_params[Settings::NativeButton::ZR];
77
78 std::transform(button_params.begin() + Settings::NativeButton::BUTTON_HID_BEGIN,
79 button_params.begin() + Settings::NativeButton::BUTTON_NS_END,
80 button_devices.begin(), Input::CreateDevice<Input::InputDevice>);
81 std::transform(stick_params.begin() + Settings::NativeAnalog::STICK_HID_BEGIN,
82 stick_params.begin() + Settings::NativeAnalog::STICK_HID_END,
83 stick_devices.begin(), Input::CreateDevice<Input::InputDevice>);
84 std::transform(motion_params.begin() + Settings::NativeMotion::MOTION_HID_BEGIN,
85 motion_params.begin() + Settings::NativeMotion::MOTION_HID_END,
86 motion_devices.begin(), Input::CreateDevice<Input::InputDevice>);
87
88 trigger_devices[0] =
89 Input::CreateDevice<Input::InputDevice>(button_params[Settings::NativeButton::ZL]);
90 trigger_devices[1] =
91 Input::CreateDevice<Input::InputDevice>(button_params[Settings::NativeButton::ZR]);
92
93 controller.colors_state.left = {
94 .body = player.body_color_left,
95 .button = player.button_color_left,
96 };
97
98 controller.colors_state.right = {
99 .body = player.body_color_right,
100 .button = player.button_color_right,
101 };
102
103 controller.colors_state.fullkey = controller.colors_state.left;
104
105 battery_devices[0] = Input::CreateDevice<Input::InputDevice>(left_side);
106 battery_devices[1] = Input::CreateDevice<Input::InputDevice>(right_side);
107
108 for (std::size_t index = 0; index < button_devices.size(); ++index) {
109 if (!button_devices[index]) {
110 continue;
111 }
112 Input::InputCallback button_callback{
113 [this, index](Input::CallbackStatus callback) { SetButton(callback, index); }};
114 button_devices[index]->SetCallback(button_callback);
115 }
116
117 for (std::size_t index = 0; index < stick_devices.size(); ++index) {
118 if (!stick_devices[index]) {
119 continue;
120 }
121 Input::InputCallback stick_callback{
122 [this, index](Input::CallbackStatus callback) { SetStick(callback, index); }};
123 stick_devices[index]->SetCallback(stick_callback);
124 }
125
126 for (std::size_t index = 0; index < trigger_devices.size(); ++index) {
127 if (!trigger_devices[index]) {
128 continue;
129 }
130 Input::InputCallback trigger_callback{
131 [this, index](Input::CallbackStatus callback) { SetTrigger(callback, index); }};
132 trigger_devices[index]->SetCallback(trigger_callback);
133 }
134
135 for (std::size_t index = 0; index < battery_devices.size(); ++index) {
136 if (!battery_devices[index]) {
137 continue;
138 }
139 Input::InputCallback battery_callback{
140 [this, index](Input::CallbackStatus callback) { SetBattery(callback, index); }};
141 battery_devices[index]->SetCallback(battery_callback);
142 }
143
144 for (std::size_t index = 0; index < motion_devices.size(); ++index) {
145 if (!motion_devices[index]) {
146 continue;
147 }
148 Input::InputCallback motion_callback{
149 [this, index](Input::CallbackStatus callback) { SetMotion(callback, index); }};
150 motion_devices[index]->SetCallback(motion_callback);
151 }
152
153 SetNpadType(MapSettingsTypeToNPad(player.controller_type));
154
155 if (player.connected) {
156 Connect();
157 } else {
158 Disconnect();
159 }
160}
161
162void EmulatedController::UnloadInput() {
163 for (auto& button : button_devices) {
164 button.reset();
165 }
166 for (auto& stick : stick_devices) {
167 stick.reset();
168 }
169 for (auto& motion : motion_devices) {
170 motion.reset();
171 }
172 for (auto& trigger : trigger_devices) {
173 trigger.reset();
174 }
175 for (auto& battery : battery_devices) {
176 battery.reset();
177 }
178}
179
180void EmulatedController::EnableConfiguration() {
181 is_configuring = true;
182 SaveCurrentConfig();
183}
184
185void EmulatedController::DisableConfiguration() {
186 is_configuring = false;
187}
188
189bool EmulatedController::IsConfiguring() const {
190 return is_configuring;
191}
192
193void EmulatedController::SaveCurrentConfig() {
194 if (!is_configuring) {
195 return;
196 }
197
198 const auto player_index = NpadIdTypeToIndex(npad_id_type);
199 auto& player = Settings::values.players.GetValue()[player_index];
200
201 for (std::size_t index = 0; index < player.buttons.size(); ++index) {
202 player.buttons[index] = button_params[index].Serialize();
203 }
204 for (std::size_t index = 0; index < player.analogs.size(); ++index) {
205 player.analogs[index] = stick_params[index].Serialize();
206 }
207 for (std::size_t index = 0; index < player.motions.size(); ++index) {
208 player.motions[index] = motion_params[index].Serialize();
209 }
210}
211
212void EmulatedController::RestoreConfig() {
213 if (!is_configuring) {
214 return;
215 }
216 ReloadFromSettings();
217}
218
219std::vector<Common::ParamPackage> EmulatedController::GetMappedDevices() const {
220 std::vector<Common::ParamPackage> devices;
221 for (const auto& param : button_params) {
222 if (!param.Has("engine")) {
223 continue;
224 }
225 const auto devices_it = std::find_if(
226 devices.begin(), devices.end(), [param](const Common::ParamPackage param_) {
227 return param.Get("engine", "") == param_.Get("engine", "") &&
228 param.Get("guid", "") == param_.Get("guid", "") &&
229 param.Get("port", "") == param_.Get("port", "");
230 });
231 if (devices_it != devices.end()) {
232 continue;
233 }
234 Common::ParamPackage device{};
235 device.Set("engine", param.Get("engine", ""));
236 device.Set("guid", param.Get("guid", ""));
237 device.Set("port", param.Get("port", ""));
238 devices.push_back(device);
239 }
240
241 for (const auto& param : stick_params) {
242 if (!param.Has("engine")) {
243 continue;
244 }
245 if (param.Get("engine", "") == "analog_from_button") {
246 continue;
247 }
248 const auto devices_it = std::find_if(
249 devices.begin(), devices.end(), [param](const Common::ParamPackage param_) {
250 return param.Get("engine", "") == param_.Get("engine", "") &&
251 param.Get("guid", "") == param_.Get("guid", "") &&
252 param.Get("port", "") == param_.Get("port", "");
253 });
254 if (devices_it != devices.end()) {
255 continue;
256 }
257 Common::ParamPackage device{};
258 device.Set("engine", param.Get("engine", ""));
259 device.Set("guid", param.Get("guid", ""));
260 device.Set("port", param.Get("port", ""));
261 devices.push_back(device);
262 }
263 return devices;
264}
265
266Common::ParamPackage EmulatedController::GetButtonParam(std::size_t index) const {
267 if (index >= button_params.size()) {
268 return {};
269 }
270 return button_params[index];
271}
272
273Common::ParamPackage EmulatedController::GetStickParam(std::size_t index) const {
274 if (index >= stick_params.size()) {
275 return {};
276 }
277 return stick_params[index];
278}
279
280Common::ParamPackage EmulatedController::GetMotionParam(std::size_t index) const {
281 if (index >= motion_params.size()) {
282 return {};
283 }
284 return motion_params[index];
285}
286
287void EmulatedController::SetButtonParam(std::size_t index, Common::ParamPackage param) {
288 if (index >= button_params.size()) {
289 return;
290 }
291 button_params[index] = param;
292 ReloadInput();
293}
294
295void EmulatedController::SetStickParam(std::size_t index, Common::ParamPackage param) {
296 if (index >= stick_params.size()) {
297 return;
298 }
299 stick_params[index] = param;
300 ReloadInput();
301}
302
303void EmulatedController::SetMotionParam(std::size_t index, Common::ParamPackage param) {
304 if (index >= motion_params.size()) {
305 return;
306 }
307 motion_params[index] = param;
308 ReloadInput();
309}
310
311void EmulatedController::SetButton(Input::CallbackStatus callback, std::size_t index) {
312 if (index >= controller.button_values.size()) {
313 return;
314 }
315 std::lock_guard lock{mutex};
316 bool value_changed = false;
317 const auto new_status = TransformToButton(callback);
318 auto& current_status = controller.button_values[index];
319 current_status.toggle = new_status.toggle;
320
321 // Update button status with current
322 if (!current_status.toggle) {
323 current_status.locked = false;
324 if (current_status.value != new_status.value) {
325 current_status.value = new_status.value;
326 value_changed = true;
327 }
328 } else {
329 // Toggle button and lock status
330 if (new_status.value && !current_status.locked) {
331 current_status.locked = true;
332 current_status.value = !current_status.value;
333 value_changed = true;
334 }
335
336 // Unlock button ready for next press
337 if (!new_status.value && current_status.locked) {
338 current_status.locked = false;
339 }
340 }
341
342 if (!value_changed) {
343 return;
344 }
345
346 if (is_configuring) {
347 controller.npad_button_state.raw = NpadButton::None;
348 controller.debug_pad_button_state.raw = 0;
349 TriggerOnChange(ControllerTriggerType::Button);
350 return;
351 }
352
353 switch (index) {
354 case Settings::NativeButton::A:
355 controller.npad_button_state.a.Assign(current_status.value);
356 controller.debug_pad_button_state.a.Assign(current_status.value);
357 break;
358 case Settings::NativeButton::B:
359 controller.npad_button_state.b.Assign(current_status.value);
360 controller.debug_pad_button_state.b.Assign(current_status.value);
361 break;
362 case Settings::NativeButton::X:
363 controller.npad_button_state.x.Assign(current_status.value);
364 controller.debug_pad_button_state.x.Assign(current_status.value);
365 break;
366 case Settings::NativeButton::Y:
367 controller.npad_button_state.y.Assign(current_status.value);
368 controller.debug_pad_button_state.y.Assign(current_status.value);
369 break;
370 case Settings::NativeButton::LStick:
371 controller.npad_button_state.stick_l.Assign(current_status.value);
372 break;
373 case Settings::NativeButton::RStick:
374 controller.npad_button_state.stick_r.Assign(current_status.value);
375 break;
376 case Settings::NativeButton::L:
377 controller.npad_button_state.l.Assign(current_status.value);
378 controller.debug_pad_button_state.l.Assign(current_status.value);
379 break;
380 case Settings::NativeButton::R:
381 controller.npad_button_state.r.Assign(current_status.value);
382 controller.debug_pad_button_state.r.Assign(current_status.value);
383 break;
384 case Settings::NativeButton::ZL:
385 controller.npad_button_state.zl.Assign(current_status.value);
386 controller.debug_pad_button_state.zl.Assign(current_status.value);
387 break;
388 case Settings::NativeButton::ZR:
389 controller.npad_button_state.zr.Assign(current_status.value);
390 controller.debug_pad_button_state.zr.Assign(current_status.value);
391 break;
392 case Settings::NativeButton::Plus:
393 controller.npad_button_state.plus.Assign(current_status.value);
394 controller.debug_pad_button_state.plus.Assign(current_status.value);
395 break;
396 case Settings::NativeButton::Minus:
397 controller.npad_button_state.minus.Assign(current_status.value);
398 controller.debug_pad_button_state.minus.Assign(current_status.value);
399 break;
400 case Settings::NativeButton::DLeft:
401 controller.npad_button_state.left.Assign(current_status.value);
402 controller.debug_pad_button_state.d_left.Assign(current_status.value);
403 break;
404 case Settings::NativeButton::DUp:
405 controller.npad_button_state.up.Assign(current_status.value);
406 controller.debug_pad_button_state.d_up.Assign(current_status.value);
407 break;
408 case Settings::NativeButton::DRight:
409 controller.npad_button_state.right.Assign(current_status.value);
410 controller.debug_pad_button_state.d_right.Assign(current_status.value);
411 break;
412 case Settings::NativeButton::DDown:
413 controller.npad_button_state.down.Assign(current_status.value);
414 controller.debug_pad_button_state.d_down.Assign(current_status.value);
415 break;
416 case Settings::NativeButton::SL:
417 controller.npad_button_state.left_sl.Assign(current_status.value);
418 controller.npad_button_state.right_sl.Assign(current_status.value);
419 break;
420 case Settings::NativeButton::SR:
421 controller.npad_button_state.left_sr.Assign(current_status.value);
422 controller.npad_button_state.right_sr.Assign(current_status.value);
423 break;
424 case Settings::NativeButton::Home:
425 case Settings::NativeButton::Screenshot:
426 break;
427 }
428 TriggerOnChange(ControllerTriggerType::Button);
429}
430
431void EmulatedController::SetStick(Input::CallbackStatus callback, std::size_t index) {
432 if (index >= controller.stick_values.size()) {
433 return;
434 }
435 std::lock_guard lock{mutex};
436 controller.stick_values[index] = TransformToStick(callback);
437
438 if (is_configuring) {
439 controller.analog_stick_state.left = {};
440 controller.analog_stick_state.right = {};
441 TriggerOnChange(ControllerTriggerType::Stick);
442 return;
443 }
444
445 const AnalogStickState stick{
446 .x = static_cast<s32>(controller.stick_values[index].x.value * HID_JOYSTICK_MAX),
447 .y = static_cast<s32>(controller.stick_values[index].y.value * HID_JOYSTICK_MAX),
448 };
449
450 switch (index) {
451 case Settings::NativeAnalog::LStick:
452 controller.analog_stick_state.left = stick;
453 controller.npad_button_state.stick_l_left.Assign(controller.stick_values[index].left);
454 controller.npad_button_state.stick_l_up.Assign(controller.stick_values[index].up);
455 controller.npad_button_state.stick_l_right.Assign(controller.stick_values[index].right);
456 controller.npad_button_state.stick_l_down.Assign(controller.stick_values[index].down);
457 break;
458 case Settings::NativeAnalog::RStick:
459 controller.analog_stick_state.right = stick;
460 controller.npad_button_state.stick_r_left.Assign(controller.stick_values[index].left);
461 controller.npad_button_state.stick_r_up.Assign(controller.stick_values[index].up);
462 controller.npad_button_state.stick_r_right.Assign(controller.stick_values[index].right);
463 controller.npad_button_state.stick_r_down.Assign(controller.stick_values[index].down);
464 break;
465 }
466
467 TriggerOnChange(ControllerTriggerType::Stick);
468}
469
470void EmulatedController::SetTrigger(Input::CallbackStatus callback, std::size_t index) {
471 if (index >= controller.trigger_values.size()) {
472 return;
473 }
474 std::lock_guard lock{mutex};
475 controller.trigger_values[index] = TransformToTrigger(callback);
476
477 if (is_configuring) {
478 controller.gc_trigger_state.left = 0;
479 controller.gc_trigger_state.right = 0;
480 TriggerOnChange(ControllerTriggerType::Trigger);
481 return;
482 }
483
484 const auto trigger = controller.trigger_values[index];
485
486 switch (index) {
487 case Settings::NativeTrigger::LTrigger:
488 controller.gc_trigger_state.left = static_cast<s32>(trigger.analog.value * HID_TRIGGER_MAX);
489 controller.npad_button_state.zl.Assign(trigger.pressed);
490 break;
491 case Settings::NativeTrigger::RTrigger:
492 controller.gc_trigger_state.right =
493 static_cast<s32>(trigger.analog.value * HID_TRIGGER_MAX);
494 controller.npad_button_state.zr.Assign(trigger.pressed);
495 break;
496 }
497
498 TriggerOnChange(ControllerTriggerType::Trigger);
499}
500
501void EmulatedController::SetMotion(Input::CallbackStatus callback, std::size_t index) {
502 if (index >= controller.motion_values.size()) {
503 return;
504 }
505 std::lock_guard lock{mutex};
506 auto& raw_status = controller.motion_values[index].raw_status;
507 auto& emulated = controller.motion_values[index].emulated;
508
509 raw_status = TransformToMotion(callback);
510 emulated.SetAcceleration(Common::Vec3f{
511 raw_status.accel.x.value,
512 raw_status.accel.y.value,
513 raw_status.accel.z.value,
514 });
515 emulated.SetGyroscope(Common::Vec3f{
516 raw_status.gyro.x.value,
517 raw_status.gyro.y.value,
518 raw_status.gyro.z.value,
519 });
520 emulated.UpdateRotation(raw_status.delta_timestamp);
521 emulated.UpdateOrientation(raw_status.delta_timestamp);
522
523 if (is_configuring) {
524 TriggerOnChange(ControllerTriggerType::Motion);
525 return;
526 }
527
528 auto& motion = controller.motion_state[index];
529 motion.accel = emulated.GetAcceleration();
530 motion.gyro = emulated.GetGyroscope();
531 motion.rotation = emulated.GetGyroscope();
532 motion.orientation = emulated.GetOrientation();
533 motion.is_at_rest = emulated.IsMoving(motion_sensitivity);
534
535 TriggerOnChange(ControllerTriggerType::Motion);
536}
537
538void EmulatedController::SetBattery(Input::CallbackStatus callback, std::size_t index) {
539 if (index >= controller.battery_values.size()) {
540 return;
541 }
542 std::lock_guard lock{mutex};
543 controller.battery_values[index] = TransformToBattery(callback);
544
545 if (is_configuring) {
546 TriggerOnChange(ControllerTriggerType::Battery);
547 return;
548 }
549
550 bool is_charging = false;
551 bool is_powered = false;
552 BatteryLevel battery_level = 0;
553 switch (controller.battery_values[index]) {
554 case Input::BatteryLevel::Charging:
555 is_charging = true;
556 is_powered = true;
557 battery_level = 6;
558 break;
559 case Input::BatteryLevel::Medium:
560 battery_level = 6;
561 break;
562 case Input::BatteryLevel::Low:
563 battery_level = 4;
564 break;
565 case Input::BatteryLevel::Critical:
566 battery_level = 2;
567 break;
568 case Input::BatteryLevel::Empty:
569 battery_level = 0;
570 break;
571 case Input::BatteryLevel::Full:
572 default:
573 is_powered = true;
574 battery_level = 8;
575 break;
576 }
577
578 switch (index) {
579 case 0:
580 controller.battery_state.left = {
581 .is_powered = is_powered,
582 .is_charging = is_charging,
583 .battery_level = battery_level,
584 };
585 break;
586 case 1:
587 controller.battery_state.right = {
588 .is_powered = is_powered,
589 .is_charging = is_charging,
590 .battery_level = battery_level,
591 };
592 break;
593 case 2:
594 controller.battery_state.dual = {
595 .is_powered = is_powered,
596 .is_charging = is_charging,
597 .battery_level = battery_level,
598 };
599 break;
600 }
601 TriggerOnChange(ControllerTriggerType::Battery);
602}
603
604bool EmulatedController::SetVibration([[maybe_unused]] std::size_t device_index,
605 [[maybe_unused]] VibrationValue vibration) {
606 return false;
607}
608
609int EmulatedController::TestVibration(std::size_t device_index) {
610 return 1;
611}
612
613void EmulatedController::Connect() {
614 std::lock_guard lock{mutex};
615 if (is_connected) {
616 LOG_WARNING(Service_HID, "Tried to turn on a connected controller {}", npad_id_type);
617 return;
618 }
619 is_connected = true;
620 TriggerOnChange(ControllerTriggerType::Connected);
621}
622
623void EmulatedController::Disconnect() {
624 std::lock_guard lock{mutex};
625 if (!is_connected) {
626 LOG_WARNING(Service_HID, "Tried to turn off a disconnected controller {}", npad_id_type);
627 return;
628 }
629 is_connected = false;
630 TriggerOnChange(ControllerTriggerType::Disconnected);
631}
632
633bool EmulatedController::IsConnected() const {
634 return is_connected;
635}
636
637bool EmulatedController::IsVibrationEnabled() const {
638 return is_vibration_enabled;
639}
640
641NpadIdType EmulatedController::GetNpadIdType() const {
642 return npad_id_type;
643}
644
645NpadType EmulatedController::GetNpadType() const {
646 return npad_type;
647}
648
649void EmulatedController::SetNpadType(NpadType npad_type_) {
650 std::lock_guard lock{mutex};
651 if (npad_type == npad_type_) {
652 return;
653 }
654 npad_type = npad_type_;
655 TriggerOnChange(ControllerTriggerType::Type);
656}
657
658ButtonValues EmulatedController::GetButtonsValues() const {
659 return controller.button_values;
660}
661
662SticksValues EmulatedController::GetSticksValues() const {
663 return controller.stick_values;
664}
665
666TriggerValues EmulatedController::GetTriggersValues() const {
667 return controller.trigger_values;
668}
669
670ControllerMotionValues EmulatedController::GetMotionValues() const {
671 return controller.motion_values;
672}
673
674ColorValues EmulatedController::GetColorsValues() const {
675 return controller.color_values;
676}
677
678BatteryValues EmulatedController::GetBatteryValues() const {
679 return controller.battery_values;
680}
681
682NpadButtonState EmulatedController::GetNpadButtons() const {
683 if (is_configuring) {
684 return {};
685 }
686 return controller.npad_button_state;
687}
688
689DebugPadButton EmulatedController::GetDebugPadButtons() const {
690 if (is_configuring) {
691 return {};
692 }
693 return controller.debug_pad_button_state;
694}
695
696AnalogSticks EmulatedController::GetSticks() const {
697 if (is_configuring) {
698 return {};
699 }
700 return controller.analog_stick_state;
701}
702
703NpadGcTriggerState EmulatedController::GetTriggers() const {
704 if (is_configuring) {
705 return {};
706 }
707 return controller.gc_trigger_state;
708}
709
710MotionState EmulatedController::GetMotions() const {
711 return controller.motion_state;
712}
713
714ControllerColors EmulatedController::GetColors() const {
715 return controller.colors_state;
716}
717
718BatteryLevelState EmulatedController::GetBattery() const {
719 return controller.battery_state;
720}
721
722void EmulatedController::TriggerOnChange(ControllerTriggerType type) {
723 for (const std::pair<int, ControllerUpdateCallback> poller_pair : callback_list) {
724 const ControllerUpdateCallback& poller = poller_pair.second;
725 if (poller.on_change) {
726 poller.on_change(type);
727 }
728 }
729}
730
731int EmulatedController::SetCallback(ControllerUpdateCallback update_callback) {
732 std::lock_guard lock{mutex};
733 callback_list.insert_or_assign(last_callback_key, update_callback);
734 return last_callback_key++;
735}
736
737void EmulatedController::DeleteCallback(int key) {
738 std::lock_guard lock{mutex};
739 if (!callback_list.contains(key)) {
740 LOG_ERROR(Input, "Tried to delete non-existent callback {}", key);
741 return;
742 }
743 callback_list.erase(key);
744}
745} // namespace Core::HID
diff --git a/src/core/hid/emulated_controller.h b/src/core/hid/emulated_controller.h
new file mode 100644
index 000000000..94db9b00b
--- /dev/null
+++ b/src/core/hid/emulated_controller.h
@@ -0,0 +1,232 @@
1// Copyright 2021 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 <functional>
8#include <mutex>
9#include <unordered_map>
10
11#include "common/input.h"
12#include "common/param_package.h"
13#include "common/point.h"
14#include "common/quaternion.h"
15#include "common/settings.h"
16#include "common/vector_math.h"
17#include "core/hid/hid_types.h"
18#include "core/hid/motion_input.h"
19
20namespace Core::HID {
21
22struct ControllerMotionInfo {
23 Input::MotionStatus raw_status;
24 MotionInput emulated{};
25};
26
27using ButtonDevices =
28 std::array<std::unique_ptr<Input::InputDevice>, Settings::NativeButton::NumButtons>;
29using StickDevices =
30 std::array<std::unique_ptr<Input::InputDevice>, Settings::NativeAnalog::NumAnalogs>;
31using ControllerMotionDevices =
32 std::array<std::unique_ptr<Input::InputDevice>, Settings::NativeMotion::NumMotions>;
33using TriggerDevices =
34 std::array<std::unique_ptr<Input::InputDevice>, Settings::NativeTrigger::NumTriggers>;
35using BatteryDevices = std::array<std::unique_ptr<Input::InputDevice>, 2>;
36
37using ButtonParams = std::array<Common::ParamPackage, Settings::NativeButton::NumButtons>;
38using StickParams = std::array<Common::ParamPackage, Settings::NativeAnalog::NumAnalogs>;
39using ControllerMotionParams = std::array<Common::ParamPackage, Settings::NativeMotion::NumMotions>;
40using TriggerParams = std::array<Common::ParamPackage, Settings::NativeTrigger::NumTriggers>;
41using BatteryParams = std::array<Common::ParamPackage, 2>;
42
43using ButtonValues = std::array<Input::ButtonStatus, Settings::NativeButton::NumButtons>;
44using SticksValues = std::array<Input::StickStatus, Settings::NativeAnalog::NumAnalogs>;
45using TriggerValues = std::array<Input::TriggerStatus, Settings::NativeTrigger::NumTriggers>;
46using ControllerMotionValues = std::array<ControllerMotionInfo, Settings::NativeMotion::NumMotions>;
47using ColorValues = std::array<Input::BodyColorStatus, 3>;
48using BatteryValues = std::array<Input::BatteryStatus, 3>;
49using VibrationValues = std::array<Input::VibrationStatus, 2>;
50
51struct AnalogSticks {
52 AnalogStickState left;
53 AnalogStickState right;
54};
55
56struct ControllerColors {
57 NpadControllerColor fullkey;
58 NpadControllerColor left;
59 NpadControllerColor right;
60};
61
62struct BatteryLevelState {
63 NpadPowerInfo dual;
64 NpadPowerInfo left;
65 NpadPowerInfo right;
66};
67
68struct ControllerMotion {
69 bool is_at_rest;
70 Common::Vec3f accel{};
71 Common::Vec3f gyro{};
72 Common::Vec3f rotation{};
73 std::array<Common::Vec3f, 3> orientation{};
74};
75
76using MotionState = std::array<ControllerMotion, 2>;
77
78struct ControllerStatus {
79 // Data from input_common
80 ButtonValues button_values{};
81 SticksValues stick_values{};
82 ControllerMotionValues motion_values{};
83 TriggerValues trigger_values{};
84 ColorValues color_values{};
85 BatteryValues battery_values{};
86 VibrationValues vibration_values{};
87
88 // Data for Nintendo devices
89 NpadButtonState npad_button_state{};
90 DebugPadButton debug_pad_button_state{};
91 AnalogSticks analog_stick_state{};
92 MotionState motion_state{};
93 NpadGcTriggerState gc_trigger_state{};
94 ControllerColors colors_state{};
95 BatteryLevelState battery_state{};
96};
97enum class ControllerTriggerType {
98 Button,
99 Stick,
100 Trigger,
101 Motion,
102 Color,
103 Battery,
104 Vibration,
105 Connected,
106 Disconnected,
107 Type,
108 All,
109};
110
111struct ControllerUpdateCallback {
112 std::function<void(ControllerTriggerType)> on_change;
113};
114
115class EmulatedController {
116public:
117 /**
118 * TODO: Write description
119 *
120 * @param npad_id_type
121 */
122 explicit EmulatedController(NpadIdType npad_id_type_);
123 ~EmulatedController();
124
125 YUZU_NON_COPYABLE(EmulatedController);
126 YUZU_NON_MOVEABLE(EmulatedController);
127
128 static NpadType MapSettingsTypeToNPad(Settings::ControllerType type);
129 static Settings::ControllerType MapNPadToSettingsType(NpadType type);
130
131 /// Gets the NpadIdType for this controller.
132 NpadIdType GetNpadIdType() const;
133
134 /// Sets the NpadType for this controller.
135 void SetNpadType(NpadType npad_type_);
136
137 /// Gets the NpadType for this controller.
138 NpadType GetNpadType() const;
139
140 void Connect();
141 void Disconnect();
142
143 bool IsConnected() const;
144 bool IsVibrationEnabled() const;
145
146 void ReloadFromSettings();
147 void ReloadInput();
148 void UnloadInput();
149
150 void EnableConfiguration();
151 void DisableConfiguration();
152 bool IsConfiguring() const;
153 void SaveCurrentConfig();
154 void RestoreConfig();
155
156 std::vector<Common::ParamPackage> GetMappedDevices() const;
157
158 Common::ParamPackage GetButtonParam(std::size_t index) const;
159 Common::ParamPackage GetStickParam(std::size_t index) const;
160 Common::ParamPackage GetMotionParam(std::size_t index) const;
161
162 void SetButtonParam(std::size_t index, Common::ParamPackage param);
163 void SetStickParam(std::size_t index, Common::ParamPackage param);
164 void SetMotionParam(std::size_t index, Common::ParamPackage param);
165
166 ButtonValues GetButtonsValues() const;
167 SticksValues GetSticksValues() const;
168 TriggerValues GetTriggersValues() const;
169 ControllerMotionValues GetMotionValues() const;
170 ColorValues GetColorsValues() const;
171 BatteryValues GetBatteryValues() const;
172
173 NpadButtonState GetNpadButtons() const;
174 DebugPadButton GetDebugPadButtons() const;
175 AnalogSticks GetSticks() const;
176 NpadGcTriggerState GetTriggers() const;
177 MotionState GetMotions() const;
178 ControllerColors GetColors() const;
179 BatteryLevelState GetBattery() const;
180
181 bool SetVibration(std::size_t device_index, VibrationValue vibration);
182 int TestVibration(std::size_t device_index);
183
184 int SetCallback(ControllerUpdateCallback update_callback);
185 void DeleteCallback(int key);
186
187private:
188 /**
189 * Sets the status of a button. Applies toggle properties to the output.
190 *
191 * @param A CallbackStatus and a button index number
192 */
193 void SetButton(Input::CallbackStatus callback, std::size_t index);
194 void SetStick(Input::CallbackStatus callback, std::size_t index);
195 void SetTrigger(Input::CallbackStatus callback, std::size_t index);
196 void SetMotion(Input::CallbackStatus callback, std::size_t index);
197 void SetBattery(Input::CallbackStatus callback, std::size_t index);
198
199 /**
200 * Triggers a callback that something has changed
201 *
202 * @param Input type of the trigger
203 */
204 void TriggerOnChange(ControllerTriggerType type);
205
206 NpadIdType npad_id_type;
207 NpadType npad_type{NpadType::None};
208 bool is_connected{false};
209 bool is_configuring{false};
210 bool is_vibration_enabled{true};
211 f32 motion_sensitivity{0.01f};
212
213 ButtonParams button_params;
214 StickParams stick_params;
215 ControllerMotionParams motion_params;
216 TriggerParams trigger_params;
217 BatteryParams battery_params;
218
219 ButtonDevices button_devices;
220 StickDevices stick_devices;
221 ControllerMotionDevices motion_devices;
222 TriggerDevices trigger_devices;
223 BatteryDevices battery_devices;
224 // VibrationDevices vibration_devices;
225
226 mutable std::mutex mutex;
227 std::unordered_map<int, ControllerUpdateCallback> callback_list;
228 int last_callback_key = 0;
229 ControllerStatus controller;
230};
231
232} // namespace Core::HID
diff --git a/src/core/hid/emulated_devices.cpp b/src/core/hid/emulated_devices.cpp
new file mode 100644
index 000000000..3caf90714
--- /dev/null
+++ b/src/core/hid/emulated_devices.cpp
@@ -0,0 +1,349 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included
4
5#include <fmt/format.h>
6
7#include "core/hid/emulated_devices.h"
8#include "core/hid/input_converter.h"
9
10namespace Core::HID {
11
12EmulatedDevices::EmulatedDevices() {}
13
14EmulatedDevices::~EmulatedDevices() = default;
15
16void EmulatedDevices::ReloadFromSettings() {
17 const auto& mouse = Settings::values.mouse_buttons;
18
19 for (std::size_t index = 0; index < mouse.size(); ++index) {
20 mouse_button_params[index] = Common::ParamPackage(mouse[index]);
21 }
22 ReloadInput();
23}
24
25void EmulatedDevices::ReloadInput() {
26 std::transform(mouse_button_params.begin() + Settings::NativeMouseButton::MOUSE_HID_BEGIN,
27 mouse_button_params.begin() + Settings::NativeMouseButton::MOUSE_HID_END,
28 mouse_button_devices.begin(), Input::CreateDevice<Input::InputDevice>);
29
30 std::transform(Settings::values.keyboard_keys.begin(), Settings::values.keyboard_keys.end(),
31 keyboard_devices.begin(), Input::CreateDeviceFromString<Input::InputDevice>);
32
33 std::transform(Settings::values.keyboard_mods.begin(), Settings::values.keyboard_mods.end(),
34 keyboard_modifier_devices.begin(),
35 Input::CreateDeviceFromString<Input::InputDevice>);
36
37 for (std::size_t index = 0; index < mouse_button_devices.size(); ++index) {
38 if (!mouse_button_devices[index]) {
39 continue;
40 }
41 Input::InputCallback button_callback{
42 [this, index](Input::CallbackStatus callback) { SetMouseButton(callback, index); }};
43 mouse_button_devices[index]->SetCallback(button_callback);
44 }
45
46 for (std::size_t index = 0; index < keyboard_devices.size(); ++index) {
47 if (!keyboard_devices[index]) {
48 continue;
49 }
50 Input::InputCallback button_callback{
51 [this, index](Input::CallbackStatus callback) { SetKeyboardButton(callback, index); }};
52 keyboard_devices[index]->SetCallback(button_callback);
53 }
54
55 for (std::size_t index = 0; index < keyboard_modifier_devices.size(); ++index) {
56 if (!keyboard_modifier_devices[index]) {
57 continue;
58 }
59 Input::InputCallback button_callback{[this, index](Input::CallbackStatus callback) {
60 SetKeyboardModifier(callback, index);
61 }};
62 keyboard_modifier_devices[index]->SetCallback(button_callback);
63 }
64}
65
66void EmulatedDevices::UnloadInput() {
67 for (auto& button : mouse_button_devices) {
68 button.reset();
69 }
70 for (auto& button : keyboard_devices) {
71 button.reset();
72 }
73 for (auto& button : keyboard_modifier_devices) {
74 button.reset();
75 }
76}
77
78void EmulatedDevices::EnableConfiguration() {
79 is_configuring = true;
80 SaveCurrentConfig();
81}
82
83void EmulatedDevices::DisableConfiguration() {
84 is_configuring = false;
85}
86
87bool EmulatedDevices::IsConfiguring() const {
88 return is_configuring;
89}
90
91void EmulatedDevices::SaveCurrentConfig() {
92 if (!is_configuring) {
93 return;
94 }
95
96 auto& mouse = Settings::values.mouse_buttons;
97
98 for (std::size_t index = 0; index < mouse.size(); ++index) {
99 mouse[index] = mouse_button_params[index].Serialize();
100 }
101}
102
103void EmulatedDevices::RestoreConfig() {
104 if (!is_configuring) {
105 return;
106 }
107 ReloadFromSettings();
108}
109
110Common::ParamPackage EmulatedDevices::GetMouseButtonParam(std::size_t index) const {
111 if (index >= mouse_button_params.size()) {
112 return {};
113 }
114 return mouse_button_params[index];
115}
116
117void EmulatedDevices::SetButtonParam(std::size_t index, Common::ParamPackage param) {
118 if (index >= mouse_button_params.size()) {
119 return;
120 }
121 mouse_button_params[index] = param;
122 ReloadInput();
123}
124
125void EmulatedDevices::SetKeyboardButton(Input::CallbackStatus callback, std::size_t index) {
126 if (index >= device_status.keyboard_values.size()) {
127 return;
128 }
129 std::lock_guard lock{mutex};
130 bool value_changed = false;
131 const auto new_status = TransformToButton(callback);
132 auto& current_status = device_status.keyboard_values[index];
133 current_status.toggle = new_status.toggle;
134
135 // Update button status with current
136 if (!current_status.toggle) {
137 current_status.locked = false;
138 if (current_status.value != new_status.value) {
139 current_status.value = new_status.value;
140 value_changed = true;
141 }
142 } else {
143 // Toggle button and lock status
144 if (new_status.value && !current_status.locked) {
145 current_status.locked = true;
146 current_status.value = !current_status.value;
147 value_changed = true;
148 }
149
150 // Unlock button ready for next press
151 if (!new_status.value && current_status.locked) {
152 current_status.locked = false;
153 }
154 }
155
156 if (!value_changed) {
157 return;
158 }
159
160 if (is_configuring) {
161 TriggerOnChange(DeviceTriggerType::Keyboard);
162 return;
163 }
164
165 // TODO(german77): Do this properly
166 // switch (index) {
167 // case Settings::NativeKeyboard::A:
168 // interface_status.keyboard_state.a.Assign(current_status.value);
169 // break;
170 // ....
171 //}
172
173 TriggerOnChange(DeviceTriggerType::Keyboard);
174}
175
176void EmulatedDevices::SetKeyboardModifier(Input::CallbackStatus callback, std::size_t index) {
177 if (index >= device_status.keyboard_moddifier_values.size()) {
178 return;
179 }
180 std::lock_guard lock{mutex};
181 bool value_changed = false;
182 const auto new_status = TransformToButton(callback);
183 auto& current_status = device_status.keyboard_moddifier_values[index];
184 current_status.toggle = new_status.toggle;
185
186 // Update button status with current
187 if (!current_status.toggle) {
188 current_status.locked = false;
189 if (current_status.value != new_status.value) {
190 current_status.value = new_status.value;
191 value_changed = true;
192 }
193 } else {
194 // Toggle button and lock status
195 if (new_status.value && !current_status.locked) {
196 current_status.locked = true;
197 current_status.value = !current_status.value;
198 value_changed = true;
199 }
200
201 // Unlock button ready for next press
202 if (!new_status.value && current_status.locked) {
203 current_status.locked = false;
204 }
205 }
206
207 if (!value_changed) {
208 return;
209 }
210
211 if (is_configuring) {
212 TriggerOnChange(DeviceTriggerType::KeyboardModdifier);
213 return;
214 }
215
216 switch (index) {
217 case Settings::NativeKeyboard::LeftControl:
218 case Settings::NativeKeyboard::RightControl:
219 device_status.keyboard_moddifier_state.control.Assign(current_status.value);
220 break;
221 case Settings::NativeKeyboard::LeftShift:
222 case Settings::NativeKeyboard::RightShift:
223 device_status.keyboard_moddifier_state.shift.Assign(current_status.value);
224 break;
225 case Settings::NativeKeyboard::LeftAlt:
226 device_status.keyboard_moddifier_state.left_alt.Assign(current_status.value);
227 break;
228 case Settings::NativeKeyboard::RightAlt:
229 device_status.keyboard_moddifier_state.right_alt.Assign(current_status.value);
230 break;
231 case Settings::NativeKeyboard::CapsLock:
232 device_status.keyboard_moddifier_state.caps_lock.Assign(current_status.value);
233 break;
234 case Settings::NativeKeyboard::ScrollLock:
235 device_status.keyboard_moddifier_state.scroll_lock.Assign(current_status.value);
236 break;
237 case Settings::NativeKeyboard::NumLock:
238 device_status.keyboard_moddifier_state.num_lock.Assign(current_status.value);
239 break;
240 }
241
242 TriggerOnChange(DeviceTriggerType::KeyboardModdifier);
243}
244
245void EmulatedDevices::SetMouseButton(Input::CallbackStatus callback, std::size_t index) {
246 if (index >= device_status.mouse_button_values.size()) {
247 return;
248 }
249 std::lock_guard lock{mutex};
250 bool value_changed = false;
251 const auto new_status = TransformToButton(callback);
252 auto& current_status = device_status.mouse_button_values[index];
253 current_status.toggle = new_status.toggle;
254
255 // Update button status with current
256 if (!current_status.toggle) {
257 current_status.locked = false;
258 if (current_status.value != new_status.value) {
259 current_status.value = new_status.value;
260 value_changed = true;
261 }
262 } else {
263 // Toggle button and lock status
264 if (new_status.value && !current_status.locked) {
265 current_status.locked = true;
266 current_status.value = !current_status.value;
267 value_changed = true;
268 }
269
270 // Unlock button ready for next press
271 if (!new_status.value && current_status.locked) {
272 current_status.locked = false;
273 }
274 }
275
276 if (!value_changed) {
277 return;
278 }
279
280 if (is_configuring) {
281 TriggerOnChange(DeviceTriggerType::Mouse);
282 return;
283 }
284
285 switch (index) {
286 case Settings::NativeMouseButton::Left:
287 device_status.mouse_button_state.left.Assign(current_status.value);
288 break;
289 case Settings::NativeMouseButton::Right:
290 device_status.mouse_button_state.right.Assign(current_status.value);
291 break;
292 case Settings::NativeMouseButton::Middle:
293 device_status.mouse_button_state.middle.Assign(current_status.value);
294 break;
295 case Settings::NativeMouseButton::Forward:
296 device_status.mouse_button_state.forward.Assign(current_status.value);
297 break;
298 case Settings::NativeMouseButton::Back:
299 device_status.mouse_button_state.back.Assign(current_status.value);
300 break;
301 }
302
303 TriggerOnChange(DeviceTriggerType::Mouse);
304}
305
306MouseButtonValues EmulatedDevices::GetMouseButtonsValues() const {
307 return device_status.mouse_button_values;
308}
309
310KeyboardKey EmulatedDevices::GetKeyboard() const {
311 return device_status.keyboard_state;
312}
313
314KeyboardModifier EmulatedDevices::GetKeyboardModifier() const {
315 return device_status.keyboard_moddifier_state;
316}
317
318MouseButton EmulatedDevices::GetMouseButtons() const {
319 return device_status.mouse_button_state;
320}
321
322MousePosition EmulatedDevices::GetMousePosition() const {
323 return device_status.mouse_position_state;
324}
325
326void EmulatedDevices::TriggerOnChange(DeviceTriggerType type) {
327 for (const std::pair<int, InterfaceUpdateCallback> poller_pair : callback_list) {
328 const InterfaceUpdateCallback& poller = poller_pair.second;
329 if (poller.on_change) {
330 poller.on_change(type);
331 }
332 }
333}
334
335int EmulatedDevices::SetCallback(InterfaceUpdateCallback update_callback) {
336 std::lock_guard lock{mutex};
337 callback_list.insert_or_assign(last_callback_key, update_callback);
338 return last_callback_key++;
339}
340
341void EmulatedDevices::DeleteCallback(int key) {
342 std::lock_guard lock{mutex};
343 if (!callback_list.contains(key)) {
344 LOG_ERROR(Input, "Tried to delete non-existent callback {}", key);
345 return;
346 }
347 callback_list.erase(key);
348}
349} // namespace Core::HID
diff --git a/src/core/hid/emulated_devices.h b/src/core/hid/emulated_devices.h
new file mode 100644
index 000000000..6f728eff5
--- /dev/null
+++ b/src/core/hid/emulated_devices.h
@@ -0,0 +1,137 @@
1// Copyright 2021 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 <functional>
8#include <mutex>
9#include <unordered_map>
10
11#include "common/input.h"
12#include "common/param_package.h"
13#include "common/settings.h"
14#include "core/hid/hid_types.h"
15#include "core/hid/motion_input.h"
16
17namespace Core::HID {
18
19using KeyboardDevices =
20 std::array<std::unique_ptr<Input::InputDevice>, Settings::NativeKeyboard::NumKeyboardKeys>;
21using KeyboardModifierDevices =
22 std::array<std::unique_ptr<Input::InputDevice>, Settings::NativeKeyboard::NumKeyboardMods>;
23using MouseButtonDevices =
24 std::array<std::unique_ptr<Input::InputDevice>, Settings::NativeMouseButton::NumMouseButtons>;
25
26using MouseButtonParams =
27 std::array<Common::ParamPackage, Settings::NativeMouseButton::NumMouseButtons>;
28
29using KeyboardValues = std::array<Input::ButtonStatus, Settings::NativeKeyboard::NumKeyboardKeys>;
30using KeyboardModifierValues =
31 std::array<Input::ButtonStatus, Settings::NativeKeyboard::NumKeyboardMods>;
32using MouseButtonValues =
33 std::array<Input::ButtonStatus, Settings::NativeMouseButton::NumMouseButtons>;
34
35struct MousePosition {
36 s32 x;
37 s32 y;
38 s32 delta_wheel_x;
39 s32 delta_wheel_y;
40};
41
42struct DeviceStatus {
43 // Data from input_common
44 KeyboardValues keyboard_values{};
45 KeyboardModifierValues keyboard_moddifier_values{};
46 MouseButtonValues mouse_button_values{};
47
48 // Data for Nintendo devices
49 KeyboardKey keyboard_state{};
50 KeyboardModifier keyboard_moddifier_state{};
51 MouseButton mouse_button_state{};
52 MousePosition mouse_position_state{};
53};
54
55enum class DeviceTriggerType {
56 Keyboard,
57 KeyboardModdifier,
58 Mouse,
59};
60
61struct InterfaceUpdateCallback {
62 std::function<void(DeviceTriggerType)> on_change;
63};
64
65class EmulatedDevices {
66public:
67 /**
68 * TODO: Write description
69 *
70 * @param npad_id_type
71 */
72 explicit EmulatedDevices();
73 ~EmulatedDevices();
74
75 YUZU_NON_COPYABLE(EmulatedDevices);
76 YUZU_NON_MOVEABLE(EmulatedDevices);
77
78 void ReloadFromSettings();
79 void ReloadInput();
80 void UnloadInput();
81
82 void EnableConfiguration();
83 void DisableConfiguration();
84 bool IsConfiguring() const;
85 void SaveCurrentConfig();
86 void RestoreConfig();
87
88 std::vector<Common::ParamPackage> GetMappedDevices() const;
89
90 Common::ParamPackage GetMouseButtonParam(std::size_t index) const;
91
92 void SetButtonParam(std::size_t index, Common::ParamPackage param);
93
94 KeyboardValues GetKeyboardValues() const;
95 KeyboardModifierValues GetKeyboardModdifierValues() const;
96 MouseButtonValues GetMouseButtonsValues() const;
97
98 KeyboardKey GetKeyboard() const;
99 KeyboardModifier GetKeyboardModifier() const;
100 MouseButton GetMouseButtons() const;
101 MousePosition GetMousePosition() const;
102
103 int SetCallback(InterfaceUpdateCallback update_callback);
104 void DeleteCallback(int key);
105
106private:
107 /**
108 * Sets the status of a button. Applies toggle properties to the output.
109 *
110 * @param A CallbackStatus and a button index number
111 */
112 void SetKeyboardButton(Input::CallbackStatus callback, std::size_t index);
113 void SetKeyboardModifier(Input::CallbackStatus callback, std::size_t index);
114 void SetMouseButton(Input::CallbackStatus callback, std::size_t index);
115
116 /**
117 * Triggers a callback that something has changed
118 *
119 * @param Input type of the trigger
120 */
121 void TriggerOnChange(DeviceTriggerType type);
122
123 bool is_configuring{false};
124
125 MouseButtonParams mouse_button_params;
126
127 KeyboardDevices keyboard_devices;
128 KeyboardModifierDevices keyboard_modifier_devices;
129 MouseButtonDevices mouse_button_devices;
130
131 mutable std::mutex mutex;
132 std::unordered_map<int, InterfaceUpdateCallback> callback_list;
133 int last_callback_key = 0;
134 DeviceStatus device_status;
135};
136
137} // namespace Core::HID
diff --git a/src/core/hid/hid_core.cpp b/src/core/hid/hid_core.cpp
new file mode 100644
index 000000000..ee76db110
--- /dev/null
+++ b/src/core/hid/hid_core.cpp
@@ -0,0 +1,144 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/assert.h"
6#include "core/hid/hid_core.h"
7
8namespace Core::HID {
9
10HIDCore::HIDCore()
11 : player_1{std::make_unique<EmulatedController>(NpadIdType::Player1)},
12 player_2{std::make_unique<EmulatedController>(NpadIdType::Player2)},
13 player_3{std::make_unique<EmulatedController>(NpadIdType::Player3)},
14 player_4{std::make_unique<EmulatedController>(NpadIdType::Player4)},
15 player_5{std::make_unique<EmulatedController>(NpadIdType::Player5)},
16 player_6{std::make_unique<EmulatedController>(NpadIdType::Player6)},
17 player_7{std::make_unique<EmulatedController>(NpadIdType::Player7)},
18 player_8{std::make_unique<EmulatedController>(NpadIdType::Player8)},
19 other{std::make_unique<EmulatedController>(NpadIdType::Other)},
20 handheld{std::make_unique<EmulatedController>(NpadIdType::Handheld)},
21 console{std::make_unique<EmulatedConsole>()}, devices{std::make_unique<EmulatedDevices>()} {}
22
23HIDCore::~HIDCore() = default;
24
25EmulatedController* HIDCore::GetEmulatedController(NpadIdType npad_id_type) {
26 switch (npad_id_type) {
27 case NpadIdType::Player1:
28 return player_1.get();
29 case NpadIdType::Player2:
30 return player_2.get();
31 case NpadIdType::Player3:
32 return player_3.get();
33 case NpadIdType::Player4:
34 return player_4.get();
35 case NpadIdType::Player5:
36 return player_5.get();
37 case NpadIdType::Player6:
38 return player_6.get();
39 case NpadIdType::Player7:
40 return player_7.get();
41 case NpadIdType::Player8:
42 return player_8.get();
43 case NpadIdType::Other:
44 return other.get();
45 case NpadIdType::Handheld:
46 return handheld.get();
47 case NpadIdType::Invalid:
48 default:
49 UNREACHABLE_MSG("Invalid NpadIdType={}", npad_id_type);
50 return nullptr;
51 }
52}
53
54const EmulatedController* HIDCore::GetEmulatedController(NpadIdType npad_id_type) const {
55 switch (npad_id_type) {
56 case NpadIdType::Player1:
57 return player_1.get();
58 case NpadIdType::Player2:
59 return player_2.get();
60 case NpadIdType::Player3:
61 return player_3.get();
62 case NpadIdType::Player4:
63 return player_4.get();
64 case NpadIdType::Player5:
65 return player_5.get();
66 case NpadIdType::Player6:
67 return player_6.get();
68 case NpadIdType::Player7:
69 return player_7.get();
70 case NpadIdType::Player8:
71 return player_8.get();
72 case NpadIdType::Other:
73 return other.get();
74 case NpadIdType::Handheld:
75 return handheld.get();
76 case NpadIdType::Invalid:
77 default:
78 UNREACHABLE_MSG("Invalid NpadIdType={}", npad_id_type);
79 return nullptr;
80 }
81}
82EmulatedConsole* HIDCore::GetEmulatedConsole() {
83 return console.get();
84}
85
86const EmulatedConsole* HIDCore::GetEmulatedConsole() const {
87 return console.get();
88}
89
90EmulatedDevices* HIDCore::GetEmulatedDevices() {
91 return devices.get();
92}
93
94const EmulatedDevices* HIDCore::GetEmulatedDevices() const {
95 return devices.get();
96}
97
98EmulatedController* HIDCore::GetEmulatedControllerByIndex(std::size_t index) {
99 return GetEmulatedController(IndexToNpadIdType(index));
100}
101
102const EmulatedController* HIDCore::GetEmulatedControllerByIndex(std::size_t index) const {
103 return GetEmulatedController(IndexToNpadIdType(index));
104}
105
106void HIDCore::SetSupportedStyleTag(NpadStyleTag style_tag) {
107 supported_style_tag.raw = style_tag.raw;
108}
109
110NpadStyleTag HIDCore::GetSupportedStyleTag() const {
111 return supported_style_tag;
112}
113
114void HIDCore::ReloadInputDevices() {
115 player_1->ReloadFromSettings();
116 player_2->ReloadFromSettings();
117 player_3->ReloadFromSettings();
118 player_4->ReloadFromSettings();
119 player_5->ReloadFromSettings();
120 player_6->ReloadFromSettings();
121 player_7->ReloadFromSettings();
122 player_8->ReloadFromSettings();
123 other->ReloadFromSettings();
124 handheld->ReloadFromSettings();
125 console->ReloadFromSettings();
126 devices->ReloadFromSettings();
127}
128
129void HIDCore::UnloadInputDevices() {
130 player_1->UnloadInput();
131 player_2->UnloadInput();
132 player_3->UnloadInput();
133 player_4->UnloadInput();
134 player_5->UnloadInput();
135 player_6->UnloadInput();
136 player_7->UnloadInput();
137 player_8->UnloadInput();
138 other->UnloadInput();
139 handheld->UnloadInput();
140 console->UnloadInput();
141 devices->UnloadInput();
142}
143
144} // namespace Core::HID
diff --git a/src/core/hid/hid_core.h b/src/core/hid/hid_core.h
new file mode 100644
index 000000000..f11f48b61
--- /dev/null
+++ b/src/core/hid/hid_core.h
@@ -0,0 +1,60 @@
1// Copyright 2021 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 <memory>
8
9#include "core/hid/emulated_console.h"
10#include "core/hid/emulated_controller.h"
11#include "core/hid/emulated_devices.h"
12
13namespace Core::HID {
14
15class HIDCore {
16public:
17 explicit HIDCore();
18 ~HIDCore();
19
20 YUZU_NON_COPYABLE(HIDCore);
21 YUZU_NON_MOVEABLE(HIDCore);
22
23 EmulatedController* GetEmulatedController(NpadIdType npad_id_type);
24 const EmulatedController* GetEmulatedController(NpadIdType npad_id_type) const;
25
26 EmulatedController* GetEmulatedControllerByIndex(std::size_t index);
27 const EmulatedController* GetEmulatedControllerByIndex(std::size_t index) const;
28
29 EmulatedConsole* GetEmulatedConsole();
30 const EmulatedConsole* GetEmulatedConsole() const;
31
32 EmulatedDevices* GetEmulatedDevices();
33 const EmulatedDevices* GetEmulatedDevices() const;
34
35 void SetSupportedStyleTag(NpadStyleTag style_tag);
36 NpadStyleTag GetSupportedStyleTag() const;
37
38 // Reloads all input devices from settings
39 void ReloadInputDevices();
40
41 // Removes all callbacks from input common
42 void UnloadInputDevices();
43
44private:
45 std::unique_ptr<EmulatedController> player_1;
46 std::unique_ptr<EmulatedController> player_2;
47 std::unique_ptr<EmulatedController> player_3;
48 std::unique_ptr<EmulatedController> player_4;
49 std::unique_ptr<EmulatedController> player_5;
50 std::unique_ptr<EmulatedController> player_6;
51 std::unique_ptr<EmulatedController> player_7;
52 std::unique_ptr<EmulatedController> player_8;
53 std::unique_ptr<EmulatedController> other;
54 std::unique_ptr<EmulatedController> handheld;
55 std::unique_ptr<EmulatedConsole> console;
56 std::unique_ptr<EmulatedDevices> devices;
57 NpadStyleTag supported_style_tag;
58};
59
60} // namespace Core::HID