summaryrefslogtreecommitdiff
path: root/src/core/hid/emulated_devices.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/hid/emulated_devices.cpp')
-rw-r--r--src/core/hid/emulated_devices.cpp451
1 files changed, 451 insertions, 0 deletions
diff --git a/src/core/hid/emulated_devices.cpp b/src/core/hid/emulated_devices.cpp
new file mode 100644
index 000000000..874780ec2
--- /dev/null
+++ b/src/core/hid/emulated_devices.cpp
@@ -0,0 +1,451 @@
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 <algorithm>
6#include <fmt/format.h>
7
8#include "core/hid/emulated_devices.h"
9#include "core/hid/input_converter.h"
10
11namespace Core::HID {
12
13EmulatedDevices::EmulatedDevices() = default;
14
15EmulatedDevices::~EmulatedDevices() = default;
16
17void EmulatedDevices::ReloadFromSettings() {
18 ReloadInput();
19}
20
21void EmulatedDevices::ReloadInput() {
22 // If you load any device here add the equivalent to the UnloadInput() function
23 std::size_t key_index = 0;
24 for (auto& mouse_device : mouse_button_devices) {
25 Common::ParamPackage mouse_params;
26 mouse_params.Set("engine", "mouse");
27 mouse_params.Set("button", static_cast<int>(key_index));
28 mouse_device = Common::Input::CreateDevice<Common::Input::InputDevice>(mouse_params);
29 key_index++;
30 }
31
32 mouse_stick_device = Common::Input::CreateDeviceFromString<Common::Input::InputDevice>(
33 "engine:mouse,axis_x:0,axis_y:1");
34
35 // First two axis are reserved for mouse position
36 key_index = 2;
37 for (auto& mouse_device : mouse_analog_devices) {
38 Common::ParamPackage mouse_params;
39 mouse_params.Set("engine", "mouse");
40 mouse_params.Set("axis", static_cast<int>(key_index));
41 mouse_device = Common::Input::CreateDevice<Common::Input::InputDevice>(mouse_params);
42 key_index++;
43 }
44
45 key_index = 0;
46 for (auto& keyboard_device : keyboard_devices) {
47 // Keyboard keys are only mapped on port 1, pad 0
48 Common::ParamPackage keyboard_params;
49 keyboard_params.Set("engine", "keyboard");
50 keyboard_params.Set("button", static_cast<int>(key_index));
51 keyboard_params.Set("port", 1);
52 keyboard_params.Set("pad", 0);
53 keyboard_device = Common::Input::CreateDevice<Common::Input::InputDevice>(keyboard_params);
54 key_index++;
55 }
56
57 key_index = 0;
58 for (auto& keyboard_device : keyboard_modifier_devices) {
59 // Keyboard moddifiers are only mapped on port 1, pad 1
60 Common::ParamPackage keyboard_params;
61 keyboard_params.Set("engine", "keyboard");
62 keyboard_params.Set("button", static_cast<int>(key_index));
63 keyboard_params.Set("port", 1);
64 keyboard_params.Set("pad", 1);
65 keyboard_device = Common::Input::CreateDevice<Common::Input::InputDevice>(keyboard_params);
66 key_index++;
67 }
68
69 for (std::size_t index = 0; index < mouse_button_devices.size(); ++index) {
70 if (!mouse_button_devices[index]) {
71 continue;
72 }
73 Common::Input::InputCallback button_callback{
74 [this, index](Common::Input::CallbackStatus callback) {
75 SetMouseButton(callback, index);
76 }};
77 mouse_button_devices[index]->SetCallback(button_callback);
78 }
79
80 for (std::size_t index = 0; index < mouse_analog_devices.size(); ++index) {
81 if (!mouse_analog_devices[index]) {
82 continue;
83 }
84 Common::Input::InputCallback button_callback{
85 [this, index](Common::Input::CallbackStatus callback) {
86 SetMouseAnalog(callback, index);
87 }};
88 mouse_analog_devices[index]->SetCallback(button_callback);
89 }
90
91 if (mouse_stick_device) {
92 Common::Input::InputCallback button_callback{
93 [this](Common::Input::CallbackStatus callback) { SetMouseStick(callback); }};
94 mouse_stick_device->SetCallback(button_callback);
95 }
96
97 for (std::size_t index = 0; index < keyboard_devices.size(); ++index) {
98 if (!keyboard_devices[index]) {
99 continue;
100 }
101 Common::Input::InputCallback button_callback{
102 [this, index](Common::Input::CallbackStatus callback) {
103 SetKeyboardButton(callback, index);
104 }};
105 keyboard_devices[index]->SetCallback(button_callback);
106 }
107
108 for (std::size_t index = 0; index < keyboard_modifier_devices.size(); ++index) {
109 if (!keyboard_modifier_devices[index]) {
110 continue;
111 }
112 Common::Input::InputCallback button_callback{
113 [this, index](Common::Input::CallbackStatus callback) {
114 SetKeyboardModifier(callback, index);
115 }};
116 keyboard_modifier_devices[index]->SetCallback(button_callback);
117 }
118}
119
120void EmulatedDevices::UnloadInput() {
121 for (auto& button : mouse_button_devices) {
122 button.reset();
123 }
124 for (auto& analog : mouse_analog_devices) {
125 analog.reset();
126 }
127 mouse_stick_device.reset();
128 for (auto& button : keyboard_devices) {
129 button.reset();
130 }
131 for (auto& button : keyboard_modifier_devices) {
132 button.reset();
133 }
134}
135
136void EmulatedDevices::EnableConfiguration() {
137 is_configuring = true;
138 SaveCurrentConfig();
139}
140
141void EmulatedDevices::DisableConfiguration() {
142 is_configuring = false;
143}
144
145bool EmulatedDevices::IsConfiguring() const {
146 return is_configuring;
147}
148
149void EmulatedDevices::SaveCurrentConfig() {
150 if (!is_configuring) {
151 return;
152 }
153}
154
155void EmulatedDevices::RestoreConfig() {
156 if (!is_configuring) {
157 return;
158 }
159 ReloadFromSettings();
160}
161
162void EmulatedDevices::SetKeyboardButton(Common::Input::CallbackStatus callback, std::size_t index) {
163 if (index >= device_status.keyboard_values.size()) {
164 return;
165 }
166 std::lock_guard lock{mutex};
167 bool value_changed = false;
168 const auto new_status = TransformToButton(callback);
169 auto& current_status = device_status.keyboard_values[index];
170 current_status.toggle = new_status.toggle;
171
172 // Update button status with current status
173 if (!current_status.toggle) {
174 current_status.locked = false;
175 if (current_status.value != new_status.value) {
176 current_status.value = new_status.value;
177 value_changed = true;
178 }
179 } else {
180 // Toggle button and lock status
181 if (new_status.value && !current_status.locked) {
182 current_status.locked = true;
183 current_status.value = !current_status.value;
184 value_changed = true;
185 }
186
187 // Unlock button, ready for next press
188 if (!new_status.value && current_status.locked) {
189 current_status.locked = false;
190 }
191 }
192
193 if (!value_changed) {
194 return;
195 }
196
197 if (is_configuring) {
198 TriggerOnChange(DeviceTriggerType::Keyboard);
199 return;
200 }
201
202 // Index should be converted from NativeKeyboard to KeyboardKeyIndex
203 UpdateKey(index, current_status.value);
204
205 TriggerOnChange(DeviceTriggerType::Keyboard);
206}
207
208void EmulatedDevices::UpdateKey(std::size_t key_index, bool status) {
209 constexpr std::size_t KEYS_PER_BYTE = 8;
210 auto& entry = device_status.keyboard_state.key[key_index / KEYS_PER_BYTE];
211 const u8 mask = static_cast<u8>(1 << (key_index % KEYS_PER_BYTE));
212 if (status) {
213 entry = entry | mask;
214 } else {
215 entry = static_cast<u8>(entry & ~mask);
216 }
217}
218
219void EmulatedDevices::SetKeyboardModifier(Common::Input::CallbackStatus callback,
220 std::size_t index) {
221 if (index >= device_status.keyboard_moddifier_values.size()) {
222 return;
223 }
224 std::lock_guard lock{mutex};
225 bool value_changed = false;
226 const auto new_status = TransformToButton(callback);
227 auto& current_status = device_status.keyboard_moddifier_values[index];
228 current_status.toggle = new_status.toggle;
229
230 // Update button status with current
231 if (!current_status.toggle) {
232 current_status.locked = false;
233 if (current_status.value != new_status.value) {
234 current_status.value = new_status.value;
235 value_changed = true;
236 }
237 } else {
238 // Toggle button and lock status
239 if (new_status.value && !current_status.locked) {
240 current_status.locked = true;
241 current_status.value = !current_status.value;
242 value_changed = true;
243 }
244
245 // Unlock button ready for next press
246 if (!new_status.value && current_status.locked) {
247 current_status.locked = false;
248 }
249 }
250
251 if (!value_changed) {
252 return;
253 }
254
255 if (is_configuring) {
256 TriggerOnChange(DeviceTriggerType::KeyboardModdifier);
257 return;
258 }
259
260 switch (index) {
261 case Settings::NativeKeyboard::LeftControl:
262 case Settings::NativeKeyboard::RightControl:
263 device_status.keyboard_moddifier_state.control.Assign(current_status.value);
264 break;
265 case Settings::NativeKeyboard::LeftShift:
266 case Settings::NativeKeyboard::RightShift:
267 device_status.keyboard_moddifier_state.shift.Assign(current_status.value);
268 break;
269 case Settings::NativeKeyboard::LeftAlt:
270 device_status.keyboard_moddifier_state.left_alt.Assign(current_status.value);
271 break;
272 case Settings::NativeKeyboard::RightAlt:
273 device_status.keyboard_moddifier_state.right_alt.Assign(current_status.value);
274 break;
275 case Settings::NativeKeyboard::CapsLock:
276 device_status.keyboard_moddifier_state.caps_lock.Assign(current_status.value);
277 break;
278 case Settings::NativeKeyboard::ScrollLock:
279 device_status.keyboard_moddifier_state.scroll_lock.Assign(current_status.value);
280 break;
281 case Settings::NativeKeyboard::NumLock:
282 device_status.keyboard_moddifier_state.num_lock.Assign(current_status.value);
283 break;
284 }
285
286 TriggerOnChange(DeviceTriggerType::KeyboardModdifier);
287}
288
289void EmulatedDevices::SetMouseButton(Common::Input::CallbackStatus callback, std::size_t index) {
290 if (index >= device_status.mouse_button_values.size()) {
291 return;
292 }
293 std::lock_guard lock{mutex};
294 bool value_changed = false;
295 const auto new_status = TransformToButton(callback);
296 auto& current_status = device_status.mouse_button_values[index];
297 current_status.toggle = new_status.toggle;
298
299 // Update button status with current
300 if (!current_status.toggle) {
301 current_status.locked = false;
302 if (current_status.value != new_status.value) {
303 current_status.value = new_status.value;
304 value_changed = true;
305 }
306 } else {
307 // Toggle button and lock status
308 if (new_status.value && !current_status.locked) {
309 current_status.locked = true;
310 current_status.value = !current_status.value;
311 value_changed = true;
312 }
313
314 // Unlock button ready for next press
315 if (!new_status.value && current_status.locked) {
316 current_status.locked = false;
317 }
318 }
319
320 if (!value_changed) {
321 return;
322 }
323
324 if (is_configuring) {
325 TriggerOnChange(DeviceTriggerType::Mouse);
326 return;
327 }
328
329 switch (index) {
330 case Settings::NativeMouseButton::Left:
331 device_status.mouse_button_state.left.Assign(current_status.value);
332 break;
333 case Settings::NativeMouseButton::Right:
334 device_status.mouse_button_state.right.Assign(current_status.value);
335 break;
336 case Settings::NativeMouseButton::Middle:
337 device_status.mouse_button_state.middle.Assign(current_status.value);
338 break;
339 case Settings::NativeMouseButton::Forward:
340 device_status.mouse_button_state.forward.Assign(current_status.value);
341 break;
342 case Settings::NativeMouseButton::Back:
343 device_status.mouse_button_state.back.Assign(current_status.value);
344 break;
345 }
346
347 TriggerOnChange(DeviceTriggerType::Mouse);
348}
349
350void EmulatedDevices::SetMouseAnalog(Common::Input::CallbackStatus callback, std::size_t index) {
351 if (index >= device_status.mouse_analog_values.size()) {
352 return;
353 }
354 std::lock_guard lock{mutex};
355 const auto analog_value = TransformToAnalog(callback);
356
357 device_status.mouse_analog_values[index] = analog_value;
358
359 if (is_configuring) {
360 device_status.mouse_position_state = {};
361 TriggerOnChange(DeviceTriggerType::Mouse);
362 return;
363 }
364
365 switch (index) {
366 case Settings::NativeMouseWheel::X:
367 device_status.mouse_wheel_state.x = static_cast<s32>(analog_value.value);
368 break;
369 case Settings::NativeMouseWheel::Y:
370 device_status.mouse_wheel_state.y = static_cast<s32>(analog_value.value);
371 break;
372 }
373
374 TriggerOnChange(DeviceTriggerType::Mouse);
375}
376
377void EmulatedDevices::SetMouseStick(Common::Input::CallbackStatus callback) {
378 std::lock_guard lock{mutex};
379 const auto touch_value = TransformToTouch(callback);
380
381 device_status.mouse_stick_value = touch_value;
382
383 if (is_configuring) {
384 device_status.mouse_position_state = {};
385 TriggerOnChange(DeviceTriggerType::Mouse);
386 return;
387 }
388
389 device_status.mouse_position_state.x = touch_value.x.value;
390 device_status.mouse_position_state.y = touch_value.y.value;
391
392 TriggerOnChange(DeviceTriggerType::Mouse);
393}
394
395KeyboardValues EmulatedDevices::GetKeyboardValues() const {
396 return device_status.keyboard_values;
397}
398
399KeyboardModifierValues EmulatedDevices::GetKeyboardModdifierValues() const {
400 return device_status.keyboard_moddifier_values;
401}
402
403MouseButtonValues EmulatedDevices::GetMouseButtonsValues() const {
404 return device_status.mouse_button_values;
405}
406
407KeyboardKey EmulatedDevices::GetKeyboard() const {
408 return device_status.keyboard_state;
409}
410
411KeyboardModifier EmulatedDevices::GetKeyboardModifier() const {
412 return device_status.keyboard_moddifier_state;
413}
414
415MouseButton EmulatedDevices::GetMouseButtons() const {
416 return device_status.mouse_button_state;
417}
418
419MousePosition EmulatedDevices::GetMousePosition() const {
420 return device_status.mouse_position_state;
421}
422
423AnalogStickState EmulatedDevices::GetMouseWheel() const {
424 return device_status.mouse_wheel_state;
425}
426
427void EmulatedDevices::TriggerOnChange(DeviceTriggerType type) {
428 for (const auto& poller_pair : callback_list) {
429 const InterfaceUpdateCallback& poller = poller_pair.second;
430 if (poller.on_change) {
431 poller.on_change(type);
432 }
433 }
434}
435
436int EmulatedDevices::SetCallback(InterfaceUpdateCallback update_callback) {
437 std::lock_guard lock{mutex};
438 callback_list.insert_or_assign(last_callback_key, update_callback);
439 return last_callback_key++;
440}
441
442void EmulatedDevices::DeleteCallback(int key) {
443 std::lock_guard lock{mutex};
444 const auto& iterator = callback_list.find(key);
445 if (iterator == callback_list.end()) {
446 LOG_ERROR(Input, "Tried to delete non-existent callback {}", key);
447 return;
448 }
449 callback_list.erase(iterator);
450}
451} // namespace Core::HID